зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
3860f49d11
|
@ -38,6 +38,7 @@ parser/**
|
|||
probes/**
|
||||
python/**
|
||||
rdf/**
|
||||
servo/**
|
||||
startupcache/**
|
||||
testing/**
|
||||
tools/update-packaging/**
|
||||
|
|
|
@ -192,14 +192,6 @@ const TELEMETRY_DDSTAT_SOLVED = 4;
|
|||
|
||||
let gDecoderDoctorHandler = {
|
||||
getLabelForNotificationBox(type) {
|
||||
if (type == "adobe-cdm-not-found" &&
|
||||
AppConstants.platform == "win") {
|
||||
return gNavigatorBundle.getString("decoder.noCodecs.message");
|
||||
}
|
||||
if (type == "adobe-cdm-not-activated" &&
|
||||
AppConstants.platform == "win") {
|
||||
return gNavigatorBundle.getString("decoder.noCodecs.message");
|
||||
}
|
||||
if (type == "platform-decoder-not-found") {
|
||||
if (AppConstants.platform == "win") {
|
||||
return gNavigatorBundle.getString("decoder.noHWAcceleration.message");
|
||||
|
|
|
@ -46,26 +46,6 @@ function* test_decoder_doctor_notification(type, notificationMessage, options) {
|
|||
});
|
||||
}
|
||||
|
||||
add_task(function* test_adobe_cdm_not_found() {
|
||||
// This is only sent on Windows.
|
||||
if (AppConstants.platform != "win") {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = gNavigatorBundle.getString("decoder.noCodecs.message");
|
||||
yield test_decoder_doctor_notification("adobe-cdm-not-found", message);
|
||||
});
|
||||
|
||||
add_task(function* test_adobe_cdm_not_activated() {
|
||||
// This is only sent on Windows.
|
||||
if (AppConstants.platform != "win") {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = gNavigatorBundle.getString("decoder.noCodecs.message");
|
||||
yield test_decoder_doctor_notification("adobe-cdm-not-activated", message);
|
||||
});
|
||||
|
||||
add_task(function* test_platform_decoder_not_found() {
|
||||
let message;
|
||||
let isLinux = AppConstants.platform == "linux";
|
||||
|
|
|
@ -3,6 +3,21 @@ const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm
|
|||
const URL_BASE = "https://example.com/browser/browser/base/content/test/general";
|
||||
const ID = "update@tests.mozilla.org";
|
||||
|
||||
function promiseInstallAddon(url) {
|
||||
return AddonManager.getInstallForURL(url, null, "application/x-xpinstall")
|
||||
.then(install => {
|
||||
ok(install, "Created install");
|
||||
return new Promise(resolve => {
|
||||
install.addListener({
|
||||
onInstallEnded(_install, addon) {
|
||||
resolve(addon);
|
||||
},
|
||||
});
|
||||
install.install();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function promiseViewLoaded(tab, viewid) {
|
||||
let win = tab.linkedBrowser.contentWindow;
|
||||
if (win.gViewController && !win.gViewController.isLoading &&
|
||||
|
@ -44,42 +59,22 @@ function getBadgeStatus() {
|
|||
return menuButton.getAttribute("badge-status");
|
||||
}
|
||||
|
||||
function promiseUpdateDownloaded(addon) {
|
||||
function promiseInstallEvent(addon, event) {
|
||||
return new Promise(resolve => {
|
||||
let listener = {
|
||||
onDownloadEnded(install) {
|
||||
if (install.addon.id == addon.id) {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
let listener = {};
|
||||
listener[event] = (install, ...args) => {
|
||||
if (install.addon.id == addon.id) {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
resolve(...args);
|
||||
}
|
||||
};
|
||||
AddonManager.addInstallListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseUpgrade(addon) {
|
||||
return new Promise(resolve => {
|
||||
let listener = {
|
||||
onInstallEnded(install, newAddon) {
|
||||
if (newAddon.id == addon.id) {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
resolve(newAddon);
|
||||
}
|
||||
},
|
||||
};
|
||||
AddonManager.addInstallListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
// Turn on background updates
|
||||
["extensions.update.enabled", true],
|
||||
|
||||
// Point updates to the local mochitest server
|
||||
["extensions.update.background.url", `${URL_BASE}/browser_webext_update.json`],
|
||||
|
||||
// Set some prefs that apply to all the tests in this file
|
||||
add_task(function setup() {
|
||||
return SpecialPowers.pushPrefEnv({set: [
|
||||
// We don't have pre-pinned certificates for the local mochitest server
|
||||
["extensions.install.requireBuiltInCerts", false],
|
||||
["extensions.update.requireBuiltInCerts", false],
|
||||
|
@ -87,27 +82,27 @@ add_task(function* () {
|
|||
// XXX remove this when prompts are enabled by default
|
||||
["extensions.webextPermissionPrompts", true],
|
||||
]});
|
||||
});
|
||||
|
||||
add_task(function* test_background_update() {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
// Turn on background updates
|
||||
["extensions.update.enabled", true],
|
||||
|
||||
// Point updates to the local mochitest server
|
||||
["extensions.update.background.url", `${URL_BASE}/browser_webext_update.json`],
|
||||
]});
|
||||
|
||||
// Install version 1.0 of the test extension
|
||||
let url1 = `${URL_BASE}/browser_webext_update1.xpi`;
|
||||
let install = yield AddonManager.getInstallForURL(url1, null, "application/x-xpinstall");
|
||||
ok(install, "Created install");
|
||||
|
||||
let addon = yield new Promise(resolve => {
|
||||
install.addListener({
|
||||
onInstallEnded(_install, _addon) {
|
||||
resolve(_addon);
|
||||
},
|
||||
});
|
||||
install.install();
|
||||
});
|
||||
let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update1.xpi`);
|
||||
|
||||
ok(addon, "Addon was installed");
|
||||
is(getBadgeStatus(), "", "Should not start out with an addon alert badge");
|
||||
|
||||
// Trigger an update check and wait for the update for this addon
|
||||
// to be downloaded.
|
||||
let updatePromise = promiseUpdateDownloaded(addon);
|
||||
let updatePromise = promiseInstallEvent(addon, "onDownloadEnded");
|
||||
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
yield updatePromise;
|
||||
|
||||
|
@ -152,7 +147,7 @@ add_task(function* () {
|
|||
yield PanelUI.hide();
|
||||
|
||||
// Re-check for an update
|
||||
updatePromise = promiseUpdateDownloaded(addon);
|
||||
updatePromise = promiseInstallEvent(addon, "onDownloadEnded");
|
||||
yield AddonManagerPrivate.backgroundUpdateCheck();
|
||||
yield updatePromise;
|
||||
|
||||
|
@ -179,7 +174,7 @@ add_task(function* () {
|
|||
is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
|
||||
|
||||
// Wait for the permission prompt and accept it this time
|
||||
updatePromise = promiseUpgrade(addon);
|
||||
updatePromise = promiseInstallEvent(addon, "onInstallEnded");
|
||||
panel = yield popupPromise;
|
||||
panel.button.click();
|
||||
|
||||
|
@ -189,4 +184,119 @@ add_task(function* () {
|
|||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
is(getBadgeStatus(), "", "Addon alert badge should be gone");
|
||||
|
||||
addon.uninstall();
|
||||
yield SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
// Helper function to test a specific scenario for interactive updates.
|
||||
// `checkFn` is a callable that triggers a check for updates.
|
||||
// `autoUpdate` specifies whether the test should be run with
|
||||
// updates applied automatically or not.
|
||||
function* interactiveUpdateTest(autoUpdate, checkFn) {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
["extensions.update.autoUpdateDefault", autoUpdate],
|
||||
|
||||
// Point updates to the local mochitest server
|
||||
["extensions.update.url", `${URL_BASE}/browser_webext_update.json`],
|
||||
]});
|
||||
|
||||
// Trigger an update check, manually applying the update if we're testing
|
||||
// without auto-update.
|
||||
function* triggerUpdate(win, addon) {
|
||||
let manualUpdatePromise;
|
||||
if (!autoUpdate) {
|
||||
manualUpdatePromise = new Promise(resolve => {
|
||||
let listener = {
|
||||
onNewInstall() {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
resolve();
|
||||
},
|
||||
};
|
||||
AddonManager.addInstallListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
checkFn(win, addon);
|
||||
|
||||
if (manualUpdatePromise) {
|
||||
yield manualUpdatePromise;
|
||||
|
||||
let item = win.document.getElementById("addon-list")
|
||||
.children.find(_item => _item.value == ID);
|
||||
EventUtils.synthesizeMouseAtCenter(item._updateBtn, {}, win);
|
||||
}
|
||||
}
|
||||
|
||||
// Install version 1.0 of the test extension
|
||||
let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update1.xpi`);
|
||||
ok(addon, "Addon was installed");
|
||||
is(addon.version, "1.0", "Version 1 of the addon is installed");
|
||||
|
||||
// Open add-ons manager and navigate to extensions list
|
||||
let loadPromise = new Promise(resolve => {
|
||||
let listener = (subject, topic) => {
|
||||
if (subject.location.href == "about:addons") {
|
||||
Services.obs.removeObserver(listener, topic);
|
||||
resolve(subject);
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(listener, "EM-loaded", false);
|
||||
});
|
||||
let tab = gBrowser.addTab("about:addons");
|
||||
gBrowser.selectedTab = tab;
|
||||
let win = yield loadPromise;
|
||||
|
||||
const VIEW = "addons://list/extension";
|
||||
let viewPromise = promiseViewLoaded(tab, VIEW);
|
||||
win.loadView(VIEW);
|
||||
yield viewPromise;
|
||||
|
||||
// Trigger an update check
|
||||
let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
yield triggerUpdate(win, addon);
|
||||
let panel = yield popupPromise;
|
||||
|
||||
// Click the cancel button, wait to see the cancel event
|
||||
let cancelPromise = promiseInstallEvent(addon, "onInstallCancelled");
|
||||
panel.secondaryButton.click();
|
||||
yield cancelPromise;
|
||||
|
||||
addon = yield AddonManager.getAddonByID(ID);
|
||||
is(addon.version, "1.0", "Should still be running the old version");
|
||||
|
||||
// Trigger a new update check
|
||||
popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
yield triggerUpdate(win, addon);
|
||||
|
||||
// This time, accept the upgrade
|
||||
let updatePromise = promiseInstallEvent(addon, "onInstallEnded");
|
||||
panel = yield popupPromise;
|
||||
panel.button.click();
|
||||
|
||||
addon = yield updatePromise;
|
||||
is(addon.version, "2.0", "Should have upgraded");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
addon.uninstall();
|
||||
yield SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
// Invoke the "Check for Updates" menu item
|
||||
function checkAll(win) {
|
||||
win.gViewController.doCommand("cmd_findAllUpdates");
|
||||
}
|
||||
|
||||
// Test "Check for Updates" with both auto-update settings
|
||||
add_task(() => interactiveUpdateTest(true, checkAll));
|
||||
add_task(() => interactiveUpdateTest(false, checkAll));
|
||||
|
||||
|
||||
// Invoke an invidual extension's "Find Updates" menu item
|
||||
function checkOne(win, addon) {
|
||||
win.gViewController.doCommand("cmd_findItemUpdates", addon);
|
||||
}
|
||||
|
||||
// Test "Find Updates" with both auto-update settings
|
||||
add_task(() => interactiveUpdateTest(true, checkOne));
|
||||
add_task(() => interactiveUpdateTest(false, checkOne));
|
||||
|
|
|
@ -128,8 +128,11 @@ this.ExtensionsUI = {
|
|||
}
|
||||
|
||||
let reply = answer => {
|
||||
Services.obs.notifyObservers(subject, "webextension-permission-response",
|
||||
JSON.stringify(answer));
|
||||
if (answer) {
|
||||
info.resolve();
|
||||
} else {
|
||||
info.reject();
|
||||
}
|
||||
};
|
||||
|
||||
let perms = info.addon.userPermissions;
|
||||
|
|
|
@ -675,6 +675,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
|
||||
#PanelUI-update-status > .toolbarbutton-text,
|
||||
#PanelUI-fxa-label > .toolbarbutton-text,
|
||||
#PanelUI-footer-addons > toolbarbutton > .toolbarbutton-text,
|
||||
#PanelUI-customize > .toolbarbutton-text {
|
||||
margin: 0;
|
||||
padding: 0 6px;
|
||||
|
|
|
@ -43,7 +43,7 @@ def compiler_class(compiler):
|
|||
def checking_fn(fn):
|
||||
return fn
|
||||
|
||||
@depends_when(self, dependable(flags), extra_toolchain_flags, when=when)
|
||||
@depends(self, dependable(flags), extra_toolchain_flags, when=when)
|
||||
@checking_fn
|
||||
def func(compiler, flags, extra_flags):
|
||||
flags = flags or []
|
||||
|
|
|
@ -74,11 +74,11 @@ js_option('--with-linux-headers',
|
|||
|
||||
passed_linux_header_flags = depends_if('--with-linux-headers')(lambda v: ['-I%s' % v[0]])
|
||||
|
||||
@depends_when(try_compile(includes=['asm/unistd.h'],
|
||||
body='return sizeof(__NR_perf_event_open);',
|
||||
flags=passed_linux_header_flags,
|
||||
check_msg='for perf_event_open system call'),
|
||||
when=have_perf_event_h)
|
||||
@depends(try_compile(includes=['asm/unistd.h'],
|
||||
body='return sizeof(__NR_perf_event_open);',
|
||||
flags=passed_linux_header_flags,
|
||||
check_msg='for perf_event_open system call'),
|
||||
when=have_perf_event_h)
|
||||
def have_perf_event_open(have_perf_event_open):
|
||||
if have_perf_event_open:
|
||||
return True
|
||||
|
|
|
@ -40,8 +40,8 @@ def pkg_check_modules(var, package_desc, when=always,
|
|||
def when_and_compile_environment(when, compile_environment):
|
||||
return when and compile_environment
|
||||
|
||||
@depends_when(pkg_config, pkg_config_version,
|
||||
when=when_and_compile_environment)
|
||||
@depends(pkg_config, pkg_config_version,
|
||||
when=when_and_compile_environment)
|
||||
def check_pkg_config(pkg_config, version):
|
||||
min_version = '0.9.0'
|
||||
if pkg_config is None:
|
||||
|
@ -52,7 +52,7 @@ def pkg_check_modules(var, package_desc, when=always,
|
|||
die("*** Your version of pkg-config is too old. You need version %s or newer.",
|
||||
min_version)
|
||||
|
||||
@depends_when(pkg_config, package_desc, when=when_and_compile_environment)
|
||||
@depends(pkg_config, package_desc, when=when_and_compile_environment)
|
||||
@imports('subprocess')
|
||||
@imports('sys')
|
||||
@imports(_from='mozbuild.configure.util', _import='LineIO')
|
||||
|
@ -74,20 +74,20 @@ def pkg_check_modules(var, package_desc, when=always,
|
|||
if not allow_missing:
|
||||
sys.exit(1)
|
||||
|
||||
@depends_when(pkg_config, package_desc, when=package)
|
||||
@depends(pkg_config, package_desc, when=package)
|
||||
@checking('%s_CFLAGS' % var, callback=lambda t: ' '.join(t))
|
||||
def pkg_cflags(pkg_config, package_desc):
|
||||
flags = check_cmd_output(pkg_config, '--cflags', package_desc)
|
||||
return tuple(flags.split())
|
||||
|
||||
@depends_when(pkg_config, package_desc, when=package)
|
||||
@depends(pkg_config, package_desc, when=package)
|
||||
@checking('%s_LIBS' % var, callback=lambda t: ' '.join(t))
|
||||
def pkg_libs(pkg_config, package_desc):
|
||||
libs = check_cmd_output(pkg_config, '--libs', package_desc)
|
||||
# Remove evil flags like -Wl,--export-dynamic
|
||||
return tuple(libs.replace('-Wl,--export-dynamic', '').split())
|
||||
|
||||
@depends_when(pkg_cflags, pkg_libs, when=package)
|
||||
@depends(pkg_cflags, pkg_libs, when=package)
|
||||
def pkg_info(cflags, libs):
|
||||
return namespace(cflags=cflags, libs=libs)
|
||||
|
||||
|
|
|
@ -399,26 +399,6 @@ def depends_if(*args):
|
|||
return wrapper
|
||||
return decorator
|
||||
|
||||
# Like @depends_if, but a distinguished value passed as a keyword argument
|
||||
# "when" is truth tested instead of every argument. This value is not passed
|
||||
# to the function if it is called.
|
||||
@template
|
||||
def depends_when(*args, **kwargs):
|
||||
if not len(kwargs) == 1 and kwargs.get('when'):
|
||||
die('depends_when requires a single keyword argument, "when"')
|
||||
when = kwargs['when']
|
||||
if not when:
|
||||
return depends(*args)
|
||||
|
||||
def decorator(fn):
|
||||
@depends(when, *args)
|
||||
def wrapper(val, *args):
|
||||
if val:
|
||||
return fn(*args)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
# Hacks related to old-configure
|
||||
# ==============================
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/intl/LocaleService.h"
|
||||
|
||||
#include "nsICommandLine.h"
|
||||
#include "nsILocaleService.h"
|
||||
|
@ -386,6 +387,7 @@ nsresult nsChromeRegistryChrome::UpdateSelectedLocale()
|
|||
NS_ASSERTION(obsSvc, "Couldn't get observer service.");
|
||||
obsSvc->NotifyObservers((nsIChromeRegistry*) this,
|
||||
"selected-locale-has-changed", nullptr);
|
||||
mozilla::intl::LocaleService::GetInstance()->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ function configureStore() {
|
|||
error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR),
|
||||
warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN),
|
||||
info: Services.prefs.getBoolPref(PREFS.FILTER.INFO),
|
||||
debug: Services.prefs.getBoolPref(PREFS.FILTER.DEBUG),
|
||||
log: Services.prefs.getBoolPref(PREFS.FILTER.LOG),
|
||||
css: Services.prefs.getBoolPref(PREFS.FILTER.CSS),
|
||||
net: Services.prefs.getBoolPref(PREFS.FILTER.NET),
|
||||
|
|
|
@ -30,6 +30,7 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
|
|||
[browser_webconsole_context_menu_open_url.js]
|
||||
[browser_webconsole_context_menu_store_as_global.js]
|
||||
[browser_webconsole_filters.js]
|
||||
[browser_webconsole_filters_persist.js]
|
||||
[browser_webconsole_init.js]
|
||||
[browser_webconsole_input_focus.js]
|
||||
[browser_webconsole_keyboard_accessibility.js]
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests all filters persist.
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html";
|
||||
|
||||
add_task(function* () {
|
||||
let hud = yield openNewTabAndConsole(TEST_URI);
|
||||
|
||||
let filterButtons = yield getFilterButtons(hud);
|
||||
info("Disable all filters");
|
||||
filterButtons.forEach(filterButton => {
|
||||
if (filterIsEnabled(filterButton)) {
|
||||
filterButton.click();
|
||||
}
|
||||
});
|
||||
|
||||
info("Close and re-open the console");
|
||||
yield closeTabAndToolbox();
|
||||
hud = yield openNewTabAndConsole(TEST_URI);
|
||||
|
||||
info("Check that all filters are disabled, and enable them");
|
||||
filterButtons = yield getFilterButtons(hud);
|
||||
filterButtons.forEach(filterButton => {
|
||||
ok(!filterIsEnabled(filterButton), "filter is disabled");
|
||||
filterButton.click();
|
||||
});
|
||||
|
||||
info("Close and re-open the console");
|
||||
yield closeTabAndToolbox();
|
||||
hud = yield openNewTabAndConsole(TEST_URI);
|
||||
|
||||
info("Check that all filters are enabled");
|
||||
filterButtons = yield getFilterButtons(hud);
|
||||
filterButtons.forEach(filterButton => {
|
||||
ok(filterIsEnabled(filterButton), "filter is enabled");
|
||||
});
|
||||
|
||||
// Check that the ui settings were persisted.
|
||||
yield closeTabAndToolbox();
|
||||
});
|
||||
|
||||
function* getFilterButtons(hud) {
|
||||
const outputNode = hud.ui.experimentalOutputNode;
|
||||
|
||||
info("Wait for console toolbar to appear");
|
||||
const toolbar = yield waitFor(() => {
|
||||
return outputNode.querySelector(".webconsole-filterbar-primary");
|
||||
});
|
||||
|
||||
// Show the filter bar if it is hidden
|
||||
if (!outputNode.querySelector(".webconsole-filterbar-secondary")) {
|
||||
toolbar.querySelector(".devtools-filter-icon").click();
|
||||
}
|
||||
|
||||
info("Wait for console filterbar to appear");
|
||||
const filterBar = yield waitFor(() => {
|
||||
return outputNode.querySelector(".webconsole-filterbar-secondary");
|
||||
});
|
||||
ok(filterBar, "Filter bar is shown when filter icon is clicked.");
|
||||
|
||||
return filterBar.querySelectorAll(".menu-filter-button");
|
||||
}
|
||||
|
||||
function filterIsEnabled(button) {
|
||||
return button.classList.contains("checked");
|
||||
}
|
|
@ -41,6 +41,9 @@ AudioChannelAgent::AudioChannelAgent()
|
|||
, mInnerWindowID(0)
|
||||
, mIsRegToService(false)
|
||||
{
|
||||
// Init service in the begining, it can help us to know whether there is any
|
||||
// created media component via AudioChannelService::IsServiceStarted().
|
||||
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
|
||||
}
|
||||
|
||||
AudioChannelAgent::~AudioChannelAgent()
|
||||
|
@ -181,6 +184,9 @@ AudioChannelAgent::InitInternal(nsPIDOMWindowInner* aWindow,
|
|||
mCallback = aCallback;
|
||||
}
|
||||
|
||||
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
|
||||
service->NotifyCreatedNewAgent(this);
|
||||
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelAgent, InitInternal, this = %p, type = %d, "
|
||||
"owner = %p, hasCallback = %d\n", this, mAudioChannelType,
|
||||
|
|
|
@ -201,6 +201,13 @@ AudioChannelService::CreateServiceIfNeeded()
|
|||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
AudioChannelService::IsServiceStarted()
|
||||
{
|
||||
// The service would start when the first AudioChannelAgent is created.
|
||||
return !!gAudioChannelService;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<AudioChannelService>
|
||||
AudioChannelService::GetOrCreate()
|
||||
{
|
||||
|
@ -286,6 +293,19 @@ AudioChannelService::~AudioChannelService()
|
|||
{
|
||||
}
|
||||
|
||||
void
|
||||
AudioChannelService::NotifyCreatedNewAgent(AudioChannelAgent* aAgent)
|
||||
{
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> window = aAgent->Window();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
window->NotifyCreatedNewMediaComponent();
|
||||
}
|
||||
|
||||
void
|
||||
AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
|
||||
AudibleState aAudible)
|
||||
|
@ -1392,7 +1412,18 @@ AudioChannelService::AudioChannelWindow::MaybeNotifyMediaBlocked(AudioChannelAge
|
|||
}
|
||||
|
||||
MOZ_ASSERT(window->IsOuterWindow());
|
||||
if (window->GetMediaSuspend() != nsISuspendedTypes::SUSPENDED_BLOCK) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> inner = window->GetCurrentInnerWindow();
|
||||
if (!inner) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = inner->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window->GetMediaSuspend() != nsISuspendedTypes::SUSPENDED_BLOCK ||
|
||||
!doc->Hidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,8 @@ public:
|
|||
|
||||
static bool IsEnableAudioCompeting();
|
||||
|
||||
static bool IsServiceStarted();
|
||||
|
||||
/**
|
||||
* Any audio channel agent that starts playing should register itself to
|
||||
* this service, sharing the AudioChannel.
|
||||
|
@ -197,6 +199,8 @@ public:
|
|||
void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel,
|
||||
bool aContentOrNormalChannel, bool aAnyChannel);
|
||||
|
||||
void NotifyCreatedNewAgent(AudioChannelAgent* aAgent);
|
||||
|
||||
private:
|
||||
AudioChannelService();
|
||||
~AudioChannelService();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* Base class for all our document implementations.
|
||||
*/
|
||||
|
||||
#include "AudioChannelService.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsIDocumentInlines.h"
|
||||
#include "mozilla/AnimationComparator.h"
|
||||
|
@ -12084,9 +12085,7 @@ nsDocument::MaybeActiveMediaComponents()
|
|||
}
|
||||
|
||||
mEverInForeground = true;
|
||||
if (GetWindow()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) {
|
||||
GetWindow()->SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
|
||||
}
|
||||
GetWindow()->MaybeActiveMediaComponents();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -4168,6 +4168,45 @@ nsPIDOMWindowInner::IsRunningTimeout()
|
|||
return TimeoutManager().IsRunningTimeout();
|
||||
}
|
||||
|
||||
void
|
||||
nsPIDOMWindowOuter::NotifyCreatedNewMediaComponent()
|
||||
{
|
||||
if (mMediaSuspend != nsISuspendedTypes::SUSPENDED_BLOCK) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the document is already on the foreground but the suspend state is still
|
||||
// suspend-block, that means the media component was created after calling
|
||||
// MaybeActiveMediaComponents, so the window's suspend state doesn't be
|
||||
// changed yet. Therefore, we need to call it again, because the state is only
|
||||
// changed after there exists alive media within the window.
|
||||
MaybeActiveMediaComponents();
|
||||
}
|
||||
|
||||
void
|
||||
nsPIDOMWindowOuter::MaybeActiveMediaComponents()
|
||||
{
|
||||
if (IsInnerWindow()) {
|
||||
return mOuterWindow->MaybeActiveMediaComponents();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> inner = GetCurrentInnerWindow();
|
||||
if (!inner) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = inner->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!doc->Hidden() &&
|
||||
mMediaSuspend == nsISuspendedTypes::SUSPENDED_BLOCK &&
|
||||
AudioChannelService::IsServiceStarted()) {
|
||||
SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
|
||||
}
|
||||
}
|
||||
|
||||
SuspendTypes
|
||||
nsPIDOMWindowOuter::GetMediaSuspend() const
|
||||
{
|
||||
|
|
|
@ -963,6 +963,9 @@ public:
|
|||
float GetAudioVolume() const;
|
||||
nsresult SetAudioVolume(float aVolume);
|
||||
|
||||
void NotifyCreatedNewMediaComponent();
|
||||
void MaybeActiveMediaComponents();
|
||||
|
||||
void SetServiceWorkersTestingEnabled(bool aEnabled);
|
||||
bool GetServiceWorkersTestingEnabled();
|
||||
|
||||
|
|
|
@ -82,7 +82,6 @@ support-files =
|
|||
support-files = pointerevent_setpointercapture_disconnected-manual.html
|
||||
[test_pointerevent_setpointercapture_inactive_button_mouse-manual.html]
|
||||
support-files = pointerevent_setpointercapture_inactive_button_mouse-manual.html
|
||||
disabled = should be investigated
|
||||
[test_pointerevent_setpointercapture_invalid_pointerid-manual.html]
|
||||
support-files = pointerevent_setpointercapture_invalid_pointerid-manual.html
|
||||
[test_pointerevent_setpointercapture_override_pending_capture_element-manual.html]
|
||||
|
|
|
@ -81,56 +81,60 @@ var MouseEventHelper = (function() {
|
|||
};
|
||||
}) ();
|
||||
|
||||
function createMouseEvent(aEventType, aParams) {
|
||||
var eventObj = {type: aEventType};
|
||||
|
||||
// Default to mouse.
|
||||
eventObj.inputSource =
|
||||
(aParams && "inputSource" in aParams) ? aParams.inputSource :
|
||||
MouseEvent.MOZ_SOURCE_MOUSE;
|
||||
// Compute pointerId
|
||||
eventObj.id =
|
||||
(eventObj.inputSource === MouseEvent.MOZ_SOURCE_MOUSE) ? MouseEventHelper.MOUSE_ID :
|
||||
MouseEventHelper.PEN_ID;
|
||||
// Check or generate a |button| value.
|
||||
var isButtonEvent = aEventType === "mouseup" || aEventType === "mousedown";
|
||||
|
||||
// Set |button| to the default value first.
|
||||
eventObj.button = isButtonEvent ? MouseEventHelper.BUTTON_LEFT
|
||||
: MouseEventHelper.BUTTON_NONE;
|
||||
|
||||
// |button| is passed, use and check it.
|
||||
if (aParams && "button" in aParams) {
|
||||
var hasButtonValue = (aParams.button !== MouseEventHelper.BUTTON_NONE);
|
||||
ok(!isButtonEvent || hasButtonValue,
|
||||
"Inappropriate |button| value caught.");
|
||||
eventObj.button = aParams.button;
|
||||
}
|
||||
|
||||
// Generate a |buttons| value and update buttons state
|
||||
var buttonsMask = MouseEventHelper.computeButtonsMaskFromButton(eventObj.button);
|
||||
switch(aEventType) {
|
||||
case "mousedown":
|
||||
MouseEventHelper.BUTTONS_STATE |= buttonsMask; // Set button flag.
|
||||
break;
|
||||
case "mouseup":
|
||||
MouseEventHelper.BUTTONS_STATE &= ~buttonsMask; // Clear button flag.
|
||||
break;
|
||||
}
|
||||
eventObj.buttons = MouseEventHelper.BUTTONS_STATE;
|
||||
|
||||
// Replace the button value for mousemove events.
|
||||
// Since in widget level design, even when no button is pressed at all, the
|
||||
// value of WidgetMouseEvent.button is still 0, which is the same value as
|
||||
// the one for mouse left button.
|
||||
if (aEventType === "mousemove") {
|
||||
eventObj.button = MouseEventHelper.BUTTON_LEFT;
|
||||
}
|
||||
return eventObj;
|
||||
}
|
||||
|
||||
// Helper function to send MouseEvent with different parameters
|
||||
function sendMouseEvent(int_win, elemId, mouseEventType, params) {
|
||||
var elem = int_win.document.getElementById(elemId);
|
||||
if(!!elem) {
|
||||
if (elem) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
var eventObj = {type: mouseEventType};
|
||||
|
||||
// Default to mouse.
|
||||
eventObj.inputSource =
|
||||
(params && "inputSource" in params) ? params.inputSource :
|
||||
MouseEvent.MOZ_SOURCE_MOUSE;
|
||||
// Compute pointerId
|
||||
eventObj.id =
|
||||
(eventObj.inputSource === MouseEvent.MOZ_SOURCE_MOUSE) ? MouseEventHelper.MOUSE_ID :
|
||||
MouseEventHelper.PEN_ID;
|
||||
// Check or generate a |button| value.
|
||||
var isButtonEvent = mouseEventType === "mouseup" ||
|
||||
mouseEventType === "mousedown";
|
||||
|
||||
// Set |button| to the default value first.
|
||||
eventObj.button = isButtonEvent ? MouseEventHelper.BUTTON_LEFT
|
||||
: MouseEventHelper.BUTTON_NONE;
|
||||
|
||||
// |button| is passed, use and check it.
|
||||
if (params && "button" in params) {
|
||||
var hasButtonValue = (params.button !== MouseEventHelper.BUTTON_NONE);
|
||||
ok(!isButtonEvent || hasButtonValue,
|
||||
"Inappropriate |button| value caught.");
|
||||
eventObj.button = params.button;
|
||||
}
|
||||
|
||||
// Generate a |buttons| value and update buttons state
|
||||
var buttonsMask = MouseEventHelper.computeButtonsMaskFromButton(eventObj.button);
|
||||
switch(mouseEventType) {
|
||||
case "mousedown":
|
||||
MouseEventHelper.BUTTONS_STATE |= buttonsMask; // Set button flag.
|
||||
break;
|
||||
case "mouseup":
|
||||
MouseEventHelper.BUTTONS_STATE &= ~buttonsMask; // Clear button flag.
|
||||
break;
|
||||
}
|
||||
eventObj.buttons = MouseEventHelper.BUTTONS_STATE;
|
||||
|
||||
// Replace the button value for mousemove events.
|
||||
// Since in widget level design, even when no button is pressed at all, the
|
||||
// value of WidgetMouseEvent.button is still 0, which is the same value as
|
||||
// the one for mouse left button.
|
||||
if (mouseEventType === "mousemove") {
|
||||
eventObj.button = MouseEventHelper.BUTTON_LEFT;
|
||||
}
|
||||
var eventObj = createMouseEvent(mouseEventType, params);
|
||||
|
||||
// Default to the center of the target element but we can still send to a
|
||||
// position outside of the target element.
|
||||
|
@ -145,6 +149,13 @@ function sendMouseEvent(int_win, elemId, mouseEventType, params) {
|
|||
}
|
||||
}
|
||||
|
||||
// Helper function to send MouseEvent with position
|
||||
function sendMouseEventAtPoint(aWindow, aLeft, aTop, aMouseEventType, aParams) {
|
||||
var eventObj = createMouseEvent(aMouseEventType, aParams);
|
||||
console.log(eventObj);
|
||||
synthesizeMouseAtPoint(aLeft, aTop, eventObj, aWindow);
|
||||
}
|
||||
|
||||
// Touch Event Helper Object
|
||||
var TouchEventHelper = {
|
||||
// State
|
||||
|
|
|
@ -17,9 +17,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
|||
runTestInNewWindow("pointerevent_setpointercapture_inactive_button_mouse-manual.html");
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
sendMouseEvent(int_win, "target0", "mousemove");
|
||||
sendMouseEvent(int_win, "target1", "mousemove");
|
||||
let target0 = int_win.document.getElementById("target0");
|
||||
let rect = target0.getBoundingClientRect();
|
||||
sendMouseEventAtPoint(int_win, rect.left + 1, rect.top + 1, "mousemove");
|
||||
sendMouseEventAtPoint(int_win, rect.left - 1, rect.top - 1, "mousemove");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
|
|
@ -429,18 +429,14 @@ GetFilesHelperBase::AddExploredDirectory(nsIFile* aDir)
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString path;
|
||||
|
||||
nsAutoString path;
|
||||
if (!isLink) {
|
||||
nsAutoString path16;
|
||||
rv = aDir->GetPath(path16);
|
||||
rv = aDir->GetPath(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
path = NS_ConvertUTF16toUTF8(path16);
|
||||
} else {
|
||||
rv = aDir->GetNativeTarget(path);
|
||||
rv = aDir->GetTarget(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -463,8 +459,8 @@ GetFilesHelperBase::ShouldFollowSymLink(nsIFile* aDir)
|
|||
MOZ_ASSERT(isLink && isDir, "Why are we here?");
|
||||
#endif
|
||||
|
||||
nsAutoCString targetPath;
|
||||
if (NS_WARN_IF(NS_FAILED(aDir->GetNativeTarget(targetPath)))) {
|
||||
nsAutoString targetPath;
|
||||
if (NS_WARN_IF(NS_FAILED(aDir->GetTarget(targetPath)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ protected:
|
|||
|
||||
// We populate this array in the I/O thread with the BlobImpl.
|
||||
FallibleTArray<RefPtr<BlobImpl>> mTargetBlobImplArray;
|
||||
nsTHashtable<nsCStringHashKey> mExploredDirectories;
|
||||
nsTHashtable<nsStringHashKey> mExploredDirectories;
|
||||
};
|
||||
|
||||
// Retrieving the list of files can be very time/IO consuming. We use this
|
||||
|
|
|
@ -1471,6 +1471,33 @@ HTMLMediaElement::GetMozDebugReaderData(nsAString& aString)
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::MozRequestDebugInfo(ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> promise = CreateDOMPromise(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoString result;
|
||||
GetMozDebugReaderData(result);
|
||||
|
||||
if (mDecoder) {
|
||||
mDecoder->RequestDebugInfo()->Then(
|
||||
AbstractThread::MainThread(), __func__,
|
||||
[promise, result] (const nsACString& aString) {
|
||||
promise->MaybeResolve(result + NS_ConvertUTF8toUTF16(aString));
|
||||
},
|
||||
[promise, result] () {
|
||||
promise->MaybeResolve(result);
|
||||
});
|
||||
} else {
|
||||
promise->MaybeResolve(result);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::MozDumpDebugInfo()
|
||||
{
|
||||
|
|
|
@ -604,6 +604,10 @@ public:
|
|||
// data. Used for debugging purposes.
|
||||
void GetMozDebugReaderData(nsAString& aString);
|
||||
|
||||
// Returns a promise which will be resolved after collecting debugging
|
||||
// data from decoder/reader/MDSM. Used for debugging purposes.
|
||||
already_AddRefed<Promise> MozRequestDebugInfo(ErrorResult& aRv);
|
||||
|
||||
void MozDumpDebugInfo();
|
||||
|
||||
void SetVisible(bool aVisible);
|
||||
|
|
|
@ -791,7 +791,9 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
|||
Preferences::GetUint("media.audio-max-decode-error", 3))
|
||||
, mVideo(this, MediaData::VIDEO_DATA,
|
||||
Preferences::GetUint("media.video-max-decode-error", 2))
|
||||
, mDemuxer(nullptr)
|
||||
, mDemuxer(new DemuxerProxy(aDemuxer, aDecoder
|
||||
? aDecoder->AbstractMainThread()
|
||||
: AbstractThread::MainThread()))
|
||||
, mDemuxerInitDone(false)
|
||||
, mLastReportedNumDecodedFrames(0)
|
||||
, mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
|
||||
|
@ -804,15 +806,10 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
|||
MOZ_ASSERT(aDemuxer);
|
||||
MOZ_COUNT_CTOR(MediaFormatReader);
|
||||
|
||||
if (aDecoder) {
|
||||
mDemuxer = MakeUnique<DemuxerProxy>(aDemuxer,
|
||||
aDecoder->AbstractMainThread());
|
||||
|
||||
if (aDecoder->CompositorUpdatedEvent()) {
|
||||
mCompositorUpdatedListener =
|
||||
aDecoder->CompositorUpdatedEvent()->Connect(
|
||||
mTaskQueue, this, &MediaFormatReader::NotifyCompositorUpdated);
|
||||
}
|
||||
if (aDecoder && aDecoder->CompositorUpdatedEvent()) {
|
||||
mCompositorUpdatedListener =
|
||||
aDecoder->CompositorUpdatedEvent()->Connect(
|
||||
mTaskQueue, this, &MediaFormatReader::NotifyCompositorUpdated);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -917,10 +914,11 @@ MediaFormatReader::InitInternal()
|
|||
mVideo.mTaskQueue =
|
||||
new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
|
||||
|
||||
// Note: GMPCrashHelper must be created on main thread, as it may use
|
||||
// weak references, which aren't threadsafe.
|
||||
mCrashHelper = mDecoder->GetCrashHelper();
|
||||
|
||||
if (mDecoder) {
|
||||
// Note: GMPCrashHelper must be created on main thread, as it may use
|
||||
// weak references, which aren't threadsafe.
|
||||
mCrashHelper = mDecoder->GetCrashHelper();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,6 +134,8 @@ private:
|
|||
DECL_MEDIA_PREF("media.gmp.decoder.enabled", PDMGMPEnabled, bool, true);
|
||||
DECL_MEDIA_PREF("media.gmp.decoder.aac", GMPAACPreferred, uint32_t, 0);
|
||||
DECL_MEDIA_PREF("media.gmp.decoder.h264", GMPH264Preferred, uint32_t, 0);
|
||||
DECL_MEDIA_PREF("media.eme.audio.blank", EMEBlankAudio, bool, false);
|
||||
DECL_MEDIA_PREF("media.eme.video.blank", EMEBlankVideo, bool, false);
|
||||
|
||||
// MediaDecoderStateMachine
|
||||
DECL_MEDIA_PREF("media.suspend-bkgnd-video.enabled", MDSMSuspendBackgroundVideoEnabled, bool, false);
|
||||
|
|
|
@ -150,6 +150,7 @@ protected:
|
|||
friend class H264Converter;
|
||||
friend class PDMFactory;
|
||||
friend class dom::RemoteDecoderModule;
|
||||
friend class EMEDecoderModule;
|
||||
|
||||
// Creates a Video decoder. The layers backend is passed in so that
|
||||
// decoders can determine whether hardware accelerated decoding can be used.
|
||||
|
|
|
@ -16,10 +16,13 @@
|
|||
#include "nsClassHashtable.h"
|
||||
#include "GMPDecoderModule.h"
|
||||
#include "MP4Decoder.h"
|
||||
#include "MediaPrefs.h"
|
||||
#include "mozilla/EMEUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
typedef MozPromiseRequestHolder<CDMProxy::DecryptPromise> DecryptPromiseRequestHolder;
|
||||
extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
|
||||
|
||||
class EMEDecryptor : public MediaDataDecoder {
|
||||
|
||||
|
@ -232,6 +235,12 @@ EMEDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
|
|||
{
|
||||
MOZ_ASSERT(aParams.mConfig.mCrypto.mValid);
|
||||
|
||||
if (MediaPrefs::EMEBlankVideo()) {
|
||||
EME_LOG("EMEDecoderModule::CreateVideoDecoder() creating a blank decoder.");
|
||||
RefPtr<PlatformDecoderModule> m(CreateBlankDecoderModule());
|
||||
return m->CreateVideoDecoder(aParams);
|
||||
}
|
||||
|
||||
if (SupportsMimeType(aParams.mConfig.mMimeType, nullptr)) {
|
||||
// GMP decodes. Assume that means it can decrypt too.
|
||||
RefPtr<MediaDataDecoderProxy> wrapper =
|
||||
|
@ -263,6 +272,12 @@ EMEDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
|
|||
MOZ_ASSERT(!SupportsMimeType(aParams.mConfig.mMimeType, nullptr));
|
||||
MOZ_ASSERT(mPDM);
|
||||
|
||||
if (MediaPrefs::EMEBlankAudio()) {
|
||||
EME_LOG("EMEDecoderModule::CreateAudioDecoder() creating a blank decoder.");
|
||||
RefPtr<PlatformDecoderModule> m(CreateBlankDecoderModule());
|
||||
return m->CreateAudioDecoder(aParams);
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder> decoder(mPDM->CreateDecoder(aParams));
|
||||
if (!decoder) {
|
||||
return nullptr;
|
||||
|
|
|
@ -598,13 +598,16 @@ void
|
|||
RemoteDataDecoder::Shutdown()
|
||||
{
|
||||
LOG("");
|
||||
MOZ_ASSERT(mJavaDecoder && mJavaCallbacks);
|
||||
|
||||
mJavaDecoder->Release();
|
||||
mJavaDecoder = nullptr;
|
||||
if (mJavaDecoder) {
|
||||
mJavaDecoder->Release();
|
||||
mJavaDecoder = nullptr;
|
||||
}
|
||||
|
||||
JavaCallbacksSupport::GetNative(mJavaCallbacks)->Cancel();
|
||||
mJavaCallbacks = nullptr;
|
||||
if (mJavaCallbacks) {
|
||||
JavaCallbacksSupport::GetNative(mJavaCallbacks)->Cancel();
|
||||
mJavaCallbacks = nullptr;
|
||||
}
|
||||
|
||||
mFormat = nullptr;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsSMILCompositor.h"
|
||||
#include "nsSMILCSSProperty.h"
|
||||
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsSMILCSSProperty.h"
|
||||
|
||||
// PLDHashEntryHdr methods
|
||||
bool
|
||||
|
@ -143,14 +144,17 @@ nsSMILCompositor::CreateSMILAttr()
|
|||
uint32_t
|
||||
nsSMILCompositor::GetFirstFuncToAffectSandwich()
|
||||
{
|
||||
// canThrottle is true when attributeName is not 'display' and
|
||||
// the element or subtree is display:none
|
||||
RefPtr<nsStyleContext> styleContext =
|
||||
nsComputedDOMStyle::GetStyleContextForElementNoFlush(mKey.mElement,
|
||||
nullptr, nullptr);
|
||||
// For performance reasons, we throttle most animations on elements in
|
||||
// display:none subtrees. (We can't throttle animations that target the
|
||||
// "display" property itself, though -- if we did, display:none elements
|
||||
// could never be dynamically displayed via animations.)
|
||||
// To determine whether we're in a display:none subtree, we will check the
|
||||
// element's primary frame since element in display:none subtree doesn't have
|
||||
// a primary frame. Before this process, we will construct frame when we
|
||||
// append an element to subtree. So we will not need to worry about pending
|
||||
// frame construction in this step.
|
||||
bool canThrottle = mKey.mAttributeName != nsGkAtoms::display &&
|
||||
styleContext &&
|
||||
styleContext->IsInDisplayNoneSubtree();
|
||||
!mKey.mElement->GetPrimaryFrame();
|
||||
|
||||
uint32_t i;
|
||||
for (i = mAnimationFunctions.Length(); i > 0; --i) {
|
||||
|
|
|
@ -15,13 +15,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=515116
|
|||
<div id="content">
|
||||
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"
|
||||
onload="this.pauseAnimations();">
|
||||
<desc>
|
||||
<defs>
|
||||
<filter>
|
||||
<feComponentTransfer>
|
||||
<feFuncR id="feFuncR" type="table"/>
|
||||
</feComponentTransfer>
|
||||
</filter>
|
||||
</desc>
|
||||
</defs>
|
||||
<text id="text">text</text>
|
||||
<path id="path"/>
|
||||
<polyline id="polyline"/>
|
||||
|
|
|
@ -104,6 +104,8 @@ partial interface HTMLMediaElement {
|
|||
readonly attribute MediaSource? mozMediaSourceObject;
|
||||
[ChromeOnly]
|
||||
readonly attribute DOMString mozDebugReaderData;
|
||||
[ChromeOnly, NewObject]
|
||||
Promise<DOMString> mozRequestDebugInfo();
|
||||
|
||||
[Pref="media.test.dumpDebugInfo"]
|
||||
void mozDumpDebugInfo();
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "LocaleService.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIToolkitChromeRegistry.h"
|
||||
|
||||
using namespace mozilla::intl;
|
||||
|
||||
/**
|
||||
* This function performs the actual language negotiation for the API.
|
||||
*
|
||||
* Currently it collects the locale ID used by nsChromeRegistry and
|
||||
* adds hardcoded "en-US" locale as a fallback.
|
||||
*/
|
||||
static void
|
||||
ReadAppLocales(nsTArray<nsCString>& aRetVal)
|
||||
{
|
||||
nsAutoCString uaLangTag;
|
||||
nsCOMPtr<nsIToolkitChromeRegistry> cr =
|
||||
mozilla::services::GetToolkitChromeRegistryService();
|
||||
if (cr) {
|
||||
cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), true, uaLangTag);
|
||||
}
|
||||
if (!uaLangTag.IsEmpty()) {
|
||||
aRetVal.AppendElement(uaLangTag);
|
||||
}
|
||||
|
||||
if (!uaLangTag.EqualsLiteral("en-US")) {
|
||||
aRetVal.AppendElement(NS_LITERAL_CSTRING("en-US"));
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::StaticAutoPtr<LocaleService> LocaleService::sInstance;
|
||||
|
||||
LocaleService* LocaleService::GetInstance()
|
||||
{
|
||||
if (!sInstance) {
|
||||
sInstance = new LocaleService();
|
||||
ClearOnShutdown(&sInstance);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
void
|
||||
LocaleService::GetAppLocales(nsTArray<nsCString>& aRetVal)
|
||||
{
|
||||
if (mAppLocales.IsEmpty()) {
|
||||
ReadAppLocales(mAppLocales);
|
||||
}
|
||||
aRetVal = mAppLocales;
|
||||
}
|
||||
|
||||
void
|
||||
LocaleService::GetAppLocale(nsACString& aRetVal)
|
||||
{
|
||||
if (mAppLocales.IsEmpty()) {
|
||||
ReadAppLocales(mAppLocales);
|
||||
}
|
||||
aRetVal = mAppLocales[0];
|
||||
}
|
||||
|
||||
void
|
||||
LocaleService::Refresh()
|
||||
{
|
||||
nsTArray<nsCString> newLocales;
|
||||
ReadAppLocales(newLocales);
|
||||
|
||||
if (mAppLocales != newLocales) {
|
||||
mAppLocales = Move(newLocales);
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->NotifyObservers(nullptr, "intl:app-locales-changed", nullptr);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_intl_LocaleService_h__
|
||||
#define mozilla_intl_LocaleService_h__
|
||||
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace intl {
|
||||
|
||||
|
||||
/**
|
||||
* LocaleService is a manager of language negotiation in Gecko.
|
||||
*
|
||||
* It's intended to be the core place for collecting available and
|
||||
* requested languages and negotiating them to produce a fallback
|
||||
* chain of locales for the application.
|
||||
*/
|
||||
class LocaleService
|
||||
{
|
||||
public:
|
||||
static LocaleService* GetInstance();
|
||||
|
||||
/**
|
||||
* Returns a list of locales that the application should be localized to.
|
||||
*
|
||||
* The result is a sorted list of valid locale IDs and it should be
|
||||
* used for all APIs that accept list of locales, like ECMA402 and L10n APIs.
|
||||
*
|
||||
* This API always returns at least one locale.
|
||||
*
|
||||
* Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
|
||||
*
|
||||
* Usage:
|
||||
* nsTArray<nsCString> appLocales;
|
||||
* LocaleService::GetInstance()->GetAppLocales(appLocales);
|
||||
*/
|
||||
void GetAppLocales(nsTArray<nsCString>& aRetVal);
|
||||
|
||||
/**
|
||||
* Returns the best locale that the application should be localized to.
|
||||
*
|
||||
* The result is a valid locale IDs and it should be
|
||||
* used for all APIs that do not handle language negotiation.
|
||||
*
|
||||
* Where possible, GetAppLocales should be preferred over this API and
|
||||
* all callsites should handle some form of "best effort" language
|
||||
* negotiation to respect user preferences in case the use case does
|
||||
* not have data for the first locale in the list.
|
||||
*
|
||||
* Example: "zh-Hans-HK"
|
||||
*
|
||||
* Usage:
|
||||
* nsAutoCString appLocale;
|
||||
* LocaleService::GetInstance()->GetAppLocale(appLocale);
|
||||
*/
|
||||
void GetAppLocale(nsACString& aRetVal);
|
||||
|
||||
/**
|
||||
* Triggers a refresh of the language negotiation process.
|
||||
*
|
||||
* If the result differs from the previous list, it will additionally
|
||||
* trigger a global event "intl:app-locales-changed".
|
||||
*/
|
||||
void Refresh();
|
||||
|
||||
protected:
|
||||
nsTArray<nsCString> mAppLocales;
|
||||
|
||||
private:
|
||||
static StaticAutoPtr<LocaleService> sInstance;
|
||||
};
|
||||
|
||||
} // intl
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_intl_LocaleService_h__ */
|
|
@ -35,7 +35,12 @@ EXPORTS += [
|
|||
'nsWin32Locale.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.intl += [
|
||||
'LocaleService.h'
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'LocaleService.cpp',
|
||||
'nsCollation.cpp',
|
||||
'nsLanguageAtomService.cpp',
|
||||
'nsLocale.cpp',
|
||||
|
@ -74,3 +79,6 @@ GENERATED_FILES += [
|
|||
langgroups = GENERATED_FILES['langGroups.properties.h']
|
||||
langgroups.script = 'props2arrays.py'
|
||||
langgroups.inputs = ['langGroups.properties']
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
DIRS += ['tests/gtest']
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "LocaleService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIToolkitChromeRegistry.h"
|
||||
|
||||
using namespace mozilla::intl;
|
||||
|
||||
|
||||
TEST(Intl_Locale_LocaleService, GetAppLocales) {
|
||||
nsTArray<nsCString> appLocales;
|
||||
LocaleService::GetInstance()->GetAppLocales(appLocales);
|
||||
|
||||
ASSERT_FALSE(appLocales.IsEmpty());
|
||||
}
|
||||
|
||||
TEST(Intl_Locale_LocaleService, GetAppLocales_firstMatchesChromeReg) {
|
||||
nsTArray<nsCString> appLocales;
|
||||
LocaleService::GetInstance()->GetAppLocales(appLocales);
|
||||
|
||||
nsAutoCString uaLangTag;
|
||||
nsCOMPtr<nsIToolkitChromeRegistry> cr =
|
||||
mozilla::services::GetToolkitChromeRegistryService();
|
||||
if (cr) {
|
||||
cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), true, uaLangTag);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(appLocales[0].Equals(uaLangTag));
|
||||
}
|
||||
|
||||
TEST(Intl_Locale_LocaleService, GetAppLocales_lastIsEnUS) {
|
||||
nsTArray<nsCString> appLocales;
|
||||
LocaleService::GetInstance()->GetAppLocales(appLocales);
|
||||
|
||||
int32_t len = appLocales.Length();
|
||||
ASSERT_TRUE(appLocales[len - 1].EqualsLiteral("en-US"));
|
||||
}
|
||||
|
||||
TEST(Intl_Locale_LocaleService, GetAppLocale) {
|
||||
nsTArray<nsCString> appLocales;
|
||||
LocaleService::GetInstance()->GetAppLocales(appLocales);
|
||||
|
||||
nsAutoCString locale;
|
||||
LocaleService::GetInstance()->GetAppLocale(locale);
|
||||
|
||||
ASSERT_TRUE(appLocales[0] == locale);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'TestLocaleService.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/intl/locale',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
|
@ -56,12 +56,6 @@ class Visitor:
|
|||
def visitProtocol(self, p):
|
||||
for namespace in p.namespaces:
|
||||
namespace.accept(self)
|
||||
for spawns in p.spawnsStmts:
|
||||
spawns.accept(self)
|
||||
for bridges in p.bridgesStmts:
|
||||
bridges.accept(self)
|
||||
for opens in p.opensStmts:
|
||||
opens.accept(self)
|
||||
for mgr in p.managers:
|
||||
mgr.accept(self)
|
||||
for managed in p.managesStmts:
|
||||
|
@ -72,15 +66,6 @@ class Visitor:
|
|||
def visitNamespace(self, ns):
|
||||
pass
|
||||
|
||||
def visitSpawnsStmt(self, spawns):
|
||||
pass
|
||||
|
||||
def visitBridgesStmt(self, bridges):
|
||||
pass
|
||||
|
||||
def visitOpensStmt(self, opens):
|
||||
pass
|
||||
|
||||
def visitManager(self, mgr):
|
||||
pass
|
||||
|
||||
|
@ -226,9 +211,6 @@ class Protocol(NamespacedNode):
|
|||
NamespacedNode.__init__(self, loc)
|
||||
self.sendSemantics = ASYNC
|
||||
self.nested = NOT_NESTED
|
||||
self.spawnsStmts = [ ]
|
||||
self.bridgesStmts = [ ]
|
||||
self.opensStmts = [ ]
|
||||
self.managers = [ ]
|
||||
self.managesStmts = [ ]
|
||||
self.messageDecls = [ ]
|
||||
|
@ -249,25 +231,6 @@ class UnionDecl(NamespacedNode):
|
|||
NamespacedNode.__init__(self, loc, name)
|
||||
self.components = components
|
||||
|
||||
class SpawnsStmt(Node):
|
||||
def __init__(self, loc, side, proto, spawnedAs):
|
||||
Node.__init__(self, loc)
|
||||
self.side = side
|
||||
self.proto = proto
|
||||
self.spawnedAs = spawnedAs
|
||||
|
||||
class BridgesStmt(Node):
|
||||
def __init__(self, loc, parentSide, childSide):
|
||||
Node.__init__(self, loc)
|
||||
self.parentSide = parentSide
|
||||
self.childSide = childSide
|
||||
|
||||
class OpensStmt(Node):
|
||||
def __init__(self, loc, side, proto):
|
||||
Node.__init__(self, loc)
|
||||
self.side = side
|
||||
self.proto = proto
|
||||
|
||||
class Manager(Node):
|
||||
def __init__(self, loc, managerName):
|
||||
Node.__init__(self, loc)
|
||||
|
|
|
@ -9,7 +9,7 @@ from collections import OrderedDict
|
|||
import ipdl.ast
|
||||
import ipdl.builtin
|
||||
from ipdl.cxx.ast import *
|
||||
from ipdl.type import ActorType, ProcessGraph, TypeVisitor, builtinHeaderIncludes
|
||||
from ipdl.type import ActorType, TypeVisitor, builtinHeaderIncludes
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
## "Public" interface to lowering
|
||||
|
@ -363,11 +363,6 @@ def _otherSide(side):
|
|||
if side == 'parent': return 'child'
|
||||
assert 0
|
||||
|
||||
def _sideToTransportMode(side):
|
||||
if side == 'parent': mode = 'SERVER'
|
||||
elif side == 'child': mode = 'CLIENT'
|
||||
return ExprVar('mozilla::ipc::Transport::MODE_'+ mode)
|
||||
|
||||
def _ifLogging(topLevelProtocol, stmts):
|
||||
iflogging = StmtIf(ExprCall(ExprVar('mozilla::ipc::LoggingEnabledFor'),
|
||||
args=[ topLevelProtocol ]))
|
||||
|
@ -1476,27 +1471,6 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
|||
_makeForwardDeclForActor(p.decl.type, 'Child')
|
||||
])
|
||||
|
||||
bridges = ProcessGraph.bridgesOf(p.decl.type)
|
||||
for bridge in bridges:
|
||||
ppt, pside = bridge.parent.ptype, _otherSide(bridge.parent.side)
|
||||
cpt, cside = bridge.child.ptype, _otherSide(bridge.child.side)
|
||||
self.hdrfile.addthings([
|
||||
Whitespace.NL,
|
||||
_makeForwardDeclForActor(ppt, pside),
|
||||
_makeForwardDeclForActor(cpt, cside)
|
||||
])
|
||||
self.cppIncludeHeaders.append(_protocolHeaderName(ppt._ast, pside))
|
||||
self.cppIncludeHeaders.append(_protocolHeaderName(cpt._ast, cside))
|
||||
|
||||
opens = ProcessGraph.opensOf(p.decl.type)
|
||||
for o in opens:
|
||||
optype, oside = o.opener.ptype, o.opener.side
|
||||
self.hdrfile.addthings([
|
||||
Whitespace.NL,
|
||||
_makeForwardDeclForActor(optype, oside)
|
||||
])
|
||||
self.cppIncludeHeaders.append(_protocolHeaderName(optype._ast, oside))
|
||||
|
||||
self.hdrfile.addthing(Whitespace("""
|
||||
//-----------------------------------------------------------------------------
|
||||
// Code common to %sChild and %sParent
|
||||
|
@ -1508,19 +1482,6 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
|||
self.hdrfile.addthing(_putInNamespaces(ns, p.namespaces))
|
||||
ns.addstmt(Whitespace.NL)
|
||||
|
||||
# user-facing methods for connecting two process with a new channel
|
||||
for bridge in bridges:
|
||||
bdecl, bdefn = _splitFuncDeclDefn(self.genBridgeFunc(bridge))
|
||||
ns.addstmts([ bdecl, Whitespace.NL ])
|
||||
self.funcDefns.append(bdefn)
|
||||
|
||||
# user-facing methods for opening a new channel across two
|
||||
# existing endpoints
|
||||
for o in opens:
|
||||
odecl, odefn = _splitFuncDeclDefn(self.genOpenFunc(o))
|
||||
ns.addstmts([ odecl, Whitespace.NL ])
|
||||
self.funcDefns.append(odefn)
|
||||
|
||||
edecl, edefn = _splitFuncDeclDefn(self.genEndpointFunc())
|
||||
ns.addstmts([ edecl, Whitespace.NL ])
|
||||
self.funcDefns.append(edefn)
|
||||
|
@ -1574,55 +1535,6 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
|||
ns.addstmts([ Whitespace.NL, Whitespace.NL ])
|
||||
|
||||
|
||||
def genBridgeFunc(self, bridge):
|
||||
p = self.protocol
|
||||
parentHandleType = _cxxBareType(ActorType(bridge.parent.ptype),
|
||||
_otherSide(bridge.parent.side),
|
||||
fq=1)
|
||||
parentvar = ExprVar('parentHandle')
|
||||
|
||||
childHandleType = _cxxBareType(ActorType(bridge.child.ptype),
|
||||
_otherSide(bridge.child.side),
|
||||
fq=1)
|
||||
childvar = ExprVar('childHandle')
|
||||
|
||||
bridgefunc = MethodDefn(MethodDecl(
|
||||
'Bridge',
|
||||
params=[ Decl(parentHandleType, parentvar.name),
|
||||
Decl(childHandleType, childvar.name) ],
|
||||
ret=Type.NSRESULT))
|
||||
bridgefunc.addstmt(StmtReturn(ExprCall(
|
||||
ExprVar('mozilla::ipc::Bridge'),
|
||||
args=[ _backstagePass(),
|
||||
p.callGetChannel(parentvar), p.callOtherPid(parentvar),
|
||||
p.callGetChannel(childvar), p.callOtherPid(childvar),
|
||||
_protocolId(p.decl.type),
|
||||
ExprVar(_messageStartName(p.decl.type) + 'Child')
|
||||
])))
|
||||
return bridgefunc
|
||||
|
||||
|
||||
def genOpenFunc(self, o):
|
||||
p = self.protocol
|
||||
localside = o.opener.side
|
||||
openertype = _cxxBareType(ActorType(o.opener.ptype), o.opener.side,
|
||||
fq=1)
|
||||
openervar = ExprVar('opener')
|
||||
openfunc = MethodDefn(MethodDecl(
|
||||
'Open',
|
||||
params=[ Decl(openertype, openervar.name) ],
|
||||
ret=Type.BOOL))
|
||||
openfunc.addstmt(StmtReturn(ExprCall(
|
||||
ExprVar('mozilla::ipc::Open'),
|
||||
args=[ _backstagePass(),
|
||||
p.callGetChannel(openervar), p.callOtherPid(openervar),
|
||||
_sideToTransportMode(localside),
|
||||
_protocolId(p.decl.type),
|
||||
ExprVar(_messageStartName(p.decl.type) + 'Child')
|
||||
])))
|
||||
return openfunc
|
||||
|
||||
|
||||
# Generate code for PFoo::CreateEndpoints.
|
||||
def genEndpointFunc(self):
|
||||
p = self.protocol.decl.type
|
||||
|
@ -2610,10 +2522,6 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
inherits=inherits,
|
||||
abstract=True)
|
||||
|
||||
bridgeActorsCreated = ProcessGraph.bridgeEndpointsOf(ptype, self.side)
|
||||
opensActorsCreated = ProcessGraph.opensEndpointsOf(ptype, self.side)
|
||||
channelOpenedActors = OrderedDict.fromkeys(bridgeActorsCreated + opensActorsCreated, None)
|
||||
|
||||
friends = _FindFriends().findFriends(ptype)
|
||||
if ptype.isManaged():
|
||||
friends.update(ptype.managers)
|
||||
|
@ -2634,13 +2542,6 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
self.prettyside)),
|
||||
Whitespace.NL ])
|
||||
|
||||
for actor in channelOpenedActors:
|
||||
self.hdrfile.addthings([
|
||||
Whitespace.NL,
|
||||
_makeForwardDeclForActor(actor.ptype, actor.side),
|
||||
Whitespace.NL
|
||||
])
|
||||
|
||||
self.cls.addstmt(Label.PROTECTED)
|
||||
for typedef in p.cxxTypedefs():
|
||||
self.cls.addstmt(typedef)
|
||||
|
@ -2692,17 +2593,6 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
ret=Type.BOOL,
|
||||
virtual=1, pure=1)))
|
||||
|
||||
for actor in channelOpenedActors:
|
||||
# add the Alloc interface for actors created when a
|
||||
# new channel is opened
|
||||
actortype = _cxxBareType(actor.asType(), actor.side)
|
||||
self.cls.addstmt(StmtDecl(MethodDecl(
|
||||
_allocMethod(actor.ptype, actor.side).name,
|
||||
params=[ Decl(Type('Transport', ptr=1), 'aTransport'),
|
||||
Decl(Type('ProcessId'), 'aOtherPid') ],
|
||||
ret=actortype,
|
||||
virtual=1, pure=1)))
|
||||
|
||||
# ActorDestroy() method; default is no-op
|
||||
self.cls.addstmts([
|
||||
Whitespace.NL,
|
||||
|
@ -2865,11 +2755,6 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
for md in p.messageDecls:
|
||||
self.visitMessageDecl(md)
|
||||
|
||||
# Handlers for the creation of actors when a new channel is
|
||||
# opened
|
||||
if len(channelOpenedActors):
|
||||
self.makeChannelOpenedHandlers(channelOpenedActors)
|
||||
|
||||
# add default cases
|
||||
default = StmtBlock()
|
||||
default.addstmt(StmtReturn(_Result.NotKnown))
|
||||
|
@ -3299,84 +3184,6 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
return case
|
||||
|
||||
|
||||
def makeChannelOpenedHandlers(self, actors):
|
||||
handlers = StmtBlock()
|
||||
|
||||
# unpack the transport descriptor et al.
|
||||
msgvar = self.msgvar
|
||||
tdvar = ExprVar('td')
|
||||
pidvar = ExprVar('pid')
|
||||
pvar = ExprVar('protocolid')
|
||||
iffail = StmtIf(ExprNot(ExprCall(
|
||||
ExprVar('mozilla::ipc::UnpackChannelOpened'),
|
||||
args=[ _backstagePass(),
|
||||
msgvar,
|
||||
ExprAddrOf(tdvar), ExprAddrOf(pidvar), ExprAddrOf(pvar) ])))
|
||||
iffail.addifstmt(StmtReturn(_Result.PayloadError))
|
||||
handlers.addstmts([
|
||||
StmtDecl(Decl(Type('TransportDescriptor'), tdvar.name)),
|
||||
StmtDecl(Decl(Type('ProcessId'), pidvar.name)),
|
||||
StmtDecl(Decl(Type('ProtocolId'), pvar.name)),
|
||||
iffail,
|
||||
Whitespace.NL
|
||||
])
|
||||
|
||||
def makeHandlerCase(actor):
|
||||
self.protocolCxxIncludes.append(_protocolHeaderName(actor.ptype._ast,
|
||||
actor.side))
|
||||
|
||||
case = StmtBlock()
|
||||
modevar = _sideToTransportMode(actor.side)
|
||||
tvar = ExprVar('t')
|
||||
iffailopen = StmtIf(ExprNot(ExprAssn(
|
||||
tvar,
|
||||
ExprCall(ExprVar('mozilla::ipc::OpenDescriptor'),
|
||||
args=[ tdvar, modevar ]))))
|
||||
iffailopen.addifstmt(StmtReturn(_Result.ValuError))
|
||||
|
||||
pvar = ExprVar('p')
|
||||
iffailalloc = StmtIf(ExprNot(ExprAssn(
|
||||
pvar,
|
||||
ExprCall(
|
||||
_allocMethod(actor.ptype, actor.side),
|
||||
args=[ _uniqueptrGet(tvar), pidvar ]))))
|
||||
iffailalloc.addifstmt(StmtReturn(_Result.ProcessingError))
|
||||
|
||||
settrans = StmtExpr(ExprCall(
|
||||
ExprSelect(pvar, '->', 'IToplevelProtocol::SetTransport'),
|
||||
args=[ExprMove(tvar)]))
|
||||
|
||||
case.addstmts([
|
||||
StmtDecl(Decl(_uniqueptr(Type('Transport')), tvar.name)),
|
||||
StmtDecl(Decl(Type(_actorName(actor.ptype.name(), actor.side),
|
||||
ptr=1), pvar.name)),
|
||||
iffailopen,
|
||||
iffailalloc,
|
||||
settrans,
|
||||
StmtBreak()
|
||||
])
|
||||
label = _messageStartName(actor.ptype)
|
||||
if actor.side == 'child':
|
||||
label += 'Child'
|
||||
return CaseLabel(label), case
|
||||
|
||||
pswitch = StmtSwitch(pvar)
|
||||
for actor in actors:
|
||||
label, case = makeHandlerCase(actor)
|
||||
pswitch.addcase(label, case)
|
||||
|
||||
die = Block()
|
||||
die.addstmts([ _fatalError('Invalid protocol'),
|
||||
StmtReturn(_Result.ValuError) ])
|
||||
pswitch.addcase(DefaultLabel(), die)
|
||||
|
||||
handlers.addstmts([
|
||||
pswitch,
|
||||
StmtReturn(_Result.Processed)
|
||||
])
|
||||
self.asyncSwitch.addcase(CaseLabel('CHANNEL_OPENED_MESSAGE_TYPE'),
|
||||
handlers)
|
||||
|
||||
##-------------------------------------------------------------------------
|
||||
## The next few functions are the crux of the IPDL code generator.
|
||||
## They generate code for all the nasty work of message
|
||||
|
|
|
@ -105,10 +105,8 @@ def locFromTok(p, num):
|
|||
##-----------------------------------------------------------------------------
|
||||
|
||||
reserved = set((
|
||||
'as',
|
||||
'async',
|
||||
'both',
|
||||
'bridges',
|
||||
'child',
|
||||
'class',
|
||||
'compress',
|
||||
|
@ -123,13 +121,11 @@ reserved = set((
|
|||
'namespace',
|
||||
'nested',
|
||||
'nullable',
|
||||
'opens',
|
||||
'or',
|
||||
'parent',
|
||||
'prio',
|
||||
'protocol',
|
||||
'returns',
|
||||
'spawns',
|
||||
'struct',
|
||||
'sync',
|
||||
'union',
|
||||
|
@ -350,61 +346,9 @@ def p_ProtocolDefn(p):
|
|||
|
||||
|
||||
def p_ProtocolBody(p):
|
||||
"""ProtocolBody : SpawnsStmtsOpt"""
|
||||
"""ProtocolBody : ManagersStmtOpt"""
|
||||
p[0] = p[1]
|
||||
|
||||
##--------------------
|
||||
## spawns/bridges/opens stmts
|
||||
|
||||
def p_SpawnsStmtsOpt(p):
|
||||
"""SpawnsStmtsOpt : SpawnsStmt SpawnsStmtsOpt
|
||||
| BridgesStmtsOpt"""
|
||||
if 2 == len(p):
|
||||
p[0] = p[1]
|
||||
else:
|
||||
p[2].spawnsStmts.insert(0, p[1])
|
||||
p[0] = p[2]
|
||||
|
||||
def p_SpawnsStmt(p):
|
||||
"""SpawnsStmt : PARENT SPAWNS ID AsOpt ';'
|
||||
| CHILD SPAWNS ID AsOpt ';'"""
|
||||
p[0] = SpawnsStmt(locFromTok(p, 1), p[1], p[3], p[4])
|
||||
|
||||
def p_AsOpt(p):
|
||||
"""AsOpt : AS PARENT
|
||||
| AS CHILD
|
||||
| """
|
||||
if 3 == len(p):
|
||||
p[0] = p[2]
|
||||
else:
|
||||
p[0] = 'child'
|
||||
|
||||
def p_BridgesStmtsOpt(p):
|
||||
"""BridgesStmtsOpt : BridgesStmt BridgesStmtsOpt
|
||||
| OpensStmtsOpt"""
|
||||
if 2 == len(p):
|
||||
p[0] = p[1]
|
||||
else:
|
||||
p[2].bridgesStmts.insert(0, p[1])
|
||||
p[0] = p[2]
|
||||
|
||||
def p_BridgesStmt(p):
|
||||
"""BridgesStmt : BRIDGES ID ',' ID ';'"""
|
||||
p[0] = BridgesStmt(locFromTok(p, 1), p[2], p[4])
|
||||
|
||||
def p_OpensStmtsOpt(p):
|
||||
"""OpensStmtsOpt : OpensStmt OpensStmtsOpt
|
||||
| ManagersStmtOpt"""
|
||||
if 2 == len(p):
|
||||
p[0] = p[1]
|
||||
else:
|
||||
p[2].opensStmts.insert(0, p[1])
|
||||
p[0] = p[2]
|
||||
|
||||
def p_OpensStmt(p):
|
||||
"""OpensStmt : PARENT OPENS ID ';'
|
||||
| CHILD OPENS ID ';'"""
|
||||
p[0] = OpensStmt(locFromTok(p, 1), p[1], p[3])
|
||||
|
||||
##--------------------
|
||||
## manager/manages stmts
|
||||
|
|
|
@ -15,17 +15,6 @@ import ipdl.builtin as builtin
|
|||
_DELETE_MSG = '__delete__'
|
||||
|
||||
|
||||
def _otherside(side):
|
||||
if side == 'parent': return 'child'
|
||||
elif side == 'child': return 'parent'
|
||||
else: assert 0 and 'unknown side "%s"'% (side)
|
||||
|
||||
def cartesian_product(s1, s2):
|
||||
for e1 in s1:
|
||||
for e2 in s2:
|
||||
yield (e1, e2)
|
||||
|
||||
|
||||
class TypeVisitor:
|
||||
def __init__(self):
|
||||
self.visited = set()
|
||||
|
@ -223,26 +212,11 @@ class MessageType(IPDLType):
|
|||
def hasImplicitActorParam(self):
|
||||
return self.isCtor() or self.isDtor()
|
||||
|
||||
class Bridge:
|
||||
def __init__(self, parentPtype, childPtype):
|
||||
assert parentPtype.isToplevel() and childPtype.isToplevel()
|
||||
self.parent = parentPtype
|
||||
self.child = childPtype
|
||||
|
||||
def __cmp__(self, o):
|
||||
return cmp(self.parent, o.parent) or cmp(self.child, o.child)
|
||||
def __eq__(self, o):
|
||||
return self.parent == o.parent and self.child == o.child
|
||||
def __hash__(self):
|
||||
return hash(self.parent) + hash(self.child)
|
||||
|
||||
class ProtocolType(IPDLType):
|
||||
def __init__(self, qname, nestedRange, sendSemantics):
|
||||
self.qname = qname
|
||||
self.nestedRange = nestedRange
|
||||
self.sendSemantics = sendSemantics
|
||||
self.spawns = set() # ProtocolType
|
||||
self.opens = set() # ProtocolType
|
||||
self.managers = [] # ProtocolType
|
||||
self.manages = [ ]
|
||||
self.hasDelete = False
|
||||
|
@ -258,14 +232,6 @@ class ProtocolType(IPDLType):
|
|||
assert mgrtype.isIPDL() and mgrtype.isProtocol()
|
||||
self.managers.append(mgrtype)
|
||||
|
||||
def addSpawn(self, ptype):
|
||||
assert self.isToplevel() and ptype.isToplevel()
|
||||
self.spawns.add(ptype)
|
||||
|
||||
def addOpen(self, ptype):
|
||||
assert self.isToplevel() and ptype.isToplevel()
|
||||
self.opens.add(ptype)
|
||||
|
||||
def managedBy(self, mgr):
|
||||
self.managers = list(mgr)
|
||||
|
||||
|
@ -556,10 +522,6 @@ With this information, it finally type checks the AST.'''
|
|||
if not runpass(CheckTypes(self.errors)):
|
||||
return False
|
||||
|
||||
if not (runpass(BuildProcessGraph(self.errors))
|
||||
and runpass(CheckProcessGraph(self.errors))):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def reportErrors(self, errout):
|
||||
|
@ -789,15 +751,6 @@ class GatherDecls(TcheckVisitor):
|
|||
# protocol scope
|
||||
self.symtab.enterScope(p)
|
||||
|
||||
for spawns in p.spawnsStmts:
|
||||
spawns.accept(self)
|
||||
|
||||
for bridges in p.bridgesStmts:
|
||||
bridges.accept(self)
|
||||
|
||||
for opens in p.opensStmts:
|
||||
opens.accept(self)
|
||||
|
||||
seenmgrs = set()
|
||||
for mgr in p.managers:
|
||||
if mgr.name in seenmgrs:
|
||||
|
@ -851,33 +804,6 @@ class GatherDecls(TcheckVisitor):
|
|||
self.symtab.exitScope(p)
|
||||
|
||||
|
||||
def visitSpawnsStmt(self, spawns):
|
||||
pname = spawns.proto
|
||||
spawns.proto = self.symtab.lookup(pname)
|
||||
if spawns.proto is None:
|
||||
self.error(spawns.loc,
|
||||
"spawned protocol `%s' has not been declared",
|
||||
pname)
|
||||
|
||||
def visitBridgesStmt(self, bridges):
|
||||
def lookup(p):
|
||||
decl = self.symtab.lookup(p)
|
||||
if decl is None:
|
||||
self.error(bridges.loc,
|
||||
"bridged protocol `%s' has not been declared", p)
|
||||
return decl
|
||||
bridges.parentSide = lookup(bridges.parentSide)
|
||||
bridges.childSide = lookup(bridges.childSide)
|
||||
|
||||
def visitOpensStmt(self, opens):
|
||||
pname = opens.proto
|
||||
opens.proto = self.symtab.lookup(pname)
|
||||
if opens.proto is None:
|
||||
self.error(opens.loc,
|
||||
"opened protocol `%s' has not been declared",
|
||||
pname)
|
||||
|
||||
|
||||
def visitManager(self, mgr):
|
||||
mgrdecl = self.symtab.lookup(mgr.name)
|
||||
pdecl = mgr.of.decl
|
||||
|
@ -1107,21 +1033,6 @@ class CheckTypes(TcheckVisitor):
|
|||
# check that we require no more "power" than our manager protocols
|
||||
ptype, pname = p.decl.type, p.decl.shortname
|
||||
|
||||
if len(p.spawnsStmts) and not ptype.isToplevel():
|
||||
self.error(p.decl.loc,
|
||||
"protocol `%s' is not top-level and so cannot declare |spawns|",
|
||||
pname)
|
||||
|
||||
if len(p.bridgesStmts) and not ptype.isToplevel():
|
||||
self.error(p.decl.loc,
|
||||
"protocol `%s' is not top-level and so cannot declare |bridges|",
|
||||
pname)
|
||||
|
||||
if len(p.opensStmts) and not ptype.isToplevel():
|
||||
self.error(p.decl.loc,
|
||||
"protocol `%s' is not top-level and so cannot declare |opens|",
|
||||
pname)
|
||||
|
||||
for mgrtype in ptype.managers:
|
||||
if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype):
|
||||
self.error(
|
||||
|
@ -1156,57 +1067,6 @@ class CheckTypes(TcheckVisitor):
|
|||
return Visitor.visitProtocol(self, p)
|
||||
|
||||
|
||||
def visitSpawnsStmt(self, spawns):
|
||||
if not self.ptype.isToplevel():
|
||||
self.error(spawns.loc,
|
||||
"only top-level protocols can have |spawns| statements; `%s' cannot",
|
||||
self.ptype.name())
|
||||
return
|
||||
|
||||
spawnedType = spawns.proto.type
|
||||
if not (spawnedType.isIPDL() and spawnedType.isProtocol()
|
||||
and spawnedType.isToplevel()):
|
||||
self.error(spawns.loc,
|
||||
"cannot spawn non-top-level-protocol `%s'",
|
||||
spawnedType.name())
|
||||
else:
|
||||
self.ptype.addSpawn(spawnedType)
|
||||
|
||||
|
||||
def visitBridgesStmt(self, bridges):
|
||||
if not self.ptype.isToplevel():
|
||||
self.error(bridges.loc,
|
||||
"only top-level protocols can have |bridges| statements; `%s' cannot",
|
||||
self.ptype.name())
|
||||
return
|
||||
|
||||
parentType = bridges.parentSide.type
|
||||
childType = bridges.childSide.type
|
||||
if not (parentType.isIPDL() and parentType.isProtocol()
|
||||
and childType.isIPDL() and childType.isProtocol()
|
||||
and parentType.isToplevel() and childType.isToplevel()):
|
||||
self.error(bridges.loc,
|
||||
"cannot bridge non-top-level-protocol(s) `%s' and `%s'",
|
||||
parentType.name(), childType.name())
|
||||
|
||||
|
||||
def visitOpensStmt(self, opens):
|
||||
if not self.ptype.isToplevel():
|
||||
self.error(opens.loc,
|
||||
"only top-level protocols can have |opens| statements; `%s' cannot",
|
||||
self.ptype.name())
|
||||
return
|
||||
|
||||
openedType = opens.proto.type
|
||||
if not (openedType.isIPDL() and openedType.isProtocol()
|
||||
and openedType.isToplevel()):
|
||||
self.error(opens.loc,
|
||||
"cannot open non-top-level-protocol `%s'",
|
||||
openedType.name())
|
||||
else:
|
||||
self.ptype.addOpen(openedType)
|
||||
|
||||
|
||||
def visitManagesStmt(self, mgs):
|
||||
pdecl = mgs.manager.decl
|
||||
ptype, pname = pdecl.type, pdecl.shortname
|
||||
|
@ -1298,330 +1158,3 @@ class CheckTypes(TcheckVisitor):
|
|||
loc,
|
||||
"ctor for protocol `%s', which is not managed by protocol `%s'",
|
||||
mname[:-len('constructor')], pname)
|
||||
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
|
||||
class Process:
|
||||
def __init__(self):
|
||||
self.actors = set() # set(Actor)
|
||||
self.edges = { } # Actor -> [ SpawnsEdge ]
|
||||
self.spawn = set() # set(Actor)
|
||||
|
||||
def edge(self, spawner, spawn):
|
||||
if spawner not in self.edges: self.edges[spawner] = [ ]
|
||||
self.edges[spawner].append(SpawnsEdge(spawner, spawn))
|
||||
self.spawn.add(spawn)
|
||||
|
||||
def iteredges(self):
|
||||
for edgelist in self.edges.itervalues():
|
||||
for edge in edgelist:
|
||||
yield edge
|
||||
|
||||
def merge(self, o):
|
||||
'Merge the Process |o| into this Process'
|
||||
if self == o:
|
||||
return
|
||||
for actor in o.actors:
|
||||
ProcessGraph.actorToProcess[actor] = self
|
||||
self.actors.update(o.actors)
|
||||
self.edges.update(o.edges)
|
||||
self.spawn.update(o.spawn)
|
||||
ProcessGraph.processes.remove(o)
|
||||
|
||||
def spawns(self, actor):
|
||||
return actor in self.spawn
|
||||
|
||||
def __cmp__(self, o): return cmp(self.actors, o.actors)
|
||||
def __eq__(self, o): return self.actors == o.actors
|
||||
def __hash__(self): return hash(id(self))
|
||||
def __repr__(self):
|
||||
return reduce(lambda a, x: str(a) + str(x) +'|', self.actors, '|')
|
||||
def __str__(self): return repr(self)
|
||||
|
||||
class Actor:
|
||||
def __init__(self, ptype, side):
|
||||
self.ptype = ptype
|
||||
self.side = side
|
||||
|
||||
def asType(self):
|
||||
return ActorType(self.ptype)
|
||||
def other(self):
|
||||
return Actor(self.ptype, _otherside(self.side))
|
||||
|
||||
def __cmp__(self, o):
|
||||
return cmp(self.ptype, o.ptype) or cmp(self.side, o.side)
|
||||
def __eq__(self, o):
|
||||
return self.ptype == o.ptype and self.side == o.side
|
||||
def __hash__(self): return hash(repr(self))
|
||||
def __repr__(self): return '%s%s'% (self.ptype.name(), self.side.title())
|
||||
def __str__(self): return repr(self)
|
||||
|
||||
class SpawnsEdge:
|
||||
def __init__(self, spawner, spawn):
|
||||
self.spawner = spawner # Actor
|
||||
self.spawn = spawn # Actor
|
||||
def __repr__(self):
|
||||
return '(%r)--spawns-->(%r)'% (self.spawner, self.spawn)
|
||||
def __str__(self): return repr(self)
|
||||
|
||||
class BridgeEdge:
|
||||
def __init__(self, bridgeProto, parent, child):
|
||||
self.bridgeProto = bridgeProto # ProtocolType
|
||||
self.parent = parent # Actor
|
||||
self.child = child # Actor
|
||||
def __repr__(self):
|
||||
return '(%r)--%s bridge-->(%r)'% (
|
||||
self.parent, self.bridgeProto.name(), self.child)
|
||||
def __str__(self): return repr(self)
|
||||
|
||||
class OpensEdge:
|
||||
def __init__(self, opener, openedProto):
|
||||
self.opener = opener # Actor
|
||||
self.openedProto = openedProto # ProtocolType
|
||||
def __repr__(self):
|
||||
return '(%r)--opens-->(%s)'% (self.opener, self.openedProto.name())
|
||||
def __str__(self): return repr(self)
|
||||
|
||||
# "singleton" class with state that persists across type checking of
|
||||
# all protocols
|
||||
class ProcessGraph:
|
||||
processes = set() # set(Process)
|
||||
bridges = { } # ProtocolType -> [ BridgeEdge ]
|
||||
opens = { } # ProtocolType -> [ OpensEdge ]
|
||||
actorToProcess = { } # Actor -> Process
|
||||
visitedSpawns = set() # set(ActorType)
|
||||
visitedBridges = set() # set(ActorType)
|
||||
|
||||
@classmethod
|
||||
def findProcess(cls, actor):
|
||||
return cls.actorToProcess.get(actor, None)
|
||||
|
||||
@classmethod
|
||||
def getProcess(cls, actor):
|
||||
if actor not in cls.actorToProcess:
|
||||
p = Process()
|
||||
p.actors.add(actor)
|
||||
cls.processes.add(p)
|
||||
cls.actorToProcess[actor] = p
|
||||
return cls.actorToProcess[actor]
|
||||
|
||||
@classmethod
|
||||
def bridgesOf(cls, bridgeP):
|
||||
return cls.bridges.get(bridgeP, [])
|
||||
|
||||
@classmethod
|
||||
def bridgeEndpointsOf(cls, ptype, side):
|
||||
actor = Actor(ptype, side)
|
||||
endpoints = []
|
||||
for b in cls.iterbridges():
|
||||
if b.parent == actor:
|
||||
endpoints.append(Actor(b.bridgeProto, 'parent'))
|
||||
if b.child == actor:
|
||||
endpoints.append(Actor(b.bridgeProto, 'child'))
|
||||
return endpoints
|
||||
|
||||
@classmethod
|
||||
def iterbridges(cls):
|
||||
for edges in cls.bridges.itervalues():
|
||||
for bridge in edges:
|
||||
yield bridge
|
||||
|
||||
@classmethod
|
||||
def opensOf(cls, openedP):
|
||||
return cls.opens.get(openedP, [])
|
||||
|
||||
@classmethod
|
||||
def opensEndpointsOf(cls, ptype, side):
|
||||
actor = Actor(ptype, side)
|
||||
endpoints = []
|
||||
for o in cls.iteropens():
|
||||
if actor == o.opener:
|
||||
endpoints.append(Actor(o.openedProto, o.opener.side))
|
||||
elif actor == o.opener.other():
|
||||
endpoints.append(Actor(o.openedProto, o.opener.other().side))
|
||||
return endpoints
|
||||
|
||||
@classmethod
|
||||
def iteropens(cls):
|
||||
for edges in cls.opens.itervalues():
|
||||
for opens in edges:
|
||||
yield opens
|
||||
|
||||
@classmethod
|
||||
def spawn(cls, spawner, remoteSpawn):
|
||||
localSpawn = remoteSpawn.other()
|
||||
spawnerProcess = ProcessGraph.getProcess(spawner)
|
||||
spawnerProcess.merge(ProcessGraph.getProcess(localSpawn))
|
||||
spawnerProcess.edge(spawner, remoteSpawn)
|
||||
|
||||
@classmethod
|
||||
def bridge(cls, parent, child, bridgeP):
|
||||
bridgeParent = Actor(bridgeP, 'parent')
|
||||
parentProcess = ProcessGraph.getProcess(parent)
|
||||
parentProcess.merge(ProcessGraph.getProcess(bridgeParent))
|
||||
bridgeChild = Actor(bridgeP, 'child')
|
||||
childProcess = ProcessGraph.getProcess(child)
|
||||
childProcess.merge(ProcessGraph.getProcess(bridgeChild))
|
||||
if bridgeP not in cls.bridges:
|
||||
cls.bridges[bridgeP] = [ ]
|
||||
cls.bridges[bridgeP].append(BridgeEdge(bridgeP, parent, child))
|
||||
|
||||
@classmethod
|
||||
def open(cls, opener, opened, openedP):
|
||||
remoteOpener, remoteOpened, = opener.other(), opened.other()
|
||||
openerProcess = ProcessGraph.getProcess(opener)
|
||||
openerProcess.merge(ProcessGraph.getProcess(opened))
|
||||
remoteOpenerProcess = ProcessGraph.getProcess(remoteOpener)
|
||||
remoteOpenerProcess.merge(ProcessGraph.getProcess(remoteOpened))
|
||||
if openedP not in cls.opens:
|
||||
cls.opens[openedP] = [ ]
|
||||
cls.opens[openedP].append(OpensEdge(opener, openedP))
|
||||
|
||||
|
||||
class BuildProcessGraph(TcheckVisitor):
|
||||
class findSpawns(TcheckVisitor):
|
||||
def __init__(self, errors):
|
||||
TcheckVisitor.__init__(self, None, errors)
|
||||
|
||||
def visitTranslationUnit(self, tu):
|
||||
TcheckVisitor.visitTranslationUnit(self, tu)
|
||||
|
||||
def visitInclude(self, inc):
|
||||
if inc.tu.protocol:
|
||||
inc.tu.protocol.accept(self)
|
||||
|
||||
def visitProtocol(self, p):
|
||||
ptype = p.decl.type
|
||||
# non-top-level protocols don't add any information
|
||||
if not ptype.isToplevel() or ptype in ProcessGraph.visitedSpawns:
|
||||
return
|
||||
|
||||
ProcessGraph.visitedSpawns.add(ptype)
|
||||
self.visiting = ptype
|
||||
ProcessGraph.getProcess(Actor(ptype, 'parent'))
|
||||
ProcessGraph.getProcess(Actor(ptype, 'child'))
|
||||
return TcheckVisitor.visitProtocol(self, p)
|
||||
|
||||
def visitSpawnsStmt(self, spawns):
|
||||
# The picture here is:
|
||||
# [ spawner | localSpawn | ??? ] (process 1)
|
||||
# |
|
||||
# |
|
||||
# [ remoteSpawn | ???] (process 2)
|
||||
#
|
||||
# A spawns stmt tells us that |spawner| and |localSpawn|
|
||||
# are in the same process.
|
||||
spawner = Actor(self.visiting, spawns.side)
|
||||
remoteSpawn = Actor(spawns.proto.type, spawns.spawnedAs)
|
||||
ProcessGraph.spawn(spawner, remoteSpawn)
|
||||
|
||||
def __init__(self, errors):
|
||||
TcheckVisitor.__init__(self, None, errors)
|
||||
self.visiting = None # ActorType
|
||||
self.visited = set() # set(ActorType)
|
||||
|
||||
def visitTranslationUnit(self, tu):
|
||||
tu.accept(self.findSpawns(self.errors))
|
||||
TcheckVisitor.visitTranslationUnit(self, tu)
|
||||
|
||||
def visitInclude(self, inc):
|
||||
if inc.tu.protocol:
|
||||
inc.tu.protocol.accept(self)
|
||||
|
||||
def visitProtocol(self, p):
|
||||
ptype = p.decl.type
|
||||
# non-top-level protocols don't add any information
|
||||
if not ptype.isToplevel() or ptype in ProcessGraph.visitedBridges:
|
||||
return
|
||||
|
||||
ProcessGraph.visitedBridges.add(ptype)
|
||||
self.visiting = ptype
|
||||
return TcheckVisitor.visitProtocol(self, p)
|
||||
|
||||
def visitBridgesStmt(self, bridges):
|
||||
bridgeProto = self.visiting
|
||||
parentSideProto = bridges.parentSide.type
|
||||
childSideProto = bridges.childSide.type
|
||||
|
||||
# the picture here is:
|
||||
# (process 1|
|
||||
# [ parentSide(Parent|Child) | childSide(Parent|Child) | ... ]
|
||||
# | |
|
||||
# | (process 2| |
|
||||
# [ parentSide(Child|Parent) | bridgeParent ] |
|
||||
# | |
|
||||
# | | (process 3|
|
||||
# [ bridgeChild | childSide(Child|Parent) ]
|
||||
#
|
||||
# First we have to figure out which parentSide/childSide
|
||||
# actors live in the same process. The possibilities are {
|
||||
# parent, child } x { parent, child }. (Multiple matches
|
||||
# aren't allowed yet.) Then we make ProcessGraph aware of the
|
||||
# new bridge.
|
||||
parentSideActor, childSideActor = None, None
|
||||
pc = ( 'parent', 'child' )
|
||||
for parentSide, childSide in cartesian_product(pc, pc):
|
||||
pactor = Actor(parentSideProto, parentSide)
|
||||
pproc = ProcessGraph.findProcess(pactor)
|
||||
cactor = Actor(childSideProto, childSide)
|
||||
cproc = ProcessGraph.findProcess(cactor)
|
||||
assert pproc and cproc
|
||||
|
||||
if pproc == cproc:
|
||||
if parentSideActor is not None:
|
||||
if parentSideProto != childSideProto:
|
||||
self.error(bridges.loc,
|
||||
"ambiguous bridge `%s' between `%s' and `%s'",
|
||||
bridgeProto.name(),
|
||||
parentSideProto.name(),
|
||||
childSideProto.name())
|
||||
else:
|
||||
parentSideActor, childSideActor = pactor.other(), cactor.other()
|
||||
|
||||
if parentSideActor is None:
|
||||
self.error(bridges.loc,
|
||||
"`%s' and `%s' cannot be bridged by `%s' ",
|
||||
parentSideProto.name(), childSideProto.name(),
|
||||
bridgeProto.name())
|
||||
|
||||
ProcessGraph.bridge(parentSideActor, childSideActor, bridgeProto)
|
||||
|
||||
def visitOpensStmt(self, opens):
|
||||
openedP = opens.proto.type
|
||||
opener = Actor(self.visiting, opens.side)
|
||||
opened = Actor(openedP, opens.side)
|
||||
|
||||
# The picture here is:
|
||||
# [ opener | opened ] (process 1)
|
||||
# | |
|
||||
# | |
|
||||
# [ remoteOpener | remoteOpened ] (process 2)
|
||||
#
|
||||
# An opens stmt tells us that the pairs |opener|/|opened|
|
||||
# and |remoteOpener|/|remoteOpened| are each in the same
|
||||
# process.
|
||||
ProcessGraph.open(opener, opened, openedP)
|
||||
|
||||
|
||||
class CheckProcessGraph(TcheckVisitor):
|
||||
def __init__(self, errors):
|
||||
TcheckVisitor.__init__(self, None, errors)
|
||||
|
||||
# TODO: verify spawns-per-process assumption and check that graph
|
||||
# is a dag
|
||||
def visitTranslationUnit(self, tu):
|
||||
if 0:
|
||||
print 'Processes'
|
||||
for process in ProcessGraph.processes:
|
||||
print ' ', process
|
||||
for edge in process.iteredges():
|
||||
print ' ', edge
|
||||
print 'Bridges'
|
||||
for bridgeList in ProcessGraph.bridges.itervalues():
|
||||
for bridge in bridgeList:
|
||||
print ' ', bridge
|
||||
print 'Opens'
|
||||
for opensList in ProcessGraph.opens.itervalues():
|
||||
for opens in opensList:
|
||||
print ' ', opens
|
||||
|
|
|
@ -6,7 +6,6 @@ IPDLTESTSRCS = $(filter Test%,$(CPPSRCS))
|
|||
IPDLTESTS = $(IPDLTESTSRCS:.cpp=)
|
||||
|
||||
EXTRA_PROTOCOLS = \
|
||||
TestBridgeSub \
|
||||
TestEndpointBridgeSub \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
include protocol PTestBridgeMainSub;
|
||||
include protocol PTestBridgeSub;
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
protocol PTestBridgeMain {
|
||||
child spawns PTestBridgeSub;
|
||||
child opens PTestBridgeMainSub;
|
||||
|
||||
child:
|
||||
async Start();
|
||||
|
||||
parent:
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace _ipdltest
|
|
@ -1,35 +0,0 @@
|
|||
include protocol PTestBridgeMain;
|
||||
include protocol PTestBridgeSub;
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
// (Bridge protocols can have different semantics than the endpoints
|
||||
// they bridge)
|
||||
intr protocol PTestBridgeMainSub {
|
||||
bridges PTestBridgeMain, PTestBridgeSub;
|
||||
|
||||
child:
|
||||
async Hi();
|
||||
intr HiRpc();
|
||||
|
||||
parent:
|
||||
async Hello();
|
||||
sync HelloSync();
|
||||
intr HelloRpc();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START: recv Hello goto HI;
|
||||
state HI: send Hi goto HELLO_SYNC;
|
||||
state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
|
||||
state HELLO_RPC: answer HelloRpc goto HI_RPC;
|
||||
state HI_RPC: call HiRpc goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace _ipdltest
|
|
@ -1,27 +0,0 @@
|
|||
include protocol PTestBridgeMainSub;
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
protocol PTestBridgeSub {
|
||||
child:
|
||||
async Ping();
|
||||
|
||||
parent:
|
||||
async BridgeEm();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Ping goto BRIDGEEM;
|
||||
state BRIDGEEM:
|
||||
recv BridgeEm goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace _ipdltest
|
|
@ -9,7 +9,6 @@ namespace _ipdltest {
|
|||
|
||||
|
||||
protocol PTestEndpointBridgeMain {
|
||||
child spawns PTestEndpointBridgeSub;
|
||||
|
||||
child:
|
||||
async Start();
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
include protocol PTestOpensOpened;
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
protocol PTestOpens {
|
||||
// This channel is opened and parked on a non-main thread
|
||||
child opens PTestOpensOpened;
|
||||
|
||||
child:
|
||||
async Start();
|
||||
|
||||
parent:
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace _ipdltest
|
|
@ -1,30 +0,0 @@
|
|||
namespace mozilla {
|
||||
namespace _ipdltest2 {
|
||||
|
||||
// (Opens protocols can have different semantics than the endpoints
|
||||
// that opened them)
|
||||
intr protocol PTestOpensOpened {
|
||||
child:
|
||||
async Hi();
|
||||
intr HiRpc();
|
||||
|
||||
parent:
|
||||
async Hello();
|
||||
sync HelloSync();
|
||||
intr HelloRpc();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START: recv Hello goto HI;
|
||||
state HI: send Hi goto HELLO_SYNC;
|
||||
state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
|
||||
state HELLO_RPC: answer HelloRpc goto HI_RPC;
|
||||
state HI_RPC: call HiRpc goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace _ipdltest2
|
|
@ -1,233 +0,0 @@
|
|||
#include "TestBridgeMain.h"
|
||||
|
||||
#include "base/task.h"
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
#include "IPDLUnitTestSubprocess.h"
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// main process
|
||||
void
|
||||
TestBridgeMainParent::Main()
|
||||
{
|
||||
if (!SendStart())
|
||||
fail("sending Start");
|
||||
}
|
||||
|
||||
PTestBridgeMainSubParent*
|
||||
TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport,
|
||||
ProcessId otherPid)
|
||||
{
|
||||
nsAutoPtr<TestBridgeMainSubParent> a(new TestBridgeMainSubParent(transport));
|
||||
if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ParentSide)) {
|
||||
return nullptr;
|
||||
}
|
||||
return a.forget();
|
||||
}
|
||||
|
||||
void
|
||||
TestBridgeMainParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
passed("ok");
|
||||
QuitParent();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestBridgeMainSubParent::RecvHello()
|
||||
{
|
||||
if (!SendHi()) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestBridgeMainSubParent::RecvHelloSync()
|
||||
{
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestBridgeMainSubParent::AnswerHelloRpc()
|
||||
{
|
||||
if (!CallHiRpc()) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
TestBridgeMainSubParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
|
||||
// ActorDestroy() is just a callback from IPDL-generated code,
|
||||
// which needs the top-level actor (this) to stay alive a little
|
||||
// longer so other things can be cleaned up.
|
||||
MessageLoop::current()->PostTask(
|
||||
do_AddRef(new DeleteTask<TestBridgeMainSubParent>(this)));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// sub process --- child of main
|
||||
TestBridgeMainChild* gBridgeMainChild;
|
||||
|
||||
TestBridgeMainChild::TestBridgeMainChild()
|
||||
: mSubprocess(nullptr)
|
||||
{
|
||||
gBridgeMainChild = this;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestBridgeMainChild::RecvStart()
|
||||
{
|
||||
vector<string> subsubArgs;
|
||||
subsubArgs.push_back("TestBridgeSub");
|
||||
|
||||
mSubprocess = new IPDLUnitTestSubprocess();
|
||||
if (!mSubprocess->SyncLaunch(subsubArgs))
|
||||
fail("problem launching subprocess");
|
||||
|
||||
IPC::Channel* transport = mSubprocess->GetChannel();
|
||||
if (!transport)
|
||||
fail("no transport");
|
||||
|
||||
TestBridgeSubParent* bsp = new TestBridgeSubParent();
|
||||
bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle()));
|
||||
|
||||
bsp->Main();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
TestBridgeMainChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
// NB: this is kosher because QuitChild() joins with the IO thread
|
||||
XRE_GetIOMessageLoop()->PostTask(
|
||||
do_AddRef(new DeleteTask<IPDLUnitTestSubprocess>(mSubprocess)));
|
||||
QuitChild();
|
||||
}
|
||||
|
||||
void
|
||||
TestBridgeSubParent::Main()
|
||||
{
|
||||
if (!SendPing())
|
||||
fail("sending Ping");
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestBridgeSubParent::RecvBridgeEm()
|
||||
{
|
||||
if (NS_FAILED(PTestBridgeMainSub::Bridge(gBridgeMainChild, this)))
|
||||
fail("bridging Main and Sub");
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
TestBridgeSubParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
gBridgeMainChild->Close();
|
||||
|
||||
// ActorDestroy() is just a callback from IPDL-generated code,
|
||||
// which needs the top-level actor (this) to stay alive a little
|
||||
// longer so other things can be cleaned up.
|
||||
MessageLoop::current()->PostTask(
|
||||
do_AddRef(new DeleteTask<TestBridgeSubParent>(this)));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// subsub process --- child of sub
|
||||
|
||||
static TestBridgeSubChild* gBridgeSubChild;
|
||||
|
||||
TestBridgeSubChild::TestBridgeSubChild()
|
||||
{
|
||||
gBridgeSubChild = this;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestBridgeSubChild::RecvPing()
|
||||
{
|
||||
if (!SendBridgeEm())
|
||||
fail("sending BridgeEm");
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
PTestBridgeMainSubChild*
|
||||
TestBridgeSubChild::AllocPTestBridgeMainSubChild(Transport* transport,
|
||||
ProcessId otherPid)
|
||||
{
|
||||
nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport));
|
||||
if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!a->SendHello())
|
||||
fail("sending Hello");
|
||||
|
||||
return a.forget();
|
||||
}
|
||||
|
||||
void
|
||||
TestBridgeSubChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
QuitChild();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestBridgeMainSubChild::RecvHi()
|
||||
{
|
||||
if (!SendHelloSync())
|
||||
fail("sending HelloSync");
|
||||
if (!CallHelloRpc())
|
||||
fail("calling HelloRpc");
|
||||
if (!mGotHi)
|
||||
fail("didn't answer HiRpc");
|
||||
|
||||
// Need to close the channel without message-processing frames on
|
||||
// the C++ stack
|
||||
MessageLoop::current()->PostTask(
|
||||
NewNonOwningRunnableMethod(this, &TestBridgeMainSubChild::Close));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestBridgeMainSubChild::AnswerHiRpc()
|
||||
{
|
||||
mGotHi = true; // d00d
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
TestBridgeMainSubChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
|
||||
gBridgeSubChild->Close();
|
||||
|
||||
// ActorDestroy() is just a callback from IPDL-generated code,
|
||||
// which needs the top-level actor (this) to stay alive a little
|
||||
// longer so other things can be cleaned up.
|
||||
MessageLoop::current()->PostTask(
|
||||
do_AddRef(new DeleteTask<TestBridgeMainSubChild>(this)));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace _ipdltest
|
|
@ -1,149 +0,0 @@
|
|||
#ifndef mozilla__ipdltest_TestBridgeMain_h
|
||||
#define mozilla__ipdltest_TestBridgeMain_h 1
|
||||
|
||||
#include "mozilla/_ipdltest/IPDLUnitTests.h"
|
||||
|
||||
#include "mozilla/_ipdltest/PTestBridgeMainParent.h"
|
||||
#include "mozilla/_ipdltest/PTestBridgeMainChild.h"
|
||||
|
||||
#include "mozilla/_ipdltest/PTestBridgeSubParent.h"
|
||||
#include "mozilla/_ipdltest/PTestBridgeSubChild.h"
|
||||
|
||||
#include "mozilla/_ipdltest/PTestBridgeMainSubParent.h"
|
||||
#include "mozilla/_ipdltest/PTestBridgeMainSubChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Main" process
|
||||
//
|
||||
class TestBridgeMainParent :
|
||||
public PTestBridgeMainParent
|
||||
{
|
||||
public:
|
||||
TestBridgeMainParent() {}
|
||||
virtual ~TestBridgeMainParent() {}
|
||||
|
||||
static bool RunTestInProcesses() { return true; }
|
||||
static bool RunTestInThreads() { return false; }
|
||||
|
||||
void Main();
|
||||
|
||||
protected:
|
||||
virtual PTestBridgeMainSubParent*
|
||||
AllocPTestBridgeMainSubParent(Transport* transport,
|
||||
ProcessId otherProcess) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
};
|
||||
|
||||
class TestBridgeMainSubParent :
|
||||
public PTestBridgeMainSubParent
|
||||
{
|
||||
public:
|
||||
explicit TestBridgeMainSubParent(Transport* aTransport)
|
||||
: mTransport(aTransport)
|
||||
{}
|
||||
virtual ~TestBridgeMainSubParent() {}
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::IPCResult RecvHello() override;
|
||||
virtual mozilla::ipc::IPCResult RecvHelloSync() override;
|
||||
virtual mozilla::ipc::IPCResult AnswerHelloRpc() override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
Transport* mTransport;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Sub" process --- child of "main"
|
||||
//
|
||||
class TestBridgeSubParent;
|
||||
|
||||
class TestBridgeMainChild :
|
||||
public PTestBridgeMainChild
|
||||
{
|
||||
public:
|
||||
TestBridgeMainChild();
|
||||
virtual ~TestBridgeMainChild() {}
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::IPCResult RecvStart() override;
|
||||
|
||||
virtual PTestBridgeMainSubChild*
|
||||
AllocPTestBridgeMainSubChild(Transport* transport,
|
||||
ProcessId otherProcess) override
|
||||
{
|
||||
// This shouldn't be called. It's just a byproduct of testing that
|
||||
// the right code is generated for a bridged protocol that's also
|
||||
// opened, but we only test bridging here.
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
IPDLUnitTestSubprocess* mSubprocess;
|
||||
};
|
||||
|
||||
class TestBridgeSubParent :
|
||||
public PTestBridgeSubParent
|
||||
{
|
||||
public:
|
||||
TestBridgeSubParent() {}
|
||||
virtual ~TestBridgeSubParent() {}
|
||||
|
||||
void Main();
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::IPCResult RecvBridgeEm() override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Subsub" process --- child of "sub"
|
||||
//
|
||||
class TestBridgeSubChild :
|
||||
public PTestBridgeSubChild
|
||||
{
|
||||
public:
|
||||
TestBridgeSubChild();
|
||||
virtual ~TestBridgeSubChild() {}
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::IPCResult RecvPing() override;
|
||||
|
||||
virtual PTestBridgeMainSubChild*
|
||||
AllocPTestBridgeMainSubChild(Transport* transport,
|
||||
ProcessId otherProcess) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
};
|
||||
|
||||
class TestBridgeMainSubChild :
|
||||
public PTestBridgeMainSubChild
|
||||
{
|
||||
public:
|
||||
explicit TestBridgeMainSubChild(Transport* aTransport)
|
||||
: mGotHi(false)
|
||||
, mTransport(aTransport)
|
||||
{}
|
||||
virtual ~TestBridgeMainSubChild() {}
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::IPCResult RecvHi() override;
|
||||
virtual mozilla::ipc::IPCResult AnswerHiRpc() override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
bool mGotHi;
|
||||
Transport* mTransport;
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif // ifndef mozilla__ipdltest_TestBridgeMain_h
|
|
@ -1,257 +0,0 @@
|
|||
#include "base/task.h"
|
||||
#include "base/thread.h"
|
||||
|
||||
#include "TestOpens.h"
|
||||
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
using base::ProcessHandle;
|
||||
using base::Thread;
|
||||
|
||||
namespace mozilla {
|
||||
// NB: this is generally bad style, but I am lazy.
|
||||
using namespace _ipdltest;
|
||||
using namespace _ipdltest2;
|
||||
|
||||
static MessageLoop* gMainThread;
|
||||
|
||||
static void
|
||||
AssertNotMainThread()
|
||||
{
|
||||
if (!gMainThread)
|
||||
fail("gMainThread is not initialized");
|
||||
if (MessageLoop::current() == gMainThread)
|
||||
fail("unexpectedly called on the main thread");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// parent
|
||||
|
||||
// Thread on which TestOpensOpenedParent runs
|
||||
static Thread* gParentThread;
|
||||
|
||||
void
|
||||
TestOpensParent::Main()
|
||||
{
|
||||
if (!SendStart())
|
||||
fail("sending Start");
|
||||
}
|
||||
|
||||
static void
|
||||
OpenParent(TestOpensOpenedParent* aParent,
|
||||
Transport* aTransport, base::ProcessId aOtherPid)
|
||||
{
|
||||
AssertNotMainThread();
|
||||
|
||||
// Open the actor on the off-main thread to park it there.
|
||||
// Messages will be delivered to this thread's message loop
|
||||
// instead of the main thread's.
|
||||
if (!aParent->Open(aTransport, aOtherPid,
|
||||
XRE_GetIOMessageLoop(), ipc::ParentSide))
|
||||
fail("opening Parent");
|
||||
}
|
||||
|
||||
PTestOpensOpenedParent*
|
||||
TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
|
||||
ProcessId otherPid)
|
||||
{
|
||||
gMainThread = MessageLoop::current();
|
||||
|
||||
gParentThread = new Thread("ParentThread");
|
||||
if (!gParentThread->Start())
|
||||
fail("starting parent thread");
|
||||
|
||||
TestOpensOpenedParent* a = new TestOpensOpenedParent(transport);
|
||||
gParentThread->message_loop()->PostTask(
|
||||
NewRunnableFunction(OpenParent, a, transport, otherPid));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
TestOpensParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
// Stops the thread and joins it
|
||||
delete gParentThread;
|
||||
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
passed("ok");
|
||||
QuitParent();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestOpensOpenedParent::RecvHello()
|
||||
{
|
||||
AssertNotMainThread();
|
||||
if (!SendHi()) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestOpensOpenedParent::RecvHelloSync()
|
||||
{
|
||||
AssertNotMainThread();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestOpensOpenedParent::AnswerHelloRpc()
|
||||
{
|
||||
AssertNotMainThread();
|
||||
if (!CallHiRpc()) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
static void
|
||||
ShutdownTestOpensOpenedParent(TestOpensOpenedParent* parent,
|
||||
Transport* transport)
|
||||
{
|
||||
delete parent;
|
||||
}
|
||||
|
||||
void
|
||||
TestOpensOpenedParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
AssertNotMainThread();
|
||||
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
|
||||
// ActorDestroy() is just a callback from IPDL-generated code,
|
||||
// which needs the top-level actor (this) to stay alive a little
|
||||
// longer so other things can be cleaned up.
|
||||
gParentThread->message_loop()->PostTask(
|
||||
NewRunnableFunction(ShutdownTestOpensOpenedParent,
|
||||
this, mTransport));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// child
|
||||
|
||||
static TestOpensChild* gOpensChild;
|
||||
// Thread on which TestOpensOpenedChild runs
|
||||
static Thread* gChildThread;
|
||||
|
||||
TestOpensChild::TestOpensChild()
|
||||
{
|
||||
gOpensChild = this;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestOpensChild::RecvStart()
|
||||
{
|
||||
if (!PTestOpensOpened::Open(this))
|
||||
fail("opening PTestOpensOpened");
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
static void
|
||||
OpenChild(TestOpensOpenedChild* aChild,
|
||||
Transport* aTransport, base::ProcessId aOtherPid)
|
||||
{
|
||||
AssertNotMainThread();
|
||||
|
||||
// Open the actor on the off-main thread to park it there.
|
||||
// Messages will be delivered to this thread's message loop
|
||||
// instead of the main thread's.
|
||||
if (!aChild->Open(aTransport, aOtherPid,
|
||||
XRE_GetIOMessageLoop(), ipc::ChildSide))
|
||||
fail("opening Child");
|
||||
|
||||
// Kick off the unit tests
|
||||
if (!aChild->SendHello())
|
||||
fail("sending Hello");
|
||||
}
|
||||
|
||||
PTestOpensOpenedChild*
|
||||
TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport,
|
||||
ProcessId otherPid)
|
||||
{
|
||||
gMainThread = MessageLoop::current();
|
||||
|
||||
gChildThread = new Thread("ChildThread");
|
||||
if (!gChildThread->Start())
|
||||
fail("starting child thread");
|
||||
|
||||
TestOpensOpenedChild* a = new TestOpensOpenedChild(transport);
|
||||
gChildThread->message_loop()->PostTask(
|
||||
NewRunnableFunction(OpenChild, a, transport, otherPid));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
TestOpensChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
// Stops the thread and joins it
|
||||
delete gChildThread;
|
||||
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
QuitChild();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestOpensOpenedChild::RecvHi()
|
||||
{
|
||||
AssertNotMainThread();
|
||||
|
||||
if (!SendHelloSync())
|
||||
fail("sending HelloSync");
|
||||
if (!CallHelloRpc())
|
||||
fail("calling HelloRpc");
|
||||
if (!mGotHi)
|
||||
fail("didn't answer HiRpc");
|
||||
|
||||
// Need to close the channel without message-processing frames on
|
||||
// the C++ stack
|
||||
MessageLoop::current()->PostTask(
|
||||
NewNonOwningRunnableMethod(this, &TestOpensOpenedChild::Close));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TestOpensOpenedChild::AnswerHiRpc()
|
||||
{
|
||||
AssertNotMainThread();
|
||||
|
||||
mGotHi = true; // d00d
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
static void
|
||||
ShutdownTestOpensOpenedChild(TestOpensOpenedChild* child,
|
||||
Transport* transport)
|
||||
{
|
||||
delete child;
|
||||
|
||||
// Kick off main-thread shutdown.
|
||||
gMainThread->PostTask(
|
||||
NewNonOwningRunnableMethod(gOpensChild, &TestOpensChild::Close));
|
||||
}
|
||||
|
||||
void
|
||||
TestOpensOpenedChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
AssertNotMainThread();
|
||||
|
||||
if (NormalShutdown != why)
|
||||
fail("unexpected destruction!");
|
||||
|
||||
// ActorDestroy() is just a callback from IPDL-generated code,
|
||||
// which needs the top-level actor (this) to stay alive a little
|
||||
// longer so other things can be cleaned up. Defer shutdown to
|
||||
// let cleanup finish.
|
||||
gChildThread->message_loop()->PostTask(
|
||||
NewRunnableFunction(ShutdownTestOpensOpenedChild,
|
||||
this, mTransport));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,107 +0,0 @@
|
|||
#ifndef mozilla__ipdltest_TestOpens_h
|
||||
#define mozilla__ipdltest_TestOpens_h 1
|
||||
|
||||
#include "mozilla/_ipdltest/IPDLUnitTests.h"
|
||||
|
||||
#include "mozilla/_ipdltest/PTestOpensParent.h"
|
||||
#include "mozilla/_ipdltest/PTestOpensChild.h"
|
||||
|
||||
#include "mozilla/_ipdltest2/PTestOpensOpenedParent.h"
|
||||
#include "mozilla/_ipdltest2/PTestOpensOpenedChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// parent process
|
||||
|
||||
namespace _ipdltest {
|
||||
|
||||
class TestOpensParent : public PTestOpensParent
|
||||
{
|
||||
public:
|
||||
TestOpensParent() {}
|
||||
virtual ~TestOpensParent() {}
|
||||
|
||||
static bool RunTestInProcesses() { return true; }
|
||||
static bool RunTestInThreads() { return false; }
|
||||
|
||||
void Main();
|
||||
|
||||
protected:
|
||||
virtual PTestOpensOpenedParent*
|
||||
AllocPTestOpensOpenedParent(Transport* transport, ProcessId otherProcess) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
||||
namespace _ipdltest2 {
|
||||
|
||||
class TestOpensOpenedParent : public PTestOpensOpenedParent
|
||||
{
|
||||
public:
|
||||
explicit TestOpensOpenedParent(Transport* aTransport)
|
||||
: mTransport(aTransport)
|
||||
{}
|
||||
virtual ~TestOpensOpenedParent() {}
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::IPCResult RecvHello() override;
|
||||
virtual mozilla::ipc::IPCResult RecvHelloSync() override;
|
||||
virtual mozilla::ipc::IPCResult AnswerHelloRpc() override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
Transport* mTransport;
|
||||
};
|
||||
|
||||
} // namespace _ipdltest2
|
||||
|
||||
// child process
|
||||
|
||||
namespace _ipdltest {
|
||||
|
||||
class TestOpensChild : public PTestOpensChild
|
||||
{
|
||||
public:
|
||||
TestOpensChild();
|
||||
virtual ~TestOpensChild() {}
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::IPCResult RecvStart() override;
|
||||
|
||||
virtual PTestOpensOpenedChild*
|
||||
AllocPTestOpensOpenedChild(Transport* transport, ProcessId otherProcess) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
||||
namespace _ipdltest2 {
|
||||
|
||||
class TestOpensOpenedChild : public PTestOpensOpenedChild
|
||||
{
|
||||
public:
|
||||
explicit TestOpensOpenedChild(Transport* aTransport)
|
||||
: mGotHi(false)
|
||||
, mTransport(aTransport)
|
||||
{}
|
||||
virtual ~TestOpensOpenedChild() {}
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::IPCResult RecvHi() override;
|
||||
virtual mozilla::ipc::IPCResult AnswerHiRpc() override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
bool mGotHi;
|
||||
Transport* mTransport;
|
||||
};
|
||||
|
||||
} // namespace _ipdltest2
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif // ifndef mozilla__ipdltest_TestOpens_h
|
|
@ -16,7 +16,6 @@ EXPORTS.mozilla._ipdltest += [
|
|||
SOURCES += [
|
||||
'TestActorPunning.cpp',
|
||||
'TestBadActor.cpp',
|
||||
'TestBridgeMain.cpp',
|
||||
'TestCancel.cpp',
|
||||
'TestCrashCleanup.cpp',
|
||||
'TestDataStructures.cpp',
|
||||
|
@ -35,7 +34,6 @@ SOURCES += [
|
|||
'TestManyChildAllocs.cpp',
|
||||
'TestMultiMgrs.cpp',
|
||||
'TestNestedLoops.cpp',
|
||||
'TestOpens.cpp',
|
||||
'TestRaceDeadlock.cpp',
|
||||
'TestRaceDeferral.cpp',
|
||||
'TestRacyInterruptReplies.cpp',
|
||||
|
@ -66,9 +64,6 @@ IPDL_SOURCES += [
|
|||
'PTestActorPunningSub.ipdl',
|
||||
'PTestBadActor.ipdl',
|
||||
'PTestBadActorSub.ipdl',
|
||||
'PTestBridgeMain.ipdl',
|
||||
'PTestBridgeMainSub.ipdl',
|
||||
'PTestBridgeSub.ipdl',
|
||||
'PTestCancel.ipdl',
|
||||
'PTestCrashCleanup.ipdl',
|
||||
'PTestDataStructures.ipdl',
|
||||
|
@ -105,8 +100,6 @@ IPDL_SOURCES += [
|
|||
'PTestMultiMgrsLeft.ipdl',
|
||||
'PTestMultiMgrsRight.ipdl',
|
||||
'PTestNestedLoops.ipdl',
|
||||
'PTestOpens.ipdl',
|
||||
'PTestOpensOpened.ipdl',
|
||||
'PTestPriority.ipdl',
|
||||
'PTestRaceDeadlock.ipdl',
|
||||
'PTestRaceDeferral.ipdl',
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
include protocol content;
|
||||
|
||||
sync protocol compositor {
|
||||
bridges compositor, content;
|
||||
|
||||
child:
|
||||
async __delete__();
|
||||
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
include protocol compositor;
|
||||
include protocol jetpack;
|
||||
include protocol media;
|
||||
include protocol plugin;
|
||||
|
||||
sync protocol content {
|
||||
parent spawns compositor as parent;
|
||||
parent spawns jetpack;
|
||||
child spawns plugin;
|
||||
child opens media;
|
||||
|
||||
child:
|
||||
async __delete__();
|
||||
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
include protocol content;
|
||||
include protocol jetpack;
|
||||
|
||||
intr protocol jetpackContent {
|
||||
bridges jetpack, content;
|
||||
|
||||
child:
|
||||
async __delete__();
|
||||
|
||||
};
|
|
@ -26,7 +26,7 @@ set_config('MOZ_SYSTEM_FFI', depends_if(system_ffi)(lambda _: True))
|
|||
add_old_configure_assignment('MOZ_SYSTEM_FFI', depends_if(system_ffi)(lambda _: True))
|
||||
|
||||
# Target selection, based on ffi/configure.ac.
|
||||
@depends_when(target, when=building_ffi)
|
||||
@depends(target, when=building_ffi)
|
||||
def ffi_target(target):
|
||||
if target.cpu not in ('x86', 'x86_64', 'arm', 'aarch64'):
|
||||
die('Building libffi from the tree is not supported on this platform. '
|
||||
|
|
|
@ -31,11 +31,11 @@
|
|||
|
||||
#ifndef JIT_SPEW_DIR
|
||||
# if defined(_WIN32)
|
||||
# define JIT_SPEW_DIR ""
|
||||
# define JIT_SPEW_DIR "."
|
||||
# elif defined(__ANDROID__)
|
||||
# define JIT_SPEW_DIR "/data/local/tmp/"
|
||||
# define JIT_SPEW_DIR "/data/local/tmp"
|
||||
# else
|
||||
# define JIT_SPEW_DIR "/tmp/"
|
||||
# define JIT_SPEW_DIR "/tmp"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Test dynamically-appended animation in a subtree that dynamically became 'display:none'</title>
|
||||
</head>
|
||||
<body style="background-color: lime;">
|
||||
<div id="target" style="display: none;">
|
||||
<svg>
|
||||
<rect width="100" height="100" fill="brown" id="rect">
|
||||
</rect>
|
||||
</svg>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('MozReftestInvalidate', function() {
|
||||
var svg = document.getElementsByTagName("svg")[0];
|
||||
svg.pauseAnimations(); // pause svg animation.
|
||||
|
||||
var target = document.getElementById("target");
|
||||
var rect = document.getElementById("rect");
|
||||
var animate = document.createElementNS('http://www.w3.org/2000/svg',
|
||||
'animate');
|
||||
animate.setAttribute('attributeName', 'fill');
|
||||
animate.setAttribute('from', 'blue');
|
||||
animate.setAttribute('to', 'red');
|
||||
animate.setAttribute('dur', '100s');
|
||||
rect.appendChild(animate);
|
||||
|
||||
requestAnimationFrame(function(time) {
|
||||
target.style.display = 'block';
|
||||
requestAnimationFrame(function(time) {
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Test animation in a subtree that dynamically becames 'display:none'</title>
|
||||
</head>
|
||||
<body style="background-color: lime;">
|
||||
<div id="target">
|
||||
<svg>
|
||||
<rect width="100%" height="100%" fill="blue">
|
||||
<animate attributeName="fill" from="brown" to="red" dur="100s"/>
|
||||
</rect>
|
||||
</svg>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('MozReftestInvalidate', function() {
|
||||
var target = document.getElementById("target");
|
||||
target.style.display = "none";
|
||||
|
||||
requestAnimationFrame(function(time) {
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Test dynamically-appended animation on an element that dynamically becomes 'display:none'</title>
|
||||
</head>
|
||||
<body style="background-color: lime;">
|
||||
<div>
|
||||
<svg>
|
||||
<rect width="100" height="100" fill="brown" id="rect">
|
||||
</rect>
|
||||
</svg>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('MozReftestInvalidate', function() {
|
||||
var rect = document.getElementById("rect");
|
||||
var animate = document.createElementNS('http://www.w3.org/2000/svg',
|
||||
'animate');
|
||||
animate.setAttribute('attributeName', 'fill');
|
||||
animate.setAttribute('from', 'blue');
|
||||
animate.setAttribute('to', 'red');
|
||||
animate.setAttribute('dur', '100s');
|
||||
rect.appendChild(animate);
|
||||
|
||||
requestAnimationFrame(function(time) {
|
||||
rect.style.display = 'none';
|
||||
requestAnimationFrame(function(time) {
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Test animation on an element that dynamically becomes 'display:none'</title>
|
||||
</head>
|
||||
<body style="background-color: lime;">
|
||||
<div>
|
||||
<svg>
|
||||
<rect width="100%" height="100%" fill="blue" id="rect">
|
||||
<animate attributeName="fill" from="brown" to="red" dur="100s"/>
|
||||
</rect>
|
||||
</svg>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('MozReftestInvalidate', function() {
|
||||
var rect = document.getElementById("rect");
|
||||
rect.style.display = "none";
|
||||
|
||||
requestAnimationFrame(function(time) {
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Testcase reference file for animated pass condition(HTML)</title>
|
||||
</head>
|
||||
<body style="background-color: lime;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="100" height="100" fill="blue"/>
|
||||
</svg>
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Testcase reference file for generic pass condition(HTML)</title>
|
||||
</head>
|
||||
<body style="background-color: lime;">
|
||||
</body>
|
||||
</html>
|
|
@ -280,4 +280,10 @@ fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,2) == anim-gradient-attr-presence-0
|
|||
== anim-display.svg lime.svg
|
||||
== anim-display-in-g-element.svg lime.svg
|
||||
|
||||
# Test animation that change 'display' style value to 'none'
|
||||
== anim-change-display-none-for-ancestor-elem.html lime.html
|
||||
== anim-change-display-none-for-target-elem.html lime.html
|
||||
== anim-change-display-none-for-dynamically-appended-elem.html lime.html
|
||||
== anim-change-display-block-for-dynamically-appended-elem.html anim-standard-ref.html
|
||||
|
||||
pref(layout.css.clip-path-shapes.enabled,true) fuzzy(63,146) == anim-clipPath-viewBox.svg anim-clipPath-viewBox-ref.svg
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include "PreloadedStyleSheet.h"
|
||||
|
||||
#include "mozilla/css/Loader.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
PreloadedStyleSheet::PreloadedStyleSheet(nsIURI* aURI,
|
||||
|
|
|
@ -11,8 +11,14 @@
|
|||
|
||||
#include "mozilla/css/SheetParsingMode.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/StyleBackendType.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIPreloadedStyleSheet.h"
|
||||
|
||||
class nsIURI;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PreloadedStyleSheet : public nsIPreloadedStyleSheet
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "nsCSSPseudoElements.h"
|
||||
#include "mozilla/StyleSetHandle.h"
|
||||
#include "mozilla/StyleSetHandleInlines.h"
|
||||
#include "mozilla/RestyleManager.h"
|
||||
#include "imgIRequest.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsCSSKeywords.h"
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#include "mozilla/dom/MediaListBinding.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
#include "nsCSSParser.h"
|
||||
#include "nsCSSRules.h"
|
||||
#include "nsMediaFeatures.h"
|
||||
#include "nsRuleNode.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
template <class Numeric>
|
||||
int32_t DoCompare(Numeric a, Numeric b)
|
||||
|
@ -512,7 +517,7 @@ nsMediaList::~nsMediaList()
|
|||
/* virtual */ JSObject*
|
||||
nsMediaList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return MediaListBinding::Wrap(aCx, this, aGivenProto);
|
||||
return dom::MediaListBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -389,6 +389,20 @@ this.ExtensionData = class {
|
|||
return result;
|
||||
}
|
||||
|
||||
// Compute the difference between two sets of permissions, suitable
|
||||
// for presenting to the user.
|
||||
static comparePermissions(oldPermissions, newPermissions) {
|
||||
// See bug 1331769: should we do something more complicated to
|
||||
// compare host permissions?
|
||||
// e.g., if we go from <all_urls> to a specific host or from
|
||||
// a *.domain.com to specific-host.domain.com that's actually a
|
||||
// drop in permissions but the simple test below will cause a prompt.
|
||||
return {
|
||||
hosts: newPermissions.hosts.filter(perm => !oldPermissions.hosts.includes(perm)),
|
||||
permissions: newPermissions.permissions.filter(perm => !oldPermissions.permissions.includes(perm)),
|
||||
};
|
||||
}
|
||||
|
||||
// Reads the extension's |manifest.json| file, and stores its
|
||||
// parsed contents in |this.manifest|.
|
||||
readManifest() {
|
||||
|
|
|
@ -7,6 +7,7 @@ description = "Testing code for libgkrust"
|
|||
|
||||
[features]
|
||||
bindgen = ["gkrust-shared/bindgen"]
|
||||
servo = ["gkrust-shared/servo"]
|
||||
|
||||
[dependencies]
|
||||
mp4parse-gtest = { path = "../../../../dom/media/gtest" }
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
features = []
|
||||
if CONFIG['MOZ_STYLO'] and CONFIG['MOZ_STYLO_BINDGEN']:
|
||||
features += ['bindgen']
|
||||
if CONFIG['MOZ_STYLO']:
|
||||
features += ['servo']
|
||||
|
||||
if CONFIG['MOZ_STYLO_BINDGEN']:
|
||||
features += ['bindgen']
|
||||
|
||||
RustLibrary('gkrust-gtest', features)
|
||||
|
|
|
@ -7,6 +7,7 @@ description = "Rust code for libxul"
|
|||
|
||||
[features]
|
||||
bindgen = ["gkrust-shared/bindgen"]
|
||||
servo = ["gkrust-shared/servo"]
|
||||
|
||||
[dependencies]
|
||||
gkrust-shared = { path = "shared" }
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
features = []
|
||||
if CONFIG['MOZ_STYLO'] and CONFIG['MOZ_STYLO_BINDGEN']:
|
||||
features += ['bindgen']
|
||||
if CONFIG['MOZ_STYLO']:
|
||||
features += ['servo']
|
||||
|
||||
if CONFIG['MOZ_STYLO_BINDGEN']:
|
||||
features += ['bindgen']
|
||||
|
||||
RustLibrary('gkrust', features)
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MPL-2.0"
|
|||
description = "Shared Rust code for libxul"
|
||||
|
||||
[dependencies]
|
||||
geckoservo = { path = "../../../../servo/ports/geckolib" }
|
||||
geckoservo = { path = "../../../../servo/ports/geckolib", optional = true }
|
||||
mp4parse_capi = { path = "../../../../media/libstagefright/binding/mp4parse_capi" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
rust_url_capi = { path = "../../../../netwerk/base/rust-url-capi" }
|
||||
|
@ -14,6 +14,7 @@ rust_url_capi = { path = "../../../../netwerk/base/rust-url-capi" }
|
|||
[features]
|
||||
default = []
|
||||
bindgen = ["geckoservo/bindgen"]
|
||||
servo = ["geckoservo"]
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
// 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/.
|
||||
|
||||
#[cfg(feature="servo")]
|
||||
extern crate geckoservo;
|
||||
|
||||
extern crate mp4parse_capi;
|
||||
extern crate nsstring;
|
||||
extern crate rust_url_capi;
|
||||
|
|
|
@ -1380,15 +1380,7 @@ var AddonManagerInternal = {
|
|||
let oldPerms = info.existingAddon.userPermissions || {hosts: [], permissions: []};
|
||||
let newPerms = info.addon.userPermissions;
|
||||
|
||||
// See bug 1331769: should we do something more complicated to
|
||||
// compare host permissions?
|
||||
// e.g., if we go from <all_urls> to a specific host or from
|
||||
// a *.domain.com to specific-host.domain.com that's actually a
|
||||
// drop in permissions but the simple test below will cause a prompt.
|
||||
let difference = {
|
||||
hosts: newPerms.hosts.filter(perm => !oldPerms.hosts.includes(perm)),
|
||||
permissions: newPerms.permissions.filter(perm => !oldPerms.permissions.includes(perm)),
|
||||
};
|
||||
let difference = Extension.comparePermissions(oldPerms, newPerms);
|
||||
|
||||
// If there are no new permissions, just go ahead with the update
|
||||
if (difference.hosts.length == 0 && difference.permissions.length == 0) {
|
||||
|
@ -2110,7 +2102,9 @@ var AddonManagerInternal = {
|
|||
install.addon.userDisabled = false;
|
||||
}
|
||||
|
||||
if (WEBEXT_PERMISSION_PROMPTS) {
|
||||
let needsRestart = (install.addon.pendingOperations != AddonManager.PENDING_NONE);
|
||||
|
||||
if (WEBEXT_PERMISSION_PROMPTS && !needsRestart) {
|
||||
let subject = {wrappedJSObject: {target: browser, addon: install.addon}};
|
||||
Services.obs.notifyObservers(subject, "webextension-install-notify", null);
|
||||
} else {
|
||||
|
@ -2847,24 +2841,12 @@ var AddonManagerInternal = {
|
|||
// "addon-install-confirmation" notification. If the application
|
||||
// does not implement its own prompt, use the built-in xul dialog.
|
||||
if (info.addon.userPermissions && WEBEXT_PERMISSION_PROMPTS) {
|
||||
const observer = {
|
||||
observe(subject, topic, data) {
|
||||
if (topic == "webextension-permission-response"
|
||||
&& subject.wrappedJSObject.info.addon == info.addon) {
|
||||
let answer = JSON.parse(data);
|
||||
Services.obs.removeObserver(observer, "webextension-permission-response");
|
||||
if (answer) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
let subject = {
|
||||
wrappedJSObject: {
|
||||
target: browser,
|
||||
info: Object.assign({resolve, reject}, info),
|
||||
}
|
||||
};
|
||||
|
||||
Services.obs.addObserver(observer, "webextension-permission-response", false);
|
||||
|
||||
let subject = {wrappedJSObject: {target: browser, info}};
|
||||
Services.obs.notifyObservers(subject, "webextension-permission-prompt", null);
|
||||
} else if (requireConfirm) {
|
||||
// The methods below all want to call the install() or cancel()
|
||||
|
|
|
@ -21,6 +21,8 @@ Cu.import("resource://gre/modules/AddonManager.jsm");
|
|||
Cu.import("resource://gre/modules/addons/AddonRepository.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils", "resource:///modules/E10SUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
|
||||
"resource://gre/modules/Extension.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
|
||||
"resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
|
@ -38,6 +40,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
|
||||
"resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
|
||||
"extensions.webextPermissionPrompts", false);
|
||||
|
||||
const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
|
||||
const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
|
||||
const PREF_XPI_ENABLED = "xpinstall.enabled";
|
||||
|
@ -694,6 +699,40 @@ var gEventManager = {
|
|||
}
|
||||
};
|
||||
|
||||
function attachUpdateHandler(install) {
|
||||
if (!WEBEXT_PERMISSION_PROMPTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
install.promptHandler = (info) => {
|
||||
let oldPerms = info.existingAddon.userPermissions || {hosts: [], permissions: []};
|
||||
let newPerms = info.addon.userPermissions;
|
||||
|
||||
let difference = Extension.comparePermissions(oldPerms, newPerms);
|
||||
|
||||
// If there are no new permissions, just proceed
|
||||
if (difference.hosts.length == 0 && difference.permissions.length == 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let subject = {
|
||||
wrappedJSObject: {
|
||||
target: getBrowserElement(),
|
||||
info: {
|
||||
type: "update",
|
||||
addon: info.addon,
|
||||
icon: info.addon.icon,
|
||||
permissions: difference,
|
||||
resolve,
|
||||
reject,
|
||||
},
|
||||
},
|
||||
};
|
||||
Services.obs.notifyObservers(subject, "webextension-permission-prompt", null);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var gViewController = {
|
||||
viewPort: null,
|
||||
|
@ -1081,6 +1120,10 @@ var gViewController = {
|
|||
pendingChecks--;
|
||||
updateStatus();
|
||||
},
|
||||
onInstallCancelled() {
|
||||
pendingChecks--;
|
||||
updateStatus();
|
||||
},
|
||||
onInstallFailed() {
|
||||
pendingChecks--;
|
||||
updateStatus();
|
||||
|
@ -1098,6 +1141,7 @@ var gViewController = {
|
|||
onUpdateAvailable(aAddon, aInstall) {
|
||||
gEventManager.delegateAddonEvent("onUpdateAvailable",
|
||||
[aAddon, aInstall]);
|
||||
attachUpdateHandler(aInstall);
|
||||
if (AddonManager.shouldAutoUpdate(aAddon)) {
|
||||
aInstall.addListener(updateInstallListener);
|
||||
aInstall.install();
|
||||
|
@ -1143,6 +1187,7 @@ var gViewController = {
|
|||
onUpdateAvailable(aAddon, aInstall) {
|
||||
gEventManager.delegateAddonEvent("onUpdateAvailable",
|
||||
[aAddon, aInstall]);
|
||||
attachUpdateHandler(aInstall);
|
||||
if (AddonManager.shouldAutoUpdate(aAddon))
|
||||
aInstall.install();
|
||||
},
|
||||
|
|
|
@ -2787,6 +2787,22 @@ NativeKey::MayBeSameCharMessage(const MSG& aCharMsg1,
|
|||
(aCharMsg1.lParam & ~kScanCodeMask) == (aCharMsg2.lParam & ~kScanCodeMask);
|
||||
}
|
||||
|
||||
bool
|
||||
NativeKey::IsSamePhysicalKeyMessage(const MSG& aKeyOrCharMsg1,
|
||||
const MSG& aKeyOrCharMsg2) const
|
||||
{
|
||||
if (NS_WARN_IF(aKeyOrCharMsg1.message < WM_KEYFIRST) ||
|
||||
NS_WARN_IF(aKeyOrCharMsg1.message > WM_KEYLAST) ||
|
||||
NS_WARN_IF(aKeyOrCharMsg2.message < WM_KEYFIRST) ||
|
||||
NS_WARN_IF(aKeyOrCharMsg2.message > WM_KEYLAST)) {
|
||||
return false;
|
||||
}
|
||||
return WinUtils::GetScanCode(aKeyOrCharMsg1.lParam) ==
|
||||
WinUtils::GetScanCode(aKeyOrCharMsg2.lParam) &&
|
||||
WinUtils::IsExtendedScanCode(aKeyOrCharMsg1.lParam) ==
|
||||
WinUtils::IsExtendedScanCode(aKeyOrCharMsg2.lParam);
|
||||
}
|
||||
|
||||
bool
|
||||
NativeKey::GetFollowingCharMessage(MSG& aCharMsg)
|
||||
{
|
||||
|
@ -2836,7 +2852,7 @@ NativeKey::GetFollowingCharMessage(MSG& aCharMsg)
|
|||
// On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
|
||||
// the message range. So, if it returns WM_NULL, we should retry to get
|
||||
// the following char message it was found above.
|
||||
for (uint32_t i = 0; i < 5; i++) {
|
||||
for (uint32_t i = 0; i < 50; i++) {
|
||||
MSG removedMsg, nextKeyMsgInAllWindows;
|
||||
bool doCrash = false;
|
||||
if (!WinUtils::PeekMessage(&removedMsg, mMsg.hwnd,
|
||||
|
@ -2977,13 +2993,52 @@ NativeKey::GetFollowingCharMessage(MSG& aCharMsg)
|
|||
MOZ_CRASH("We lost the following char message");
|
||||
}
|
||||
|
||||
// Retry for the strange case.
|
||||
// We're still not sure why ::PeekMessage() may return WM_NULL even with
|
||||
// its first message and its last message are same message. However,
|
||||
// at developing Metrofox, we met this case even with usual keyboard
|
||||
// layouts. So, it might be possible in desktop application or it really
|
||||
// occurs with some odd keyboard layouts which perhaps hook API.
|
||||
if (removedMsg.message == WM_NULL) {
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Warning,
|
||||
("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
|
||||
"remove a char message, instead, removed WM_NULL message, ",
|
||||
"removedMsg=%s",
|
||||
this, ToString(removedMsg).get()));
|
||||
// Check if there is the message which we're trying to remove.
|
||||
MSG newNextKeyMsg;
|
||||
if (!WinUtils::PeekMessage(&newNextKeyMsg, mMsg.hwnd,
|
||||
WM_KEYFIRST, WM_KEYLAST,
|
||||
PM_NOREMOVE | PM_NOYIELD)) {
|
||||
// If there is no key message, we should mark this keydown as consumed
|
||||
// because the key operation may have already been handled or canceled.
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Warning,
|
||||
("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
|
||||
"remove a char message because it's gone during removing it from "
|
||||
"the queue, nextKeyMsg=%s",
|
||||
this, ToString(nextKeyMsg).get()));
|
||||
MOZ_ASSERT(!mCharMessageHasGone);
|
||||
mFollowingCharMsgs.Clear();
|
||||
mCharMessageHasGone = true;
|
||||
return false;
|
||||
}
|
||||
if (!IsCharMessage(newNextKeyMsg)) {
|
||||
// If next key message becomes a non-char message, we should mark this
|
||||
// keydown as consumed because the key operation may have already been
|
||||
// handled or canceled.
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Warning,
|
||||
("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
|
||||
"remove a char message because it's gone during removing it from "
|
||||
"the queue, nextKeyMsg=%s, newNextKeyMsg=%s",
|
||||
this, ToString(nextKeyMsg).get(), ToString(newNextKeyMsg).get()));
|
||||
MOZ_ASSERT(!mCharMessageHasGone);
|
||||
mFollowingCharMsgs.Clear();
|
||||
mCharMessageHasGone = true;
|
||||
return false;
|
||||
}
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Debug,
|
||||
("%p NativeKey::GetFollowingCharMessage(), there is the message "
|
||||
"which is being tried to be removed from the queue, trying again...",
|
||||
this));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2999,64 +3054,80 @@ NativeKey::GetFollowingCharMessage(MSG& aCharMsg)
|
|||
return false;
|
||||
}
|
||||
|
||||
// This is normal case.
|
||||
if (MayBeSameCharMessage(removedMsg, nextKeyMsg)) {
|
||||
aCharMsg = removedMsg;
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Verbose,
|
||||
("%p NativeKey::GetFollowingCharMessage(), succeeded to retrieve "
|
||||
"next char message, aCharMsg=%s",
|
||||
this, ToString(aCharMsg).get()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Even if removed message is different char message from the found char
|
||||
// message, when the scan code is same, we can assume that the message
|
||||
// is overwritten by somebody who hooks API. See bug 1336028 comment 0 for
|
||||
// the possible scenarios.
|
||||
if (IsCharMessage(removedMsg) &&
|
||||
IsSamePhysicalKeyMessage(removedMsg, nextKeyMsg)) {
|
||||
aCharMsg = removedMsg;
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Warning,
|
||||
("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
|
||||
"remove a char message, but the removed message was changed from "
|
||||
"the found message except their scancode, aCharMsg=%s, "
|
||||
"nextKeyMsg(the found message)=%s",
|
||||
this, ToString(aCharMsg).get(), ToString(nextKeyMsg).get()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: Although, we don't know when this case occurs, the scan code value
|
||||
// in lParam may be changed from 0 to something. The changed value
|
||||
// is different from the scan code of handling keydown message.
|
||||
if (!MayBeSameCharMessage(removedMsg, nextKeyMsg)) {
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Error,
|
||||
("%p NativeKey::GetFollowingCharMessage(), FAILED, removed message "
|
||||
"is really different from what we have already found, removedMsg=%s, "
|
||||
"nextKeyMsg=%s",
|
||||
this, ToString(removedMsg).get(), ToString(nextKeyMsg).get()));
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Error,
|
||||
("%p NativeKey::GetFollowingCharMessage(), FAILED, removed message "
|
||||
"is really different from what we have already found, removedMsg=%s, "
|
||||
"nextKeyMsg=%s",
|
||||
this, ToString(removedMsg).get(), ToString(nextKeyMsg).get()));
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
nsPrintfCString info("\nPeekMessage() removed unexpcted char message! "
|
||||
"\nActive keyboard layout=0x%08X (%s), "
|
||||
"\nHandling message: %s, InSendMessageEx()=%s, "
|
||||
"\nFound message: %s, "
|
||||
"\nRemoved message: %s, ",
|
||||
KeyboardLayout::GetActiveLayout(),
|
||||
KeyboardLayout::GetActiveLayoutName().get(),
|
||||
ToString(mMsg).get(),
|
||||
GetResultOfInSendMessageEx().get(),
|
||||
ToString(nextKeyMsg).get(),
|
||||
ToString(removedMsg).get());
|
||||
nsPrintfCString info("\nPeekMessage() removed unexpcted char message! "
|
||||
"\nActive keyboard layout=0x%08X (%s), "
|
||||
"\nHandling message: %s, InSendMessageEx()=%s, "
|
||||
"\nFound message: %s, "
|
||||
"\nRemoved message: %s, ",
|
||||
KeyboardLayout::GetActiveLayout(),
|
||||
KeyboardLayout::GetActiveLayoutName().get(),
|
||||
ToString(mMsg).get(),
|
||||
GetResultOfInSendMessageEx().get(),
|
||||
ToString(nextKeyMsg).get(),
|
||||
ToString(removedMsg).get());
|
||||
CrashReporter::AppendAppNotesToCrashReport(info);
|
||||
// What's the next key message?
|
||||
MSG nextKeyMsgAfter;
|
||||
if (WinUtils::PeekMessage(&nextKeyMsgAfter, mMsg.hwnd,
|
||||
WM_KEYFIRST, WM_KEYLAST,
|
||||
PM_NOREMOVE | PM_NOYIELD)) {
|
||||
nsPrintfCString info("\nNext key message after unexpected char message "
|
||||
"removed: %s, ",
|
||||
ToString(nextKeyMsgAfter).get());
|
||||
CrashReporter::AppendAppNotesToCrashReport(info);
|
||||
// What's the next key message?
|
||||
MSG nextKeyMsgAfter;
|
||||
if (WinUtils::PeekMessage(&nextKeyMsgAfter, mMsg.hwnd,
|
||||
WM_KEYFIRST, WM_KEYLAST,
|
||||
PM_NOREMOVE | PM_NOYIELD)) {
|
||||
nsPrintfCString info("\nNext key message after unexpected char message "
|
||||
"removed: %s, ",
|
||||
ToString(nextKeyMsgAfter).get());
|
||||
CrashReporter::AppendAppNotesToCrashReport(info);
|
||||
} else {
|
||||
CrashReporter::AppendAppNotesToCrashReport(
|
||||
NS_LITERAL_CSTRING("\nThere is no key message after unexpected char "
|
||||
"message removed, "));
|
||||
}
|
||||
// Another window has a key message?
|
||||
MSG nextKeyMsgInAllWindows;
|
||||
if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0,
|
||||
WM_KEYFIRST, WM_KEYLAST,
|
||||
PM_NOREMOVE | PM_NOYIELD)) {
|
||||
nsPrintfCString info("\nNext key message in all windows: %s.",
|
||||
ToString(nextKeyMsgInAllWindows).get());
|
||||
CrashReporter::AppendAppNotesToCrashReport(info);
|
||||
} else {
|
||||
CrashReporter::AppendAppNotesToCrashReport(
|
||||
NS_LITERAL_CSTRING("\nThere is no key message in any windows."));
|
||||
}
|
||||
#endif // #ifdef MOZ_CRASHREPORTER
|
||||
MOZ_CRASH("PeekMessage() removed unexpected message");
|
||||
} else {
|
||||
CrashReporter::AppendAppNotesToCrashReport(
|
||||
NS_LITERAL_CSTRING("\nThere is no key message after unexpected char "
|
||||
"message removed, "));
|
||||
}
|
||||
|
||||
aCharMsg = removedMsg;
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Verbose,
|
||||
("%p NativeKey::GetFollowingCharMessage(), succeeded to retrieve next "
|
||||
"char message, aCharMsg=%s",
|
||||
this, ToString(aCharMsg).get()));
|
||||
return true;
|
||||
// Another window has a key message?
|
||||
if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0,
|
||||
WM_KEYFIRST, WM_KEYLAST,
|
||||
PM_NOREMOVE | PM_NOYIELD)) {
|
||||
nsPrintfCString info("\nNext key message in all windows: %s.",
|
||||
ToString(nextKeyMsgInAllWindows).get());
|
||||
CrashReporter::AppendAppNotesToCrashReport(info);
|
||||
} else {
|
||||
CrashReporter::AppendAppNotesToCrashReport(
|
||||
NS_LITERAL_CSTRING("\nThere is no key message in any windows."));
|
||||
}
|
||||
#endif // #ifdef MOZ_CRASHREPORTER
|
||||
MOZ_CRASH("PeekMessage() removed unexpected message");
|
||||
}
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Error,
|
||||
("%p NativeKey::GetFollowingCharMessage(), FAILED, removed messages "
|
||||
|
|
|
@ -513,6 +513,8 @@ private:
|
|||
return (aMessage == WM_SYSCHAR || aMessage == WM_SYSDEADCHAR);
|
||||
}
|
||||
bool MayBeSameCharMessage(const MSG& aCharMsg1, const MSG& aCharMsg2) const;
|
||||
bool IsSamePhysicalKeyMessage(const MSG& aKeyOrCharMsg1,
|
||||
const MSG& aKeyOrCharMsg2) const;
|
||||
bool IsFollowedByPrintableCharMessage() const;
|
||||
bool IsFollowedByPrintableCharOrSysCharMessage() const;
|
||||
bool IsFollowedByDeadCharMessage() const;
|
||||
|
|
Загрузка…
Ссылка в новой задаче