mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
Merge mozilla-central to inbound
This commit is contained in:
commit
1ef71e5fc2
2
.flake8
2
.flake8
@ -45,7 +45,7 @@ exclude =
|
||||
toolkit/content/tests/chrome/file_about_networking_wsh.py,
|
||||
toolkit/crashreporter/tools/symbolstore.py,
|
||||
toolkit/crashreporter/tools/unit-symbolstore.py,
|
||||
toolkit/library/dependentlibs.py,
|
||||
toolkit/library/build/dependentlibs.py,
|
||||
toolkit/locales/generate_update_locale.py,
|
||||
toolkit/mozapps,
|
||||
toolkit/moz.configure,
|
||||
|
@ -3,5 +3,4 @@ support-files = head.js
|
||||
|
||||
[browser_toolbarButtonKeyPress.js]
|
||||
[browser_toolbarKeyNav.js]
|
||||
skip-if = fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateRegistration(unsigned long, mozilla::dom::ServiceWorkerRegistrationData&)
|
||||
support-files = !/browser/base/content/test/permissions/permissions.html
|
||||
|
@ -6,21 +6,16 @@ support-files=
|
||||
[browser_canvas_fingerprinting_resistance.js]
|
||||
skip-if = debug || os == "linux" && asan # Bug 1522069
|
||||
[browser_permissions.js]
|
||||
skip-if = fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
[browser_permissions_event_telemetry.js]
|
||||
skip-if = fission && debug
|
||||
[browser_permissions_postPrompt.js]
|
||||
skip-if = fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
support-files=
|
||||
dummy.js
|
||||
[browser_permissions_handling_user_input.js]
|
||||
skip-if = fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateRegistration(unsigned long, mozilla::dom::ServiceWorkerRegistrationData&)
|
||||
support-files=
|
||||
dummy.js
|
||||
[browser_reservedkey.js]
|
||||
skip-if = fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
[browser_temporary_permissions.js]
|
||||
fail-if = fission
|
||||
fail-if = fission # SecurityError: Permission denied to access property "document" on cross-origin object
|
||||
support-files =
|
||||
temporary_permissions_subframe.html
|
||||
../webrtc/get_user_media.html
|
||||
@ -32,6 +27,5 @@ support-files =
|
||||
../general/audio.ogg
|
||||
skip-if = verify && os == 'linux' && debug # Bug 1483648
|
||||
[browser_temporary_permissions_expiry.js]
|
||||
skip-if = fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
[browser_temporary_permissions_navigation.js]
|
||||
[browser_temporary_permissions_tabs.js]
|
||||
|
@ -1034,6 +1034,19 @@ nsDefaultCommandLineHandler.prototype = {
|
||||
if (win) {
|
||||
win.close();
|
||||
}
|
||||
// If this is a silent run where we do not open any window, we must
|
||||
// notify shutdown so that the quit-application-granted notification
|
||||
// will happen. This is required in the AddonManager to properly
|
||||
// handle shutdown blockers for Telemetry and XPIDatabase.
|
||||
// Some command handlers open a window asynchronously, so lets give
|
||||
// that time and then verify that a window was not opened before
|
||||
// quiting.
|
||||
Services.tm.idleDispatchToMainThread(() => {
|
||||
win = Services.wm.getMostRecentWindow(null);
|
||||
if (!win) {
|
||||
Services.startup.quit(Services.startup.eForceQuit);
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -69,7 +69,8 @@ add_task(async function test() {
|
||||
() =>
|
||||
(notification = gBrowser
|
||||
.getNotificationBox()
|
||||
.getNotificationWithValue("master-password-login-required"))
|
||||
.getNotificationWithValue("master-password-login-required")),
|
||||
"waiting for master-password-login-required notification"
|
||||
);
|
||||
|
||||
ok(
|
||||
|
@ -78,6 +78,10 @@ Preferences.addAll([
|
||||
{ id: "privacy.trackingprotection.fingerprinting.enabled", type: "bool" },
|
||||
{ id: "privacy.trackingprotection.cryptomining.enabled", type: "bool" },
|
||||
|
||||
// Social tracking
|
||||
{ id: "privacy.trackingprotection.socialtracking.enabled", type: "bool" },
|
||||
{ id: "privacy.socialtracking.block_cookies.enabled", type: "bool" },
|
||||
|
||||
// Tracker list
|
||||
{ id: "urlclassifier.trackingTable", type: "string" },
|
||||
|
||||
@ -1057,6 +1061,12 @@ var gPrivacyPane = {
|
||||
trackingProtectionWritePrefs() {
|
||||
let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
|
||||
let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
|
||||
let stpPref = Preferences.get(
|
||||
"privacy.trackingprotection.socialtracking.enabled"
|
||||
);
|
||||
let stpCookiePref = Preferences.get(
|
||||
"privacy.socialtracking.block_cookies.enabled"
|
||||
);
|
||||
let tpMenu = document.getElementById("trackingProtectionMenu");
|
||||
let tpCheckbox = document.getElementById(
|
||||
"contentBlockingTrackingProtectionCheckbox"
|
||||
@ -1077,14 +1087,23 @@ var gPrivacyPane = {
|
||||
case "always":
|
||||
enabledPref.value = true;
|
||||
pbmPref.value = true;
|
||||
if (stpCookiePref.value) {
|
||||
stpPref.value = true;
|
||||
}
|
||||
break;
|
||||
case "private":
|
||||
enabledPref.value = false;
|
||||
pbmPref.value = true;
|
||||
if (stpCookiePref.value) {
|
||||
stpPref.value = false;
|
||||
}
|
||||
break;
|
||||
case "never":
|
||||
enabledPref.value = false;
|
||||
pbmPref.value = false;
|
||||
if (stpCookiePref.value) {
|
||||
stpPref.value = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -23,8 +23,6 @@ from distutils.dir_util import copy_tree
|
||||
|
||||
from shutil import which
|
||||
|
||||
URL_REPO = "https://github.com/llvm/llvm-project"
|
||||
|
||||
|
||||
def symlink(source, link_name):
|
||||
os_symlink = getattr(os, "symlink", None)
|
||||
@ -182,16 +180,16 @@ def install_libgcc(gcc_dir, clang_dir, is_final_stage):
|
||||
"x86_64-unknown-linux-gnu",
|
||||
os.path.basename(libgcc_dir))
|
||||
mkdir_p(clang_lib_dir)
|
||||
copy_tree(libgcc_dir, clang_lib_dir)
|
||||
copy_tree(libgcc_dir, clang_lib_dir, preserve_symlinks=True)
|
||||
libgcc_dir = os.path.join(gcc_dir, "lib64")
|
||||
clang_lib_dir = os.path.join(clang_dir, "lib")
|
||||
copy_tree(libgcc_dir, clang_lib_dir)
|
||||
copy_tree(libgcc_dir, clang_lib_dir, preserve_symlinks=True)
|
||||
libgcc_dir = os.path.join(gcc_dir, "lib32")
|
||||
clang_lib_dir = os.path.join(clang_dir, "lib32")
|
||||
copy_tree(libgcc_dir, clang_lib_dir)
|
||||
copy_tree(libgcc_dir, clang_lib_dir, preserve_symlinks=True)
|
||||
include_dir = os.path.join(gcc_dir, "include")
|
||||
clang_include_dir = os.path.join(clang_dir, "include")
|
||||
copy_tree(include_dir, clang_include_dir)
|
||||
copy_tree(include_dir, clang_include_dir, preserve_symlinks=True)
|
||||
|
||||
|
||||
def install_import_library(build_dir, clang_dir):
|
||||
|
@ -27,6 +27,7 @@ protected:
|
||||
if (Name == "pair" || Name == "atomic" ||
|
||||
// libstdc++ specific names
|
||||
Name == "__atomic_base" || Name == "atomic_bool" ||
|
||||
Name == "__cxx_atomic_impl" || Name == "__cxx_atomic_base_impl" ||
|
||||
Name == "__pair_base" ||
|
||||
// MSVCRT specific names
|
||||
Name == "_Atomic_impl" || Name == "_Atomic_base" ||
|
||||
|
@ -25,7 +25,7 @@ def GeckoBinary(linkage='dependent', mozglue=None):
|
||||
if linkage == 'dependent':
|
||||
USE_LIBS += [
|
||||
'nspr',
|
||||
'xul',
|
||||
'xul-real',
|
||||
]
|
||||
elif linkage == 'standalone':
|
||||
DEFINES['XPCOM_GLUE'] = True
|
||||
|
@ -19,7 +19,7 @@ ifdef SHARED_LIBRARY
|
||||
SHARED_LIBRARY_FILES = $(SHARED_LIBRARY)
|
||||
SHARED_LIBRARY_DEST ?= $(FINAL_TARGET)
|
||||
ifndef SHARED_LIBRARY_TARGET
|
||||
SHARED_LIBRARY_TARGET = target-shared
|
||||
SHARED_LIBRARY_TARGET = target
|
||||
endif
|
||||
INSTALL_TARGETS += SHARED_LIBRARY
|
||||
endif # SHARED_LIBRARY
|
||||
|
@ -73,12 +73,7 @@ CURRENT_DIRS := $($(CURRENT_TIER)_dirs)
|
||||
$(compile_targets) $(syms_targets):
|
||||
$(if $(filter $(RECURSE_BASE_DIR)%,$@),$(call RECURSE,$(@F),$(@D)))
|
||||
|
||||
# Equivalent to a mix of:
|
||||
# $(syms_targets): %/syms: %/target
|
||||
# and
|
||||
# $(syms_targets): %/syms: %/target-shared
|
||||
# for the right syms targets.
|
||||
$(foreach syms_target,$(syms_targets),$(eval $(syms_target): $(filter $(dir $(syms_target))target $(dir $(syms_target))target-shared,$(compile_targets))))
|
||||
$(syms_targets): %/syms: %/target
|
||||
|
||||
# Only hook symbols targets into the main compile graph in automation.
|
||||
ifdef MOZ_AUTOMATION
|
||||
@ -187,14 +182,14 @@ xpcom/xpidl/export: xpcom/idl-parser/xpidl/export
|
||||
dom/bindings/export: layout/style/export
|
||||
|
||||
ifdef ENABLE_CLANG_PLUGIN
|
||||
$(filter-out config/host build/unix/stdc++compat/% build/clang-plugin/%,$(compile_targets)): build/clang-plugin/host build/clang-plugin/tests/target
|
||||
build/clang-plugin/tests/target: build/clang-plugin/host
|
||||
$(filter-out config/host build/unix/stdc++compat/% build/clang-plugin/%,$(compile_targets)): build/clang-plugin/host build/clang-plugin/tests/target-objects
|
||||
build/clang-plugin/tests/target-objects: build/clang-plugin/host
|
||||
endif
|
||||
|
||||
# Interdependencies that moz.build world don't know about yet for compilation.
|
||||
# Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),gtk3)
|
||||
toolkit/library/target-shared: widget/gtk/mozgtk/gtk3/target-shared
|
||||
toolkit/library/target: widget/gtk/mozgtk/gtk3/target
|
||||
endif
|
||||
endif
|
||||
# Most things are built during compile (target/host), but some things happen during export
|
||||
|
@ -412,8 +412,7 @@ compile:: host target
|
||||
|
||||
host:: $(HOST_OBJS) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(HOST_RUST_PROGRAMS) $(HOST_RUST_LIBRARY_FILE) $(HOST_SHARED_LIBRARY)
|
||||
|
||||
target:: $(filter-out $(MOZBUILD_NON_DEFAULT_TARGETS),$(LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(RUST_LIBRARY_FILE) $(RUST_PROGRAMS))
|
||||
target-shared:: $(filter-out $(MOZBUILD_NON_DEFAULT_TARGETS),$(SHARED_LIBRARY))
|
||||
target:: $(filter-out $(MOZBUILD_NON_DEFAULT_TARGETS),$(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(RUST_LIBRARY_FILE) $(RUST_PROGRAMS))
|
||||
|
||||
ifndef LIBRARY
|
||||
ifdef OBJS
|
||||
@ -421,6 +420,9 @@ target:: $(OBJS)
|
||||
endif
|
||||
endif
|
||||
|
||||
target-objects: $(OBJS) $(PROGOBJS)
|
||||
host-objects: $(HOST_OBJS) $(HOST_PROGOBJS)
|
||||
|
||||
syms::
|
||||
|
||||
include $(MOZILLA_DIR)/config/makefiles/target_binaries.mk
|
||||
@ -511,11 +513,11 @@ endef
|
||||
# PROGRAM = Foo
|
||||
# creates OBJS, links with LIBS to create Foo
|
||||
#
|
||||
$(PROGRAM): $(PROGOBJS) $(STATIC_LIBS) $(RUST_STATIC_LIB) $(EXTRA_DEPS) $(RESFILE) $(GLOBAL_DEPS) $(call mkdir_deps,$(FINAL_TARGET))
|
||||
$(PROGRAM): $(PROGOBJS) $(STATIC_LIBS) $(EXTRA_DEPS) $(RESFILE) $(GLOBAL_DEPS) $(call mkdir_deps,$(FINAL_TARGET))
|
||||
$(REPORT_BUILD)
|
||||
@$(RM) $@.manifest
|
||||
ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
|
||||
$(LINKER) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) -IMPLIB:$(basename $(@F)).lib $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_PROGRAM_LDFLAGS) $($(notdir $@)_$(OBJS_VAR_SUFFIX)) $(RESFILE) $(STATIC_LIBS) $(RUST_STATIC_LIB) $(SHARED_LIBS) $(OS_LIBS)
|
||||
$(LINKER) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) -IMPLIB:$(basename $(@F)).lib $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_PROGRAM_LDFLAGS) $($(notdir $@)_$(OBJS_VAR_SUFFIX)) $(RESFILE) $(STATIC_LIBS) $(SHARED_LIBS) $(OS_LIBS)
|
||||
ifdef MSMANIFEST_TOOL
|
||||
@if test -f $@.manifest; then \
|
||||
if test -f '$(srcdir)/$(notdir $@).manifest'; then \
|
||||
@ -536,7 +538,7 @@ ifdef MOZ_PROFILE_GENERATE
|
||||
touch -t `date +%Y%m%d%H%M.%S -d 'now+5seconds'` pgo.relink
|
||||
endif
|
||||
else # !WINNT || GNU_CC
|
||||
$(call EXPAND_CC_OR_CXX,$@) -o $@ $(COMPUTED_CXX_LDFLAGS) $(PGO_CFLAGS) $($(notdir $@)_$(OBJS_VAR_SUFFIX)) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(STATIC_LIBS) $(RUST_STATIC_LIB) $(MOZ_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(OS_LIBS)
|
||||
$(call EXPAND_CC_OR_CXX,$@) -o $@ $(COMPUTED_CXX_LDFLAGS) $(PGO_CFLAGS) $($(notdir $@)_$(OBJS_VAR_SUFFIX)) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(STATIC_LIBS) $(MOZ_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(OS_LIBS)
|
||||
$(call py_action,check_binary,--target $@)
|
||||
endif # WINNT && !GNU_CC
|
||||
|
||||
@ -653,12 +655,12 @@ endif
|
||||
# symlinks back to the originals. The symlinks are a no-op for stabs debugging,
|
||||
# so no need to conditionalize on OS version or debugging format.
|
||||
|
||||
$(SHARED_LIBRARY): $(OBJS) $(RESFILE) $(RUST_STATIC_LIB) $(STATIC_LIBS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
|
||||
$(SHARED_LIBRARY): $(OBJS) $(RESFILE) $(STATIC_LIBS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
|
||||
$(REPORT_BUILD)
|
||||
ifndef INCREMENTAL_LINKER
|
||||
$(RM) $@
|
||||
endif
|
||||
$(MKSHLIB) $($@_$(OBJS_VAR_SUFFIX)) $(RESFILE) $(LDFLAGS) $(STATIC_LIBS) $(RUST_STATIC_LIB) $(SHARED_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(OS_LIBS)
|
||||
$(MKSHLIB) $($@_$(OBJS_VAR_SUFFIX)) $(RESFILE) $(LDFLAGS) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(OS_LIBS)
|
||||
$(call py_action,check_binary,--target $@)
|
||||
|
||||
ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
|
||||
|
@ -23,6 +23,16 @@ add_task(async function() {
|
||||
await stepInToLine(threadFront, 30);
|
||||
await stepOverToLine(threadFront, 31);
|
||||
await stepOverToLine(threadFront, 32);
|
||||
|
||||
// Check that the scopes pane shows the value of the local variable.
|
||||
await waitForPaused(dbg);
|
||||
for (let i = 1; ; i++) {
|
||||
if (getScopeLabel(dbg, i) == "c") {
|
||||
is("NaN", getScopeValue(dbg, i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await stepOverToLine(threadFront, 33);
|
||||
await reverseStepOverToLine(threadFront, 32);
|
||||
await stepOutToLine(threadFront, 27);
|
||||
|
@ -1097,13 +1097,12 @@ function waitUntilPauseFinishes() {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
while (gPauseMode != PauseModes.PAUSED) {
|
||||
gActiveChild.waitUntilPaused();
|
||||
if (pointEquals(gActiveChild.pausePoint(), gPausePoint)) {
|
||||
return;
|
||||
}
|
||||
pokeChild(gActiveChild);
|
||||
}
|
||||
|
||||
gActiveChild.waitUntilPaused();
|
||||
}
|
||||
|
||||
// Synchronously send a child to the specific point and pause.
|
||||
|
@ -408,7 +408,7 @@ ReplayDebugger.prototype = {
|
||||
if (!this._objects[data.id]) {
|
||||
this._addObject(data);
|
||||
}
|
||||
this._getObject(data.id)._names = names;
|
||||
this._getObject(data.id)._setNames(names);
|
||||
}
|
||||
|
||||
for (const frame of pauseData.frames) {
|
||||
@ -1297,6 +1297,13 @@ ReplayDebuggerEnvironment.prototype = {
|
||||
return this._data.optimizedOut;
|
||||
},
|
||||
|
||||
_setNames(names) {
|
||||
this._names = {};
|
||||
names.forEach(({ name, value }) => {
|
||||
this._names[name] = this._dbg._convertValue(value);
|
||||
});
|
||||
},
|
||||
|
||||
_ensureNames() {
|
||||
if (!this._names) {
|
||||
const names = this._dbg._sendRequestAllowDiverge(
|
||||
@ -1306,10 +1313,7 @@ ReplayDebuggerEnvironment.prototype = {
|
||||
},
|
||||
[]
|
||||
);
|
||||
this._names = {};
|
||||
names.forEach(({ name, value }) => {
|
||||
this._names[name] = this._dbg._convertValue(value);
|
||||
});
|
||||
this._setNames(names);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1471,6 +1471,8 @@ function getPauseData() {
|
||||
const names = getEnvironmentNames(env);
|
||||
rv.environments[id] = { data, names };
|
||||
|
||||
names.forEach(({ value }) => addValue(value, true));
|
||||
|
||||
addObject(data.callee);
|
||||
addEnvironment(data.parent);
|
||||
}
|
||||
|
@ -9390,7 +9390,7 @@ static Maybe<LayoutDeviceToScreenScale> ParseScaleString(
|
||||
}
|
||||
|
||||
nsresult scaleErrorCode;
|
||||
float scale = aScaleString.ToFloat(&scaleErrorCode);
|
||||
float scale = aScaleString.ToFloatAllowTrailingChars(&scaleErrorCode);
|
||||
if (NS_FAILED(scaleErrorCode)) {
|
||||
return Some(LayoutDeviceToScreenScale(kViewportMinScale));
|
||||
}
|
||||
|
@ -39,9 +39,10 @@ void nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper) {
|
||||
CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
|
||||
}
|
||||
|
||||
if (mozilla::recordreplay::IsReplaying()) {
|
||||
mozilla::recordreplay::SetWeakPointerJSRoot(this, aWrapper);
|
||||
}
|
||||
// Never collect the wrapper object while recording or replaying, to avoid
|
||||
// non-deterministic behaviors if the cache is emptied and then refilled at
|
||||
// a different point when replaying.
|
||||
recordreplay::HoldJSObject(aWrapper);
|
||||
}
|
||||
|
||||
void nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) {
|
||||
@ -98,12 +99,6 @@ static void DebugWrapperTraceCallback(JS::GCCellPtr aPtr, const char* aName,
|
||||
|
||||
void nsWrapperCache::CheckCCWrapperTraversal(void* aScriptObjectHolder,
|
||||
nsScriptObjectTracer* aTracer) {
|
||||
// Skip checking if we are recording or replaying, as calling
|
||||
// GetWrapperPreserveColor() can cause the cache's wrapper to be cleared.
|
||||
if (recordreplay::IsRecordingOrReplaying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject* wrapper = GetWrapperPreserveColor();
|
||||
if (!wrapper) {
|
||||
return;
|
||||
|
@ -82,10 +82,6 @@ static_assert(sizeof(void*) == 4, "Only support 32-bit and 64-bit");
|
||||
* A number of the methods are implemented in nsWrapperCacheInlines.h because we
|
||||
* have to include some JS headers that don't play nicely with the rest of the
|
||||
* codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
|
||||
*
|
||||
* When recording or replaying an execution, wrapper caches are instrumented so
|
||||
* that they behave consistently even if the GC executes at different points
|
||||
* and collects different objects.
|
||||
*/
|
||||
|
||||
class nsWrapperCache {
|
||||
@ -102,10 +98,6 @@ class nsWrapperCache {
|
||||
{
|
||||
}
|
||||
~nsWrapperCache() {
|
||||
// Clear any JS root associated with this cache while replaying.
|
||||
if (mozilla::recordreplay::IsReplaying()) {
|
||||
mozilla::recordreplay::SetWeakPointerJSRoot(this, nullptr);
|
||||
}
|
||||
// Preserved wrappers should never end up getting cleared, but this can
|
||||
// happen during shutdown when a leaked wrapper object is finalized, causing
|
||||
// its wrapper to be cleared.
|
||||
@ -145,23 +137,6 @@ class nsWrapperCache {
|
||||
* escape.
|
||||
*/
|
||||
JSObject* GetWrapperMaybeDead() const {
|
||||
// Keep track of accesses on the cache when recording or replaying an
|
||||
// execution. Accesses during a GC (when thread events are disallowed)
|
||||
// fetch the underlying object without making sure the returned value
|
||||
// is consistent between recording and replay.
|
||||
if (mozilla::recordreplay::IsRecordingOrReplaying() &&
|
||||
!mozilla::recordreplay::AreThreadEventsDisallowed() &&
|
||||
!mozilla::recordreplay::HasDivergedFromRecording()) {
|
||||
bool success = mozilla::recordreplay::RecordReplayValue(!!mWrapper);
|
||||
if (mozilla::recordreplay::IsReplaying()) {
|
||||
if (success) {
|
||||
MOZ_RELEASE_ASSERT(mWrapper);
|
||||
} else {
|
||||
const_cast<nsWrapperCache*>(this)->ClearWrapper();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mWrapper;
|
||||
}
|
||||
|
||||
|
@ -763,6 +763,8 @@ skip-if = !e10s # Track Bug 1281415
|
||||
[test_meta_viewport_remove_node_from_multiple.html]
|
||||
[test_meta_viewport_replace_content.html]
|
||||
[test_meta_viewport_tiny_display_size.html]
|
||||
[test_meta_viewport_initial_scale_with_trailing_characters.html]
|
||||
[test_meta_viewport_width_with_trailing_characters.html]
|
||||
[test_mozbrowser_apis_blocked.html]
|
||||
[test_mozMatchesSelector.html]
|
||||
[test_mutationobserver_anonymous.html]
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>initial-scale with trailing characters in meta viewport</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta name="viewport" content="initial-scale=1.0/">
|
||||
<script src="viewport_helpers.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>initial-scale=1.0/</p>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
add_task(async function initial_scale_with_trailing_characters() {
|
||||
await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
|
||||
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.width, 800, "width should be scaled by 1 / initial-scale");
|
||||
is(info.height, 480, "height should be scaled by 1 / initial-scale");
|
||||
is(info.defaultZoom, 1, "initial-scale should be 1");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>width with trailing characters in meta viewport</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta name="viewport" content="width=400/">
|
||||
<script src="viewport_helpers.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>width=400/</p>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
add_task(async function width_with_trailing_characters() {
|
||||
await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
|
||||
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.width, 400, "width should be 400");
|
||||
is(info.height, 240, "height should be 240 which is the result of the " +
|
||||
"display height * viewport width / display width");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1368,18 +1368,9 @@ inline mozilla::dom::ReflectionScope GetReflectionScope(
|
||||
|
||||
template <class T>
|
||||
inline void ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj) {
|
||||
// Skip clearing the wrapper when replaying. This method is called during
|
||||
// finalization of |obj|, and when replaying a strong reference is kept on
|
||||
// the contents of the cache: since |obj| is being finalized, the cache
|
||||
// cannot point to |obj|, and clearing here won't do anything.
|
||||
// Additionally, the reference held on the cache may have already been
|
||||
// released, if we are finalizing later than we did while recording, and the
|
||||
// cache may have already been deleted.
|
||||
if (!recordreplay::IsReplaying()) {
|
||||
MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj ||
|
||||
(js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead()));
|
||||
cache->ClearWrapper(obj);
|
||||
}
|
||||
MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj ||
|
||||
(js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead()));
|
||||
cache->ClearWrapper(obj);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@ -1387,13 +1378,9 @@ inline void ClearWrapper(T* p, void*, JSObject* obj) {
|
||||
// QueryInterface to nsWrapperCache can't GC, we hope.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
// Skip clearing the wrapper when replaying, for the same reason as in the
|
||||
// overload above: |p| may have been deleted and we cannot QI it.
|
||||
if (!recordreplay::IsReplaying()) {
|
||||
nsWrapperCache* cache;
|
||||
CallQueryInterface(p, &cache);
|
||||
ClearWrapper(p, cache, obj);
|
||||
}
|
||||
nsWrapperCache* cache;
|
||||
CallQueryInterface(p, &cache);
|
||||
ClearWrapper(p, cache, obj);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@ -2562,11 +2549,6 @@ bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject, nsWrapperCache* aCache) {
|
||||
// object types.
|
||||
inline size_t BindingJSObjectMallocBytes(void* aNativePtr) { return 0; }
|
||||
|
||||
// Register a thing which DeferredFinalize might be called on during GC
|
||||
// finalization. See DeferredFinalize.h
|
||||
template <class T>
|
||||
static void RecordReplayRegisterDeferredFinalize(T* aObject);
|
||||
|
||||
// The BindingJSObjectCreator class is supposed to be used by a caller that
|
||||
// wants to create and initialise a binding JSObject. After initialisation has
|
||||
// been successfully completed it should call ForgetObject().
|
||||
@ -2632,7 +2614,10 @@ class MOZ_STACK_CLASS BindingJSObjectCreator {
|
||||
void InitializationSucceeded() {
|
||||
T* pointer;
|
||||
mNative.forget(&pointer);
|
||||
RecordReplayRegisterDeferredFinalize<T>(pointer);
|
||||
|
||||
// Never collect binding objects while recording or replaying, to avoid
|
||||
// non-deterministically releasing references during finalization.
|
||||
recordreplay::HoldJSObject(mReflector);
|
||||
|
||||
mReflector = nullptr;
|
||||
}
|
||||
@ -2727,12 +2712,6 @@ struct DeferredFinalizer {
|
||||
DeferredFinalize(Impl::AppendDeferredFinalizePointer,
|
||||
Impl::DeferredFinalize, aObject);
|
||||
}
|
||||
|
||||
static void RecordReplayRegisterDeferredFinalize(T* aObject) {
|
||||
typedef DeferredFinalizerImpl<T> Impl;
|
||||
RecordReplayRegisterDeferredFinalizeThing(
|
||||
Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize, aObject);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
@ -2740,10 +2719,6 @@ struct DeferredFinalizer<T, true> {
|
||||
static void AddForDeferredFinalization(T* aObject) {
|
||||
DeferredFinalize(reinterpret_cast<nsISupports*>(aObject));
|
||||
}
|
||||
|
||||
static void RecordReplayRegisterDeferredFinalize(T* aObject) {
|
||||
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, aObject);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
@ -2751,11 +2726,6 @@ static void AddForDeferredFinalization(T* aObject) {
|
||||
DeferredFinalizer<T>::AddForDeferredFinalization(aObject);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void RecordReplayRegisterDeferredFinalize(T* aObject) {
|
||||
DeferredFinalizer<T>::RecordReplayRegisterDeferredFinalize(aObject);
|
||||
}
|
||||
|
||||
// This returns T's CC participant if it participates in CC and does not inherit
|
||||
// from nsISupports. Otherwise, it returns null. QI should be used to get the
|
||||
// participant if T inherits from nsISupports.
|
||||
@ -2865,7 +2835,6 @@ bool CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
|
||||
NS_ADDREF(aNative);
|
||||
|
||||
aCache->SetWrapper(aGlobal);
|
||||
RecordReplayRegisterDeferredFinalize<T>(aNative);
|
||||
|
||||
dom::AllocateProtoAndIfaceCache(
|
||||
aGlobal, CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);
|
||||
|
@ -29,7 +29,7 @@ interface nsICommandManager : nsISupports
|
||||
{
|
||||
/*
|
||||
* Register an observer on the specified command. The observer's Observe
|
||||
* method will get called when the state (enabled/disbaled, or toggled etc)
|
||||
* method will get called when the state (enabled/disabled, or toggled etc)
|
||||
* of the command changes.
|
||||
*
|
||||
* You can register the same observer on multiple commmands by calling this
|
||||
|
@ -8,7 +8,7 @@
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "1167443",
|
||||
title: "Basic audio & video call with disabled bundle and disbaled RTCP-Mux"
|
||||
title: "Basic audio & video call with disabled bundle and disabled RTCP-Mux"
|
||||
});
|
||||
|
||||
var test;
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "mozilla/net/CookieSettings.h"
|
||||
#include "mozilla/net/NeckoChannelParams.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIReferrerInfo.h"
|
||||
@ -98,8 +99,13 @@ ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo* aInfo)
|
||||
|
||||
if (ServiceWorkerParentInterceptEnabled()) {
|
||||
RefPtr<ServiceWorkerPrivateImpl> inner = new ServiceWorkerPrivateImpl(this);
|
||||
nsresult rv = inner->Initialize();
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
// Assert in all debug builds as well as non-debug Nightly and Dev Edition.
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(inner->Initialize()));
|
||||
#else
|
||||
MOZ_ALWAYS_SUCCEEDS(inner->Initialize());
|
||||
#endif
|
||||
|
||||
mInner = inner.forget();
|
||||
}
|
||||
|
@ -20,28 +20,21 @@ support-files =
|
||||
|
||||
[browser_antitracking.js]
|
||||
[browser_antitracking_subiframes.js]
|
||||
skip-if = fission
|
||||
[browser_devtools_serviceworker_interception.js]
|
||||
skip-if =
|
||||
serviceworker_e10s ||
|
||||
fission && debug # Causes shutdown crashes under Fission.
|
||||
fission && debug # Causes shutdown crashes under Fission. Assertion failure: parentFound
|
||||
[browser_force_refresh.js]
|
||||
skip-if = fission
|
||||
[browser_download.js]
|
||||
skip-if = fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
[browser_download_canceled.js]
|
||||
skip-if = verify || (fission && debug) # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
skip-if = verify
|
||||
[browser_navigation_process_swap.js]
|
||||
skip-if = fission || !e10s || verify # Bug 1548643
|
||||
[browser_storage_permission.js]
|
||||
skip-if =
|
||||
(verify && debug && (os == 'win' || os == 'mac')) ||
|
||||
fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
skip-if = verify && debug && (os == 'win' || os == 'mac'))
|
||||
[browser_storage_recovery.js]
|
||||
skip-if = fission || serviceworker_e10s # Fails intermittently under Fission
|
||||
[browser_unregister_with_containers.js]
|
||||
skip-if = fission && debug # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
[browser_userContextId_openWindow.js]
|
||||
skip-if =
|
||||
!e10s || serviceworker_e10s ||
|
||||
(fission && debug) # Crashes: @ mozilla::dom::ServiceWorkerManagerService::PropagateUnregister(unsigned long, mozilla::ipc::PrincipalInfo const&, nsTSubstring<char16_t> const&)
|
||||
skip-if = !e10s || serviceworker_e10s
|
||||
|
@ -863,9 +863,12 @@ nsresult nsXBLBinding::DoInitJSClass(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
|
||||
::JS_SetPrivate(proto, docInfo);
|
||||
NS_ADDREF(docInfo);
|
||||
RecordReplayRegisterDeferredFinalize(docInfo);
|
||||
JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding));
|
||||
|
||||
// Don't collect the proto while recording/replaying, to avoid
|
||||
// non-deterministically releasing the docInfo reference.
|
||||
recordreplay::HoldJSObject(proto);
|
||||
|
||||
// Next, enter the realm of the property holder, wrap the proto, and
|
||||
// stick it on.
|
||||
JSAutoRealm ar3(cx, holder);
|
||||
|
@ -373,8 +373,8 @@ class MOZ_STACK_CLASS LayerMetricsWrapper final {
|
||||
EventRegionsOverride GetEventRegionsOverride() const {
|
||||
MOZ_ASSERT(IsValid());
|
||||
|
||||
if (mLayer->AsRefLayer()) {
|
||||
return mLayer->AsRefLayer()->GetEventRegionsOverride();
|
||||
if (AsRefLayer()) {
|
||||
return AsRefLayer()->GetEventRegionsOverride();
|
||||
}
|
||||
return EventRegionsOverride::NoOverride;
|
||||
}
|
||||
|
@ -326,7 +326,11 @@ class MOZ_STACK_CLASS WebRenderScrollDataWrapper final {
|
||||
|
||||
EventRegionsOverride GetEventRegionsOverride() const {
|
||||
MOZ_ASSERT(IsValid());
|
||||
return mLayer->GetEventRegionsOverride();
|
||||
// Only ref layers can have an event regions override.
|
||||
if (GetReferentId()) {
|
||||
return mLayer->GetEventRegionsOverride();
|
||||
}
|
||||
return EventRegionsOverride::NoOverride;
|
||||
}
|
||||
|
||||
const ScrollbarData& GetScrollbarData() const {
|
||||
|
@ -27,6 +27,15 @@ flat varying int vFuncs[4];
|
||||
#define FILTER_OFFSET 7
|
||||
#define FILTER_COMPONENT_TRANSFER 8
|
||||
#define FILTER_IDENTITY 9
|
||||
#define FILTER_COMPOSITE 10
|
||||
|
||||
#define COMPOSITE_OVER 0
|
||||
#define COMPOSITE_IN 1
|
||||
#define COMPOSITE_OUT 2
|
||||
#define COMPOSITE_ATOP 3
|
||||
#define COMPOSITE_XOR 4
|
||||
#define COMPOSITE_LIGHTER 5
|
||||
#define COMPOSITE_ARITHMETIC 6
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
@ -141,6 +150,12 @@ void main(void) {
|
||||
case FILTER_COMPONENT_TRANSFER:
|
||||
vData = ivec4(aFilterExtraDataAddress, 0, 0);
|
||||
break;
|
||||
case FILTER_COMPOSITE:
|
||||
vData = ivec4(aFilterGenericInt, 0, 0, 0);
|
||||
if (aFilterGenericInt == COMPOSITE_ARITHMETIC) {
|
||||
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -452,6 +467,46 @@ vec4 ComponentTransfer(vec4 colora) {
|
||||
return colora;
|
||||
}
|
||||
|
||||
// Composite Filter
|
||||
|
||||
vec4 composite(vec4 Cs, vec4 Cb, int mode) {
|
||||
vec4 Cr = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
switch (mode) {
|
||||
case COMPOSITE_OVER:
|
||||
Cr.rgb = Cs.a * Cs.rgb + Cb.a * Cb.rgb * (1.0 - Cs.a);
|
||||
Cr.a = Cs.a + Cb.a * (1.0 - Cs.a);
|
||||
break;
|
||||
case COMPOSITE_IN:
|
||||
Cr.rgb = Cs.a * Cs.rgb * Cb.a;
|
||||
Cr.a = Cs.a * Cb.a;
|
||||
break;
|
||||
case COMPOSITE_OUT:
|
||||
Cr.rgb = Cs.a * Cs.rgb * (1.0 - Cb.a);
|
||||
Cr.a = Cs.a * (1.0 - Cb.a);
|
||||
break;
|
||||
case COMPOSITE_ATOP:
|
||||
Cr.rgb = Cs.a * Cs.rgb * Cb.a + Cb.a * Cb.rgb * (1.0 - Cs.a);
|
||||
Cr.a = Cs.a * Cb.a + Cb.a * (1.0 - Cs.a);
|
||||
break;
|
||||
case COMPOSITE_XOR:
|
||||
Cr.rgb = Cs.a * Cs.rgb * (1.0 - Cb.a) + Cb.a * Cb.rgb * (1.0 - Cs.a);
|
||||
Cr.a = Cs.a * (1.0 - Cb.a) + Cb.a * (1.0 - Cs.a);
|
||||
break;
|
||||
case COMPOSITE_LIGHTER:
|
||||
Cr.rgb = Cs.a * Cs.rgb + Cb.a * Cb.rgb;
|
||||
Cr.a = Cs.a + Cb.a;
|
||||
Cr = clamp(Cr, vec4(0.0), vec4(1.0));
|
||||
break;
|
||||
case COMPOSITE_ARITHMETIC:
|
||||
Cr = vec4(vFilterData0.x) * Cs * Cb + vec4(vFilterData0.y) * Cs + vec4(vFilterData0.z) * Cb + vec4(vFilterData0.w);
|
||||
Cr = clamp(Cr, vec4(0.0), vec4(1.0));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Cr;
|
||||
}
|
||||
|
||||
vec4 sampleInUvRect(sampler2DArray sampler, vec3 uv, vec4 uvRect) {
|
||||
vec2 clamped = clamp(uv.xy, uvRect.xy, uvRect.zw);
|
||||
return texture(sampler, vec3(clamped, uv.z), 0.0);
|
||||
@ -521,6 +576,9 @@ void main(void) {
|
||||
case FILTER_IDENTITY:
|
||||
result = Ca;
|
||||
break;
|
||||
case FILTER_COMPOSITE:
|
||||
result = composite(Ca, Cb, vData.x);
|
||||
needsPremul = false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1956,6 +1956,10 @@ impl PictureCompositeMode {
|
||||
primitive.input1.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect)
|
||||
.union(&primitive.input2.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect))
|
||||
}
|
||||
FilterPrimitiveKind::Composite(ref primitive) => {
|
||||
primitive.input1.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect)
|
||||
.union(&primitive.input2.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect))
|
||||
}
|
||||
FilterPrimitiveKind::Identity(ref primitive) =>
|
||||
primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
|
||||
FilterPrimitiveKind::Opacity(ref primitive) =>
|
||||
|
@ -2037,12 +2037,13 @@ impl PrimitiveStore {
|
||||
}
|
||||
|
||||
// Inflate the local rect for this primitive by the inflation factor of
|
||||
// the picture context. This ensures that even if the primitive itself
|
||||
// is not visible, any effects from the blur radius will be correctly
|
||||
// taken into account.
|
||||
// the picture context and include the shadow offset. This ensures that
|
||||
// even if the primitive itself is not visible, any effects from the
|
||||
// blur radius or shadow will be correctly taken into account.
|
||||
let inflation_factor = surface.inflation_factor;
|
||||
let local_rect = prim_local_rect
|
||||
.inflate(inflation_factor, inflation_factor)
|
||||
.union(&prim_shadow_rect)
|
||||
.intersection(&prim_instance.local_clip_rect);
|
||||
let local_rect = match local_rect {
|
||||
Some(local_rect) => local_rect,
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use api::{
|
||||
ColorU, MixBlendMode, FilterPrimitiveInput, FilterPrimitiveKind, ColorSpace,
|
||||
PropertyBinding, PropertyBindingId,
|
||||
PropertyBinding, PropertyBindingId, CompositeOperator,
|
||||
};
|
||||
use api::units::{Au, LayoutSize, LayoutVector2D};
|
||||
use crate::display_list_flattener::IsVisible;
|
||||
@ -19,6 +19,41 @@ use crate::prim_store::{
|
||||
InternablePrimitive,
|
||||
};
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
|
||||
pub enum CompositeOperatorKey {
|
||||
Over,
|
||||
In,
|
||||
Out,
|
||||
Atop,
|
||||
Xor,
|
||||
Lighter,
|
||||
Arithmetic([Au; 4]),
|
||||
}
|
||||
|
||||
impl From<CompositeOperator> for CompositeOperatorKey {
|
||||
fn from(operator: CompositeOperator) -> Self {
|
||||
match operator {
|
||||
CompositeOperator::Over => CompositeOperatorKey::Over,
|
||||
CompositeOperator::In => CompositeOperatorKey::In,
|
||||
CompositeOperator::Out => CompositeOperatorKey::Out,
|
||||
CompositeOperator::Atop => CompositeOperatorKey::Atop,
|
||||
CompositeOperator::Xor => CompositeOperatorKey::Xor,
|
||||
CompositeOperator::Lighter => CompositeOperatorKey::Lighter,
|
||||
CompositeOperator::Arithmetic(k_vals) => {
|
||||
let k_vals = [
|
||||
Au::from_f32_px(k_vals[0]),
|
||||
Au::from_f32_px(k_vals[1]),
|
||||
Au::from_f32_px(k_vals[2]),
|
||||
Au::from_f32_px(k_vals[3]),
|
||||
];
|
||||
CompositeOperatorKey::Arithmetic(k_vals)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
|
||||
@ -32,6 +67,7 @@ pub enum FilterPrimitiveKey {
|
||||
DropShadow(ColorSpace, (VectorKey, Au, ColorU), FilterPrimitiveInput),
|
||||
ComponentTransfer(ColorSpace, FilterPrimitiveInput, Vec<SFilterData>),
|
||||
Offset(ColorSpace, FilterPrimitiveInput, VectorKey),
|
||||
Composite(ColorSpace, FilterPrimitiveInput, FilterPrimitiveInput, CompositeOperatorKey),
|
||||
}
|
||||
|
||||
/// Represents a hashable description of how a picture primitive
|
||||
@ -178,6 +214,8 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
|
||||
FilterPrimitiveKey::ComponentTransfer(primitive.color_space, component_transfer.input, filter_data.clone()),
|
||||
FilterPrimitiveKind::Offset(info) =>
|
||||
FilterPrimitiveKey::Offset(primitive.color_space, info.input, info.offset.into()),
|
||||
FilterPrimitiveKind::Composite(info) =>
|
||||
FilterPrimitiveKey::Composite(primitive.color_space, info.input1, info.input2, info.operator.into()),
|
||||
}
|
||||
}).collect())
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
use api::{ImageDescriptor, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind};
|
||||
use api::{CompositeOperator, ImageDescriptor, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind};
|
||||
use api::{LineStyle, LineOrientation, ClipMode, DirtyRect, MixBlendMode, ColorF, ColorSpace};
|
||||
use api::units::*;
|
||||
use crate::border::BorderSegmentCacheKey;
|
||||
@ -642,6 +642,7 @@ pub enum SvgFilterInfo {
|
||||
DropShadow(ColorF),
|
||||
Offset(DeviceVector2D),
|
||||
ComponentTransfer(SFilterData),
|
||||
Composite(CompositeOperator),
|
||||
// TODO: This is used as a hack to ensure that a blur task's input is always in the blur's previous pass.
|
||||
Identity,
|
||||
}
|
||||
@ -1496,6 +1497,34 @@ impl RenderTask {
|
||||
);
|
||||
render_tasks.add(offset_task)
|
||||
}
|
||||
FilterPrimitiveKind::Composite(info) => {
|
||||
let input_1_task_id = get_task_input(
|
||||
&info.input1,
|
||||
filter_primitives,
|
||||
render_tasks,
|
||||
cur_index,
|
||||
&outputs,
|
||||
original_task_id,
|
||||
primitive.color_space
|
||||
);
|
||||
let input_2_task_id = get_task_input(
|
||||
&info.input2,
|
||||
filter_primitives,
|
||||
render_tasks,
|
||||
cur_index,
|
||||
&outputs,
|
||||
original_task_id,
|
||||
primitive.color_space
|
||||
);
|
||||
|
||||
let task = RenderTask::new_svg_filter_primitive(
|
||||
vec![input_1_task_id, input_2_task_id],
|
||||
content_size,
|
||||
uv_rect_kind,
|
||||
SvgFilterInfo::Composite(info.operator),
|
||||
);
|
||||
render_tasks.add(task)
|
||||
}
|
||||
};
|
||||
outputs.push(render_task_id);
|
||||
}
|
||||
@ -1842,6 +1871,14 @@ impl RenderTask {
|
||||
data.update(request);
|
||||
}
|
||||
}
|
||||
SvgFilterInfo::Composite(ref operator) => {
|
||||
if let CompositeOperator::Arithmetic(k_vals) = operator {
|
||||
let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(|| GpuCacheHandle::new());
|
||||
if let Some(mut request) = gpu_cache.request(handle) {
|
||||
request.push(*k_vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -1502,6 +1502,7 @@ impl SvgFilterTask {
|
||||
SvgFilterInfo::Offset(..) => 7,
|
||||
SvgFilterInfo::ComponentTransfer(..) => 8,
|
||||
SvgFilterInfo::Identity => 9,
|
||||
SvgFilterInfo::Composite(..) => 10,
|
||||
};
|
||||
|
||||
let input_count = match filter {
|
||||
@ -1517,7 +1518,8 @@ impl SvgFilterTask {
|
||||
|
||||
// Not techincally a 2 input filter, but we have 2 inputs here: original content & blurred content.
|
||||
SvgFilterInfo::DropShadow(..) |
|
||||
SvgFilterInfo::Blend(..) => 2,
|
||||
SvgFilterInfo::Blend(..) |
|
||||
SvgFilterInfo::Composite(..) => 2,
|
||||
};
|
||||
|
||||
let generic_int = match filter {
|
||||
@ -1527,7 +1529,8 @@ impl SvgFilterTask {
|
||||
data.g_func.to_int() << 8 |
|
||||
data.b_func.to_int() << 4 |
|
||||
data.a_func.to_int()) as u16),
|
||||
|
||||
SvgFilterInfo::Composite(operator) =>
|
||||
operator.as_int() as u16,
|
||||
SvgFilterInfo::LinearToSrgb |
|
||||
SvgFilterInfo::SrgbToLinear |
|
||||
SvgFilterInfo::Flood(..) |
|
||||
|
@ -723,7 +723,6 @@ pub enum MixBlendMode {
|
||||
Luminosity = 15,
|
||||
}
|
||||
|
||||
/// An input to a SVG filter primitive.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub enum ColorSpace {
|
||||
@ -731,6 +730,35 @@ pub enum ColorSpace {
|
||||
LinearRgb,
|
||||
}
|
||||
|
||||
/// Available composite operoations for the composite filter primitive
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub enum CompositeOperator {
|
||||
Over,
|
||||
In,
|
||||
Atop,
|
||||
Out,
|
||||
Xor,
|
||||
Lighter,
|
||||
Arithmetic([f32; 4]),
|
||||
}
|
||||
|
||||
impl CompositeOperator {
|
||||
// This must stay in sync with the composite operator defines in cs_svg_filter.glsl
|
||||
pub fn as_int(&self) -> u32 {
|
||||
match self {
|
||||
CompositeOperator::Over => 0,
|
||||
CompositeOperator::In => 1,
|
||||
CompositeOperator::Out => 2,
|
||||
CompositeOperator::Atop => 3,
|
||||
CompositeOperator::Xor => 4,
|
||||
CompositeOperator::Lighter => 5,
|
||||
CompositeOperator::Arithmetic(..) => 6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An input to a SVG filter primitive.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub enum FilterPrimitiveInput {
|
||||
@ -844,6 +872,14 @@ pub struct OffsetPrimitive {
|
||||
pub offset: LayoutVector2D,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct CompositePrimitive {
|
||||
pub input1: FilterPrimitiveInput,
|
||||
pub input2: FilterPrimitiveInput,
|
||||
pub operator: CompositeOperator,
|
||||
}
|
||||
|
||||
/// See: https://github.com/eqrion/cbindgen/issues/9
|
||||
/// cbindgen:derive-eq=false
|
||||
#[repr(C)]
|
||||
@ -860,6 +896,7 @@ pub enum FilterPrimitiveKind {
|
||||
DropShadow(DropShadowPrimitive),
|
||||
ComponentTransfer(ComponentTransferPrimitive),
|
||||
Offset(OffsetPrimitive),
|
||||
Composite(CompositePrimitive),
|
||||
}
|
||||
|
||||
impl Default for FilterPrimitiveKind {
|
||||
@ -881,6 +918,7 @@ impl FilterPrimitiveKind {
|
||||
FilterPrimitiveKind::Blend(..) |
|
||||
FilterPrimitiveKind::ColorMatrix(..) |
|
||||
FilterPrimitiveKind::Offset(..) |
|
||||
FilterPrimitiveKind::Composite(..) |
|
||||
// Component transfer's filter data is sanitized separately.
|
||||
FilterPrimitiveKind::ComponentTransfer(..) => {}
|
||||
}
|
||||
@ -1429,5 +1467,6 @@ impl_default_for_enums! {
|
||||
YuvData => NV12(ImageKey::default(), ImageKey::default()),
|
||||
YuvFormat => NV12,
|
||||
FilterPrimitiveInput => Original,
|
||||
ColorSpace => Srgb
|
||||
ColorSpace => Srgb,
|
||||
CompositeOperator => Over
|
||||
}
|
||||
|
BIN
gfx/wr/wrench/reftests/filters/filter-drop-shadow-clip-2.png
Normal file
BIN
gfx/wr/wrench/reftests/filters/filter-drop-shadow-clip-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
@ -0,0 +1,17 @@
|
||||
# Bug 1561447: If a clip task is created for a picture, it should take into account the rect of the drop shadow
|
||||
---
|
||||
root:
|
||||
items:
|
||||
- type: clip
|
||||
id: 2
|
||||
bounds: 10 0 300 300
|
||||
clip-rect: 10 0 300 300
|
||||
- type: stacking-context
|
||||
bounds: 30 30 0 0
|
||||
transform: rotate-z(45)
|
||||
filters: drop-shadow([15, 0], 0, red)
|
||||
clip-node: 2
|
||||
items:
|
||||
- type: rect
|
||||
bounds: 0 0 100 100
|
||||
color: blue
|
@ -35,6 +35,7 @@ platform(linux,mac) == filter-drop-shadow.yaml filter-drop-shadow.png
|
||||
platform(linux,mac) == filter-drop-shadow-on-viewport-edge.yaml filter-drop-shadow-on-viewport-edge.png
|
||||
platform(linux,mac) == blend-clipped.yaml blend-clipped.png
|
||||
platform(linux,mac) == filter-drop-shadow-clip.yaml filter-drop-shadow-clip.png
|
||||
platform(linux,mac) == filter-drop-shadow-clip-2.yaml filter-drop-shadow-clip-2.png
|
||||
== filter-segments.yaml filter-segments-ref.yaml
|
||||
== iframe-dropshadow.yaml iframe-dropshadow-ref.yaml
|
||||
skip_on(android) == filter-mix-blend-mode.yaml filter-mix-blend-mode-ref.yaml # Android debug: GL error 502 at blit_framebuffer (emulator) or draw_elements_instanced (Pixel2); Android opt: fails
|
||||
@ -59,3 +60,4 @@ platform(linux,mac) == svg-filter-drop-shadow-perspective.yaml svg-filter-drop-s
|
||||
== backdrop-filter-basic.yaml backdrop-filter-basic-ref.yaml
|
||||
platform(linux,mac) == backdrop-filter-perspective.yaml backdrop-filter-perspective.png
|
||||
platform(linux,max) == svg-filter-offset.yaml svg-filter-offset-ref.yaml
|
||||
skip_on(android) == fuzzy(1,100) svg-filter-composite.yaml svg-filter-composite-ref.yaml
|
||||
|
73
gfx/wr/wrench/reftests/filters/svg-filter-composite-ref.yaml
Normal file
73
gfx/wr/wrench/reftests/filters/svg-filter-composite-ref.yaml
Normal file
@ -0,0 +1,73 @@
|
||||
# Tests the composite SVG filter primitive
|
||||
---
|
||||
root:
|
||||
items:
|
||||
- type: stacking-context
|
||||
bounds: 0 0 0 0
|
||||
items:
|
||||
- type: rect
|
||||
color: yellow
|
||||
bounds: 10 10 100 100
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
||||
- type: stacking-context
|
||||
bounds: 200 0 0 0
|
||||
items:
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 50 50
|
||||
- type: stacking-context
|
||||
bounds: 400 0 0 0
|
||||
items:
|
||||
- type: rect
|
||||
color: yellow
|
||||
bounds: 10 10 100 100
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 50 50
|
||||
- type: stacking-context
|
||||
bounds: 600 0 0 0
|
||||
items:
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
||||
- type: rect
|
||||
color: white
|
||||
bounds: 60 60 50 50
|
||||
- type: stacking-context
|
||||
bounds: 0 200 0 0
|
||||
items:
|
||||
- type: rect
|
||||
color: yellow
|
||||
bounds: 10 10 100 100
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
||||
- type: rect
|
||||
color: white
|
||||
bounds: 60 60 50 50
|
||||
- type: stacking-context
|
||||
bounds: 200 200 0 0
|
||||
items:
|
||||
- type: rect
|
||||
color: [255, 0, 0, 1.0]
|
||||
bounds: 10 10 100 100
|
||||
- type: rect
|
||||
color: [0, 0, 255, 1.0]
|
||||
bounds: 60 60 100 100
|
||||
- type: rect
|
||||
color: [255, 0, 255, 1.0]
|
||||
bounds: 60 60 50 50
|
||||
- type: stacking-context
|
||||
bounds: 400 200 0 0
|
||||
items:
|
||||
- type: rect
|
||||
color: [255, 255, 127, 1.0]
|
||||
bounds: 10 10 100 100
|
||||
- type: rect
|
||||
color: [127, 127, 255, 1.0]
|
||||
bounds: 60 60 100 100
|
||||
- type: rect
|
||||
color: [188, 188, 188, 1.0]
|
||||
bounds: 60 60 50 50
|
124
gfx/wr/wrench/reftests/filters/svg-filter-composite.yaml
Normal file
124
gfx/wr/wrench/reftests/filters/svg-filter-composite.yaml
Normal file
@ -0,0 +1,124 @@
|
||||
# Tests the composite SVG filter primitive
|
||||
---
|
||||
root:
|
||||
items:
|
||||
- type: stacking-context
|
||||
bounds: 0 0 0 0
|
||||
filter-primitives:
|
||||
- type: flood
|
||||
color: yellow
|
||||
in: previous
|
||||
- type: offset
|
||||
offset: -50 -50
|
||||
in: previous
|
||||
- type: composite
|
||||
in1: original
|
||||
in2: 1
|
||||
operator: over
|
||||
items:
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
||||
- type: stacking-context
|
||||
bounds: 200 0 0 0
|
||||
filter-primitives:
|
||||
- type: flood
|
||||
color: yellow
|
||||
in: previous
|
||||
- type: offset
|
||||
offset: -50 -50
|
||||
in: previous
|
||||
- type: composite
|
||||
in1: original
|
||||
in2: 1
|
||||
operator: in
|
||||
items:
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
||||
- type: stacking-context
|
||||
bounds: 400 0 0 0
|
||||
filter-primitives:
|
||||
- type: flood
|
||||
color: yellow
|
||||
in: previous
|
||||
- type: offset
|
||||
offset: -50 -50
|
||||
in: previous
|
||||
- type: composite
|
||||
in1: original
|
||||
in2: 1
|
||||
operator: atop
|
||||
items:
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
||||
- type: stacking-context
|
||||
bounds: 600 0 0 0
|
||||
filter-primitives:
|
||||
- type: flood
|
||||
color: yellow
|
||||
in: previous
|
||||
- type: offset
|
||||
offset: -50 -50
|
||||
in: previous
|
||||
- type: composite
|
||||
in1: original
|
||||
in2: 1
|
||||
operator: out
|
||||
items:
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
||||
- type: stacking-context
|
||||
bounds: 0 200 0 0
|
||||
filter-primitives:
|
||||
- type: flood
|
||||
color: yellow
|
||||
in: previous
|
||||
- type: offset
|
||||
offset: -50 -50
|
||||
in: previous
|
||||
- type: composite
|
||||
in1: original
|
||||
in2: 1
|
||||
operator: xor
|
||||
items:
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
||||
- type: stacking-context
|
||||
bounds: 200 200 0 0
|
||||
filter-primitives:
|
||||
- type: flood
|
||||
color: [255, 0, 0, 1.0]
|
||||
in: previous
|
||||
- type: offset
|
||||
offset: -50 -50
|
||||
in: previous
|
||||
- type: composite
|
||||
in1: original
|
||||
in2: 1
|
||||
operator: lighter
|
||||
items:
|
||||
- type: rect
|
||||
color: [0, 0, 255, 1.0]
|
||||
bounds: 60 60 100 100
|
||||
- type: stacking-context
|
||||
bounds: 400 200 0 0
|
||||
filter-primitives:
|
||||
- type: flood
|
||||
color: yellow
|
||||
in: previous
|
||||
- type: offset
|
||||
offset: -50 -50
|
||||
in: previous
|
||||
- type: composite
|
||||
in1: original
|
||||
in2: 1
|
||||
operator: arithmetic
|
||||
k-values: [0.5, 0.5, 0.5, 0]
|
||||
items:
|
||||
- type: rect
|
||||
color: blue
|
||||
bounds: 60 60 100 100
|
@ -404,6 +404,26 @@ fn write_filter_primitives(
|
||||
filter_input_node(&mut table, "in", info.input);
|
||||
vector_node(&mut table, "offset", &info.offset);
|
||||
}
|
||||
FilterPrimitiveKind::Composite(info) => {
|
||||
yaml_node(&mut table, "type", Yaml::String("composite".into()));
|
||||
filter_input_node(&mut table, "in1", info.input1);
|
||||
filter_input_node(&mut table, "in2", info.input2);
|
||||
|
||||
let operator = match info.operator {
|
||||
CompositeOperator::Over => "over",
|
||||
CompositeOperator::In => "in",
|
||||
CompositeOperator::Out => "out",
|
||||
CompositeOperator::Atop => "atop",
|
||||
CompositeOperator::Xor => "xor",
|
||||
CompositeOperator::Lighter => "lighter",
|
||||
CompositeOperator::Arithmetic(..) => "arithmetic",
|
||||
};
|
||||
str_node(&mut table, "operator", operator);
|
||||
|
||||
if let CompositeOperator::Arithmetic(k_vals) = info.operator {
|
||||
f32_vec_node(&mut table, "k-values", &k_vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
enum_node(&mut table, "color-space", filter_primitive.color_space);
|
||||
filter_primitives.push(Yaml::Hash(table));
|
||||
|
@ -775,6 +775,28 @@ impl YamlHelper for Yaml {
|
||||
offset: self["offset"].as_vector().unwrap(),
|
||||
})
|
||||
}
|
||||
"composite" => {
|
||||
let operator = match self["operator"].as_str().unwrap() {
|
||||
"over" => CompositeOperator::Over,
|
||||
"in" => CompositeOperator::In,
|
||||
"out" => CompositeOperator::Out,
|
||||
"atop" => CompositeOperator::Atop,
|
||||
"xor" => CompositeOperator::Xor,
|
||||
"lighter" => CompositeOperator::Lighter,
|
||||
"arithmetic" => {
|
||||
let k_vals = self["k-values"].as_vec_f32().unwrap();
|
||||
assert!(k_vals.len() == 4, "Must be 4 k values for arithmetic composite operator");
|
||||
let k_vals = [k_vals[0], k_vals[1], k_vals[2], k_vals[3]];
|
||||
CompositeOperator::Arithmetic(k_vals)
|
||||
}
|
||||
_ => panic!("Invalid composite operator"),
|
||||
};
|
||||
FilterPrimitiveKind::Composite(CompositePrimitive {
|
||||
input1: self["in1"].as_filter_input().unwrap(),
|
||||
input2: self["in2"].as_filter_input().unwrap(),
|
||||
operator,
|
||||
})
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
@ -32,3 +32,6 @@ Rust
|
||||
|
||||
For Rust Mozilla provides a crate `fluent-locale`_ which implements the concepts described
|
||||
above.
|
||||
|
||||
.. _Intl.Locale: https://bugzilla.mozilla.org/show_bug.cgi?id=1433303
|
||||
.. _fluent-locale: https://docs.rs/fluent-locale/
|
||||
|
@ -1,25 +0,0 @@
|
||||
# -*- 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/.
|
||||
|
||||
Library('plugin-container')
|
||||
|
||||
SOURCES += [
|
||||
'plugin-container.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
LOCAL_INCLUDES += [
|
||||
'/toolkit/xre',
|
||||
'/xpcom/base',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
|
||||
LOCAL_INCLUDES += [
|
||||
'/security/sandbox/chromium',
|
||||
'/security/sandbox/chromium-shim',
|
||||
]
|
@ -12,9 +12,6 @@ DIRS += [
|
||||
'testshell',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
|
||||
DIRS += ['contentproc']
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DIRS += ['mscom']
|
||||
|
||||
|
@ -59,7 +59,7 @@ namespace {
|
||||
|
||||
static const char kDefaultRuntimeScriptFilename[] = "xpcshell.js";
|
||||
|
||||
inline XPCShellEnvironment* Environment(Handle<JSObject*> global) {
|
||||
inline XPCShellEnvironment* Environment(JS::Handle<JSObject*> global) {
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(global)) {
|
||||
return nullptr;
|
||||
|
@ -306,7 +306,7 @@ class Nursery {
|
||||
// Register a malloced buffer that is held by a nursery object, which
|
||||
// should be freed at the end of a minor GC. Buffers are unregistered when
|
||||
// their owning objects are tenured.
|
||||
bool registerMallocedBuffer(void* buffer);
|
||||
MOZ_MUST_USE bool registerMallocedBuffer(void* buffer);
|
||||
|
||||
// Mark a malloced buffer as no longer needing to be freed.
|
||||
void removeMallocedBuffer(void* buffer) {
|
||||
|
@ -47,9 +47,6 @@ for stlfile in ['jsdate.*', 'jsnum.*']:
|
||||
with Files('builtin/intl/*'):
|
||||
BUG_COMPONENT = component_intl
|
||||
|
||||
if CONFIG['JS_BUNDLED_EDITLINE']:
|
||||
DIRS += ['editline']
|
||||
|
||||
if not CONFIG['JS_DISABLE_SHELL']:
|
||||
DIRS += ['shell']
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
if CONFIG['JS_SHELL_NAME']:
|
||||
GeckoProgram(CONFIG['JS_SHELL_NAME'], linkage=None)
|
||||
if CONFIG['JS_BUNDLED_EDITLINE']:
|
||||
DIRS += ['../editline']
|
||||
USE_LIBS += ['editline']
|
||||
USE_LIBS += ['static:js']
|
||||
|
||||
|
@ -549,6 +549,27 @@ JSFlatString* JSRope::flattenInternal(JSContext* maybecx) {
|
||||
wholeChars = const_cast<CharT*>(left.nonInlineChars<CharT>(nogc));
|
||||
wholeCapacity = capacity;
|
||||
|
||||
// registerMallocedBuffer is fallible, so attempt it first before doing
|
||||
// anything irreversible.
|
||||
Nursery& nursery = runtimeFromMainThread()->gc.nursery();
|
||||
bool inTenured = !bufferIfNursery;
|
||||
if (!inTenured && left.isTenured()) {
|
||||
// tenured leftmost child is giving its chars buffer to the
|
||||
// nursery-allocated root node.
|
||||
if (!nursery.registerMallocedBuffer(wholeChars)) {
|
||||
if (maybecx) {
|
||||
ReportOutOfMemory(maybecx);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// leftmost child -> root is a tenured -> nursery edge.
|
||||
bufferIfNursery->putWholeCell(&left);
|
||||
} else if (inTenured && !left.isTenured()) {
|
||||
// leftmost child is giving its nursery-held chars buffer to a
|
||||
// tenured string.
|
||||
nursery.removeMallocedBuffer(wholeChars);
|
||||
}
|
||||
|
||||
/*
|
||||
* Simulate a left-most traversal from the root to leftMost->leftChild()
|
||||
* via first_visit_node
|
||||
@ -586,19 +607,6 @@ JSFlatString* JSRope::flattenInternal(JSContext* maybecx) {
|
||||
left.setLengthAndFlags(left_len, DEPENDENT_FLAGS | LATIN1_CHARS_BIT);
|
||||
}
|
||||
left.d.s.u3.base = (JSLinearString*)this; /* will be true on exit */
|
||||
Nursery& nursery = runtimeFromMainThread()->gc.nursery();
|
||||
bool inTenured = !bufferIfNursery;
|
||||
if (!inTenured && left.isTenured()) {
|
||||
// tenured leftmost child is giving its chars buffer to the
|
||||
// nursery-allocated root node.
|
||||
nursery.registerMallocedBuffer(wholeChars);
|
||||
// leftmost child -> root is a tenured -> nursery edge.
|
||||
bufferIfNursery->putWholeCell(&left);
|
||||
} else if (inTenured && !left.isTenured()) {
|
||||
// leftmost child is giving its nursery-held chars buffer to a
|
||||
// tenured string.
|
||||
nursery.removeMallocedBuffer(wholeChars);
|
||||
}
|
||||
goto visit_right_child;
|
||||
}
|
||||
}
|
||||
|
@ -2280,6 +2280,23 @@ static bool DecodeStartSection(Decoder& d, ModuleEnvironment* env) {
|
||||
return d.finishSection(*range, "start");
|
||||
}
|
||||
|
||||
static inline ElemSegment::Kind NormalizeElemSegmentKind(
|
||||
ElemSegmentKind decodedKind) {
|
||||
switch (decodedKind) {
|
||||
case ElemSegmentKind::Active:
|
||||
case ElemSegmentKind::ActiveWithIndex: {
|
||||
return ElemSegment::Kind::Active;
|
||||
}
|
||||
case ElemSegmentKind::Passive: {
|
||||
return ElemSegment::Kind::Passive;
|
||||
}
|
||||
case ElemSegmentKind::Declared: {
|
||||
return ElemSegment::Kind::Declared;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("unexpected elem segment kind");
|
||||
}
|
||||
|
||||
static bool DecodeElemSection(Decoder& d, ModuleEnvironment* env) {
|
||||
MaybeSectionRange range;
|
||||
if (!d.startSection(SectionId::Elem, env, &range, "elem")) {
|
||||
@ -2319,24 +2336,7 @@ static bool DecodeElemSection(Decoder& d, ModuleEnvironment* env) {
|
||||
}
|
||||
|
||||
ElemSegmentKind kind = flags->kind();
|
||||
|
||||
switch (kind) {
|
||||
case ElemSegmentKind::Active:
|
||||
case ElemSegmentKind::ActiveWithIndex: {
|
||||
seg->kind = ElemSegment::Kind::Active;
|
||||
break;
|
||||
}
|
||||
case ElemSegmentKind::Passive: {
|
||||
seg->kind = ElemSegment::Kind::Passive;
|
||||
break;
|
||||
}
|
||||
case ElemSegmentKind::Declared: {
|
||||
seg->kind = ElemSegment::Kind::Declared;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE();
|
||||
}
|
||||
seg->kind = NormalizeElemSegmentKind(kind);
|
||||
|
||||
if (kind == ElemSegmentKind::Active ||
|
||||
kind == ElemSegmentKind::ActiveWithIndex) {
|
||||
|
@ -33,8 +33,11 @@ class SandboxPrivate : public nsIGlobalObject,
|
||||
// The type used to cast to void needs to match the one in GetPrivate.
|
||||
nsIScriptObjectPrincipal* sop =
|
||||
static_cast<nsIScriptObjectPrincipal*>(sbp.forget().take());
|
||||
mozilla::RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, sop);
|
||||
JS_SetPrivate(global, sop);
|
||||
|
||||
// Never collect the global while recording or replaying, so that the
|
||||
// principal reference is not released at a non-deterministic point.
|
||||
mozilla::recordreplay::HoldJSObject(global);
|
||||
}
|
||||
|
||||
static SandboxPrivate* GetPrivate(JSObject* obj) {
|
||||
|
@ -486,8 +486,6 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mIdentity = aIdentity;
|
||||
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mIdentity);
|
||||
|
||||
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
|
||||
|
||||
MOZ_ASSERT(mMaybeProto, "bad ctor param");
|
||||
@ -503,8 +501,6 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mIdentity = aIdentity;
|
||||
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mIdentity);
|
||||
|
||||
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
|
||||
|
||||
MOZ_ASSERT(aScope, "bad ctor param");
|
||||
@ -526,13 +522,8 @@ void XPCWrappedNative::Destroy() {
|
||||
#endif
|
||||
|
||||
if (mIdentity) {
|
||||
// Either release mIdentity immediately or defer the release. When
|
||||
// recording or replaying the release must always be deferred, so that
|
||||
// DeferredFinalize matches the earlier call to
|
||||
// RecordReplayRegisterDeferredFinalizeThing.
|
||||
XPCJSRuntime* rt = GetRuntime();
|
||||
if ((rt && rt->GetDoingFinalization()) ||
|
||||
recordreplay::IsRecordingOrReplaying()) {
|
||||
if (rt && rt->GetDoingFinalization()) {
|
||||
DeferredFinalize(mIdentity.forget().take());
|
||||
} else {
|
||||
mIdentity = nullptr;
|
||||
@ -555,6 +546,10 @@ inline void XPCWrappedNative::SetFlatJSObject(JSObject* object) {
|
||||
|
||||
mFlatJSObject = object;
|
||||
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
|
||||
|
||||
// Never collect the wrapper object while recording or replaying, to avoid
|
||||
// non-deterministically releasing references during finalization.
|
||||
recordreplay::HoldJSObject(object);
|
||||
}
|
||||
|
||||
inline void XPCWrappedNative::UnsetFlatJSObject() {
|
||||
@ -762,10 +757,8 @@ void XPCWrappedNative::FlatJSObjectFinalized() {
|
||||
}
|
||||
|
||||
// We also need to release any native pointers held...
|
||||
// As for XPCWrappedNative::Destroy, when recording or replaying the
|
||||
// release must always be deferred.
|
||||
RefPtr<nsISupports> native = to->TakeNative();
|
||||
if (native && (GetRuntime() || recordreplay::IsRecordingOrReplaying())) {
|
||||
if (native && GetRuntime()) {
|
||||
DeferredFinalize(native.forget().take());
|
||||
}
|
||||
|
||||
@ -1033,7 +1026,6 @@ nsresult XPCWrappedNative::InitTearOff(JSContext* cx,
|
||||
|
||||
aTearOff->SetInterface(aInterface);
|
||||
aTearOff->SetNative(qiResult);
|
||||
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, qiResult);
|
||||
|
||||
if (needJSObject && !InitTearOffJSObject(cx, aTearOff)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -28,8 +28,6 @@ XPCWrappedNativeProto::XPCWrappedNativeProto(
|
||||
#ifdef DEBUG
|
||||
gDEBUG_LiveProtoCount++;
|
||||
#endif
|
||||
|
||||
RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mClassInfo);
|
||||
}
|
||||
|
||||
XPCWrappedNativeProto::~XPCWrappedNativeProto() {
|
||||
@ -57,6 +55,10 @@ bool XPCWrappedNativeProto::Init(JSContext* cx, nsIXPCScriptable* scriptable) {
|
||||
bool success = !!mJSProtoObject;
|
||||
if (success) {
|
||||
JS_SetPrivate(mJSProtoObject, this);
|
||||
|
||||
// Never collect the proto object while recording or replaying, to avoid
|
||||
// non-deterministically releasing references during finalization.
|
||||
recordreplay::HoldJSObject(mJSProtoObject);
|
||||
}
|
||||
|
||||
return success;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StaticPrefs_mathml.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsDisplayList.h"
|
||||
@ -103,28 +104,38 @@ nscoord nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext,
|
||||
// default: medium
|
||||
//
|
||||
if (!aThicknessAttribute.IsEmpty()) {
|
||||
if (aThicknessAttribute.EqualsLiteral("thin")) {
|
||||
lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE);
|
||||
minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS;
|
||||
// should visually decrease by at least one pixel, if default is not a
|
||||
// pixel
|
||||
if (defaultThickness > onePixel &&
|
||||
lineThickness > defaultThickness - onePixel)
|
||||
lineThickness = defaultThickness - onePixel;
|
||||
} else if (aThicknessAttribute.EqualsLiteral("medium")) {
|
||||
// medium is default
|
||||
} else if (aThicknessAttribute.EqualsLiteral("thick")) {
|
||||
lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE);
|
||||
minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS;
|
||||
// should visually increase by at least one pixel
|
||||
if (lineThickness < defaultThickness + onePixel)
|
||||
lineThickness = defaultThickness + onePixel;
|
||||
} else {
|
||||
if (StaticPrefs::mathml_mfrac_linethickness_names_disabled()) {
|
||||
// length value
|
||||
lineThickness = defaultThickness;
|
||||
ParseNumericValue(aThicknessAttribute, &lineThickness,
|
||||
nsMathMLElement::PARSE_ALLOW_UNITLESS, aPresContext,
|
||||
aComputedStyle, aFontSizeInflation);
|
||||
} else {
|
||||
if (aThicknessAttribute.EqualsLiteral("thin")) {
|
||||
lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE);
|
||||
minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS;
|
||||
// should visually decrease by at least one pixel, if default is not a
|
||||
// pixel
|
||||
if (defaultThickness > onePixel &&
|
||||
lineThickness > defaultThickness - onePixel) {
|
||||
lineThickness = defaultThickness - onePixel;
|
||||
}
|
||||
} else if (aThicknessAttribute.EqualsLiteral("medium")) {
|
||||
// medium is default
|
||||
} else if (aThicknessAttribute.EqualsLiteral("thick")) {
|
||||
lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE);
|
||||
minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS;
|
||||
// should visually increase by at least one pixel
|
||||
if (lineThickness < defaultThickness + onePixel) {
|
||||
lineThickness = defaultThickness + onePixel;
|
||||
}
|
||||
} else {
|
||||
// length value
|
||||
lineThickness = defaultThickness;
|
||||
ParseNumericValue(aThicknessAttribute, &lineThickness,
|
||||
nsMathMLElement::PARSE_ALLOW_UNITLESS, aPresContext,
|
||||
aComputedStyle, aFontSizeInflation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,10 +52,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=553917
|
||||
<math><mspace width="2"/></math>
|
||||
<math><mo lspace="BADlspace">+</mo></math>
|
||||
<math><mspace height="BADheight"/></math>
|
||||
<math><mspace depth="BADdepth"/></math>*/
|
||||
<math><mspace depth="BADdepth"/></math>
|
||||
<math><mfrac linethickness="thin"><mn>1</mn><mn>2</mn></mfrac></math>
|
||||
<math><mfrac linethickness="medium"><mn>1</mn><mn>2</mn></mfrac></math>
|
||||
<math><mfrac linethickness="thick"><mn>1</mn><mn>2</mn></mfrac></math>
|
||||
*/
|
||||
LengthParsingError : {
|
||||
status: [false, false, false, false, false, false],
|
||||
args: [["2..0"], ["1.5notaunit"], ["2"],["BADlspace"],["BADheight"],["BADdepth"]]
|
||||
status: [false, false, false, false, false, false, false, false, false],
|
||||
args: [["2..0"], ["1.5notaunit"], ["2"],["BADlspace"],["BADheight"],["BADdepth"], ["thin"],["medium"],["thick"]]
|
||||
},
|
||||
/*<math><mmultiscripts></mmultiscripts></math>
|
||||
<math><mmultiscripts><mprescripts/><mprescripts/></mmultiscripts></math>
|
||||
@ -157,6 +161,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=553917
|
||||
<math><mo lspace="BADlspace">+</mo></math>
|
||||
<math><mspace height="BADheight"/></math>
|
||||
<math><mspace depth="BADdepth"/></math>
|
||||
<math><mfrac linethickness="thin"><mn>1</mn><mn>2</mn></mfrac></math>
|
||||
<math><mfrac linethickness="medium"><mn>1</mn><mn>2</mn></mfrac></math>
|
||||
<math><mfrac linethickness="thick"><mn>1</mn><mn>2</mn></mfrac></math>
|
||||
|
||||
<!-- MMultiscriptsErrors -->
|
||||
<math><mmultiscripts></mmultiscripts></math>
|
||||
|
1
layout/painting/crashtests/1574392.html
Normal file
1
layout/painting/crashtests/1574392.html
Normal file
@ -0,0 +1 @@
|
||||
<u>🏴</u>
|
@ -21,4 +21,4 @@ load 1547420-1.html
|
||||
load 1549909.html
|
||||
asserts(6) load 1551389-1.html # bug 847368
|
||||
asserts(0-2) load 1555819-1.html
|
||||
|
||||
load 1574392.html
|
||||
|
@ -3877,7 +3877,10 @@ static sk_sp<const SkTextBlob> CreateTextBlob(
|
||||
// allocate space for the run buffer, then fill it with the glyphs
|
||||
uint32_t len =
|
||||
CountAllGlyphs(aTextRun, aCompressedGlyph, aStringStart, aStringEnd);
|
||||
MOZ_ASSERT(len > 0, "there must be at least one glyph for skip ink");
|
||||
if (len <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkTextBlobBuilder builder;
|
||||
const SkTextBlobBuilder::RunBuffer& run = builder.allocRunPos(aFont, len);
|
||||
|
||||
@ -4116,6 +4119,10 @@ void nsCSSRendering::PaintDecorationLine(
|
||||
iter.GetStringStart(), iter.GetStringEnd(),
|
||||
(float)appUnitsPerDevPixel, textPos, spacingOffset);
|
||||
|
||||
if (!textBlob) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (textRun->UseCenterBaseline()) {
|
||||
// writing modes that use a center baseline need to be adjusted on a
|
||||
// font-by-font basis since Skia lines up the text on a alphabetic
|
||||
|
BIN
layout/reftests/fonts/AwamiNastaliq-Regular.woff
Normal file
BIN
layout/reftests/fonts/AwamiNastaliq-Regular.woff
Normal file
Binary file not shown.
@ -38,7 +38,7 @@
|
||||
<tr>
|
||||
<td>mfrac: bevelled, linethickness</td>
|
||||
<td><math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<mstyle bevelled="true" linethickness="thick">
|
||||
<mstyle bevelled="true" linethickness="10px">
|
||||
<mfrac>
|
||||
<mi>a</mi>
|
||||
<mi>b</mi>
|
||||
|
@ -149,7 +149,7 @@ random-if(gtkWidget) == mpadded-9.html mpadded-9-ref.html # bug 1309430
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == munderover-align-accent-true.html munderover-align-accent-true-ref.html # Bug 1392106
|
||||
== munder-mover-align-accent-true.html munder-mover-align-accent-true-ref.html
|
||||
== munder-mover-align-accent-false.html munder-mover-align-accent-false-ref.html
|
||||
== mfrac-linethickness-1.xhtml mfrac-linethickness-1-ref.xhtml
|
||||
pref(mathml.mfrac_linethickness_names.disabled,false) == mfrac-linethickness-1.xhtml mfrac-linethickness-1-ref.xhtml
|
||||
== mfrac-linethickness-2.xhtml mfrac-linethickness-2-ref.xhtml
|
||||
== mfrac-linethickness-3.xhtml mfrac-linethickness-3-ref.xhtml
|
||||
== mathml-negativespace.html mathml-negativespace-ref.html
|
||||
@ -341,23 +341,23 @@ random-if(gtkWidget) == rowlines-3-2.html rowlines-3-2-ref.html # bug 1309426
|
||||
== mfrac-A-2.html mfrac-A-2-ref.html
|
||||
== mfrac-A-3.html mfrac-A-3-ref.html
|
||||
== mfrac-A-4.html mfrac-A-4-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == mfrac-A-5.html mfrac-A-5-ref.html # bug 1309426
|
||||
pref(mathml.mfrac_linethickness_names.disabled,false) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == mfrac-A-5.html mfrac-A-5-ref.html # bug 1309426
|
||||
== mfrac-A-6.html mfrac-A-6-ref.html
|
||||
== mfrac-A-7.html mfrac-A-7-ref.html
|
||||
== mfrac-A-8.html mfrac-A-8-ref.html
|
||||
== mfrac-B-1.html mfrac-B-1-ref.html
|
||||
== mfrac-B-2.html mfrac-B-2-3-ref.html
|
||||
== mfrac-B-3.html mfrac-B-2-3-ref.html
|
||||
pref(mathml.mfrac_linethickness_names.disabled,false) == mfrac-B-2.html mfrac-B-2-3-ref.html
|
||||
pref(mathml.mfrac_linethickness_names.disabled,false) == mfrac-B-3.html mfrac-B-2-3-ref.html
|
||||
fuzzy-if(geckoview&&webrender,0-198,0-781) == mfrac-B-4.html mfrac-B-4-5-ref.html
|
||||
== mfrac-B-5.html mfrac-B-4-5-ref.html
|
||||
fuzzy-if(geckoview&&webrender,0-198,0-781) == mfrac-B-6.html mfrac-B-6-7-ref.html
|
||||
== mfrac-B-7.html mfrac-B-6-7-ref.html
|
||||
fuzzy-if(OSX,0-1,0-100) fuzzy-if(skiaContent,0-1,0-14) == mfrac-C-1.html mfrac-C-1-ref.html
|
||||
== mfrac-C-2.html mfrac-C-2-ref.html
|
||||
pref(mathml.mfrac_linethickness_names.disabled,false) == mfrac-C-2.html mfrac-C-2-ref.html
|
||||
fuzzy-if(geckoview&&webrender,0-198,0-776) == mfrac-C-3.html mfrac-C-3-ref.html
|
||||
fuzzy-if(geckoview&&webrender,0-198,0-270) == mfrac-C-4.html mfrac-C-4-ref.html
|
||||
fuzzy-if(OSX,0-1,0-100) fuzzy-if(skiaContent,0-1,0-14) == mfrac-D-1.html mfrac-D-1-ref.html
|
||||
== mfrac-D-2.html mfrac-D-2-ref.html
|
||||
pref(mathml.mfrac_linethickness_names.disabled,false) == mfrac-D-2.html mfrac-D-2-ref.html
|
||||
fuzzy-if(geckoview&&webrender,0-198,0-776) == mfrac-D-3.html mfrac-D-3-ref.html
|
||||
fuzzy-if(geckoview&&webrender,0-198,0-270) == mfrac-D-4.html mfrac-D-4-ref.html
|
||||
== mfrac-E-1.html mfrac-E-1-ref.html
|
||||
|
@ -114,3 +114,7 @@ fuzzy-if(skiaContent,0-4,0-2) == underline-select-1.html underline-select-1-ref.
|
||||
fuzzy-if(Android,0-238,0-36) == vertical-mode-decorations-2.html vertical-mode-decorations-2-ref.html
|
||||
!= 1415214.html 1415214-notref.html
|
||||
test-pref(layout.css.text-decoration-thickness.enabled,false) == text-decoration-shorthands-001.html text-decoration-shorthands-001-ref.html
|
||||
# fails because of bug 1572302
|
||||
test-pref(layout.css.text-decoration-skip-ink.enabled,true) test-pref(layout.css.text-underline-offset.enabled,true) fails HTTP(..) == skip-ink-multiline-position.html skip-ink-multiline-position-ref.html
|
||||
# fails due to bug 1573711
|
||||
test-pref(layout.css.text-decoration-skip-ink.enabled,true) fails == skip-ink-vertical-align.html skip-ink-vertical-align-ref.html
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Reference case for text-decoration-skip-ink: text on the second line should not change the position of the first line's underline</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: awami;
|
||||
src: url("../fonts/AwamiNastaliq-Regular.woff");
|
||||
}
|
||||
div{
|
||||
font: 36px/2 awami, sans-serif;
|
||||
unicode-bidi:bidi-override;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
overflow: hidden;
|
||||
text-decoration: red underline;
|
||||
text-underline-offset: -0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Adding more characters on the second line should not affect the position of the underline on line one. <br>See <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1572302"> Bug 1572303</a> </p>
|
||||
<div dir="rtl">fی sعلي</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test case for text-decoration-skip-ink: text on the second line should not change the position of the first line's underline</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: awami;
|
||||
src: url("../fonts/AwamiNastaliq-Regular.woff");
|
||||
}
|
||||
div{
|
||||
font: 36px/2 awami, sans-serif;
|
||||
unicode-bidi:bidi-override;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
overflow: hidden;
|
||||
text-decoration: red underline;
|
||||
text-underline-offset: -0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Adding more characters on the second line should not affect the position of the underline on line one. <br>See <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1572302"> Bug 1572303</a> </p>
|
||||
<div dir="rtl">fی علي</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Reference case for text-decoration-skip-ink: skip-ink should detect and work with vertical-align</title>
|
||||
<style>
|
||||
div{
|
||||
font: 48px/2 Times;
|
||||
text-decoration: purple underline;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
span{
|
||||
vertical-align: super;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Ink should not be skipped when vertical-align moves text away from the underline</p>
|
||||
<div>test <span>pgqy</span> test</div>
|
||||
</body>
|
||||
</html>
|
19
layout/reftests/text-decoration/skip-ink-vertical-align.html
Normal file
19
layout/reftests/text-decoration/skip-ink-vertical-align.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test case for text-decoration-skip-ink: skip-ink should detect and work with vertical-align</title>
|
||||
<style>
|
||||
div{
|
||||
font: 48px/2 Times;
|
||||
text-decoration: purple underline;
|
||||
}
|
||||
span{
|
||||
vertical-align: super;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Ink should not be skipped when vertical-align moves text away from the underline</p>
|
||||
<div>test <span>pgqy</span> test</div>
|
||||
</body>
|
||||
</html>
|
@ -461,32 +461,11 @@ struct Loader::Sheets {
|
||||
// The SheetLoadData pointers in mLoadingDatas below are weak references.
|
||||
nsDataHashtable<SheetLoadDataHashKey, SheetLoadData*> mLoadingDatas;
|
||||
|
||||
nsRefPtrHashtable<nsStringHashKey, StyleSheet> mInlineSheets;
|
||||
|
||||
|
||||
RefPtr<StyleSheet> LookupInline(const nsAString&);
|
||||
|
||||
// A cache hit or miss. It is a miss if the `StyleSheet` is null.
|
||||
using CacheResult = Tuple<RefPtr<StyleSheet>, SheetState>;
|
||||
CacheResult Lookup(SheetLoadDataHashKey&, bool aSyncLoad);
|
||||
|
||||
size_t SizeOfIncludingThis(MallocSizeOf) const;
|
||||
};
|
||||
|
||||
RefPtr<StyleSheet> Loader::Sheets::LookupInline(const nsAString& aBuffer) {
|
||||
auto result = mInlineSheets.Lookup(aBuffer);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
if (result.Data()->HasForcedUniqueInner()) {
|
||||
// Remove it now that we know that we're never going to use this stylesheet
|
||||
// again.
|
||||
result.Remove();
|
||||
return nullptr;
|
||||
}
|
||||
return result.Data()->Clone(nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static void AssertComplete(const StyleSheet& aSheet) {
|
||||
// This sheet came from the XUL cache or our per-document hashtable; it
|
||||
// better be a complete sheet.
|
||||
@ -580,39 +559,6 @@ auto Loader::Sheets::Lookup(SheetLoadDataHashKey& aKey, bool aSyncLoad)
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t Loader::Sheets::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
|
||||
size_t n = aMallocSizeOf(this);
|
||||
|
||||
n += mCompleteSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
for (auto iter = mCompleteSheets.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
// If the sheet has a parent, then its parent will report it so we don't
|
||||
// have to worry about it here. Likewise, if aSheet has an owning node, then
|
||||
// the document that node is in will report it.
|
||||
const StyleSheet* sheet = iter.UserData();
|
||||
if (!sheet->GetOwnerNode() && !sheet->GetParentSheet()) {
|
||||
n += sheet->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
}
|
||||
|
||||
n += mInlineSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
for (auto iter = mInlineSheets.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
||||
// If the sheet has a parent, then its parent will report it so we don't
|
||||
// have to worry about it here.
|
||||
const StyleSheet* sheet = iter.UserData();
|
||||
MOZ_ASSERT(!sheet->GetParentSheet(), "How did an @import rule end up here?");
|
||||
if (!sheet->GetOwnerNode()) {
|
||||
n += sheet->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
}
|
||||
|
||||
// Measurement of the following members may be added later if DMD finds it is
|
||||
// worthwhile:
|
||||
// - mLoadingDatas: transient, and should be small
|
||||
// - mPendingDatas: transient, and should be small
|
||||
return n;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* Loader Implementation *
|
||||
*************************/
|
||||
@ -1879,7 +1825,6 @@ Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(
|
||||
|
||||
// Check IsAlternateSheet now, since it can mutate our document.
|
||||
auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
|
||||
LOG((" Sheet is alternate: %d", static_cast<int>(isAlternate)));
|
||||
|
||||
// Use the document's base URL so that @import in the inline sheet picks up
|
||||
// the right base.
|
||||
@ -1888,71 +1833,48 @@ Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(
|
||||
nsIURI* originalURI = nullptr;
|
||||
|
||||
MOZ_ASSERT(aInfo.mIntegrity.IsEmpty());
|
||||
auto sheet = MakeRefPtr<StyleSheet>(eAuthorSheetFeatures, aInfo.mCORSMode,
|
||||
SRIMetadata{});
|
||||
sheet->SetURIs(sheetURI, originalURI, baseURI);
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo =
|
||||
ReferrerInfo::CreateForInternalCSSResources(aInfo.mContent->OwnerDoc());
|
||||
sheet->SetReferrerInfo(referrerInfo);
|
||||
|
||||
// We only cache sheets if in shadow trees, since regular document sheets are
|
||||
// likely to be unique.
|
||||
const bool isWorthCaching = aInfo.mContent->IsInShadowTree();
|
||||
RefPtr<StyleSheet> sheet;
|
||||
if (isWorthCaching) {
|
||||
if (!mSheets) {
|
||||
mSheets = MakeUnique<Sheets>();
|
||||
}
|
||||
sheet = mSheets->LookupInline(aBuffer);
|
||||
nsIPrincipal* principal = aInfo.mContent->NodePrincipal();
|
||||
if (aInfo.mTriggeringPrincipal) {
|
||||
// The triggering principal may be an expanded principal, which is safe to
|
||||
// use for URL security checks, but not as the loader principal for a
|
||||
// stylesheet. So treat this as principal inheritance, and downgrade if
|
||||
// necessary.
|
||||
principal =
|
||||
BasePrincipal::Cast(aInfo.mTriggeringPrincipal)->PrincipalToInherit();
|
||||
}
|
||||
const bool sheetFromCache = !!sheet;
|
||||
if (!sheet) {
|
||||
sheet = MakeRefPtr<StyleSheet>(eAuthorSheetFeatures, aInfo.mCORSMode,
|
||||
SRIMetadata{});
|
||||
sheet->SetURIs(sheetURI, originalURI, baseURI);
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo =
|
||||
ReferrerInfo::CreateForInternalCSSResources(aInfo.mContent->OwnerDoc());
|
||||
sheet->SetReferrerInfo(referrerInfo);
|
||||
|
||||
nsIPrincipal* principal = aInfo.mContent->NodePrincipal();
|
||||
if (aInfo.mTriggeringPrincipal) {
|
||||
// The triggering principal may be an expanded principal, which is safe to
|
||||
// use for URL security checks, but not as the loader principal for a
|
||||
// stylesheet. So treat this as principal inheritance, and downgrade if
|
||||
// necessary.
|
||||
principal =
|
||||
BasePrincipal::Cast(aInfo.mTriggeringPrincipal)->PrincipalToInherit();
|
||||
}
|
||||
// We never actually load this, so just set its principal directly
|
||||
sheet->SetPrincipal(principal);
|
||||
|
||||
// We never actually load this, so just set its principal directly
|
||||
sheet->SetPrincipal(principal);
|
||||
}
|
||||
LOG((" Sheet is alternate: %d", static_cast<int>(isAlternate)));
|
||||
|
||||
auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
|
||||
isAlternate, aInfo.mIsExplicitlyEnabled);
|
||||
|
||||
InsertSheetInTree(*sheet, aInfo.mContent);
|
||||
|
||||
Completed completed;
|
||||
if (sheetFromCache) {
|
||||
MOZ_ASSERT(sheet->IsComplete());
|
||||
completed = Completed::Yes;
|
||||
} else {
|
||||
auto data = MakeRefPtr<SheetLoadData>(
|
||||
this, aInfo.mTitle, nullptr, sheet, false, owningElement, isAlternate,
|
||||
matched, aObserver, nullptr, aInfo.mReferrerInfo, aInfo.mContent);
|
||||
data->mLineNumber = aLineNumber;
|
||||
// Parse completion releases the load data.
|
||||
//
|
||||
// Note that we need to parse synchronously, since the web expects that the
|
||||
// effects of inline stylesheets are visible immediately (aside from
|
||||
// @imports).
|
||||
NS_ConvertUTF16toUTF8 utf8(aBuffer);
|
||||
completed = ParseSheet(utf8, *data, AllowAsyncParse::No);
|
||||
if (completed == Completed::Yes) {
|
||||
// TODO(emilio): Try to cache sheets with @import rules, maybe?
|
||||
if (isWorthCaching) {
|
||||
mSheets->mInlineSheets.Put(aBuffer, sheet);
|
||||
}
|
||||
} else {
|
||||
data->mMustNotify = true;
|
||||
}
|
||||
}
|
||||
auto data = MakeRefPtr<SheetLoadData>(
|
||||
this, aInfo.mTitle, nullptr, sheet, false, owningElement, isAlternate,
|
||||
matched, aObserver, nullptr, aInfo.mReferrerInfo, aInfo.mContent);
|
||||
data->mLineNumber = aLineNumber;
|
||||
// Parse completion releases the load data.
|
||||
//
|
||||
// Note that we need to parse synchronously, since the web expects that the
|
||||
// effects of inline stylesheets are visible immediately (aside from
|
||||
// @imports).
|
||||
NS_ConvertUTF16toUTF8 utf8(aBuffer);
|
||||
Completed completed = ParseSheet(utf8, *data, AllowAsyncParse::No);
|
||||
|
||||
if (completed == Completed::No) {
|
||||
data->mMustNotify = true;
|
||||
}
|
||||
return LoadSheetResult{completed, isAlternate, matched};
|
||||
}
|
||||
|
||||
@ -2475,16 +2397,28 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Loader, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Loader, Release)
|
||||
|
||||
size_t Loader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
|
||||
size_t Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
|
||||
size_t n = aMallocSizeOf(this);
|
||||
|
||||
if (mSheets) {
|
||||
n += mSheets->SizeOfIncludingThis(aMallocSizeOf);
|
||||
n += mSheets->mCompleteSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
for (auto iter = mSheets->mCompleteSheets.ConstIter(); !iter.Done();
|
||||
iter.Next()) {
|
||||
// If aSheet has a parent, then its parent will report it so we don't
|
||||
// have to worry about it here. Likewise, if aSheet has an owning node,
|
||||
// then the document that node is in will report it.
|
||||
const StyleSheet* sheet = iter.UserData();
|
||||
n += (sheet->GetOwnerNode() || sheet->GetParentSheet())
|
||||
? 0
|
||||
: sheet->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
}
|
||||
n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
|
||||
// Measurement of the following members may be added later if DMD finds it is
|
||||
// worthwhile:
|
||||
// - mLoadingDatas: transient, and should be small
|
||||
// - mPendingDatas: transient, and should be small
|
||||
// - mPostedEvents: transient, and should be small
|
||||
//
|
||||
// The following members aren't measured:
|
||||
|
@ -3,4 +3,4 @@ git repository using the update.sh script.
|
||||
|
||||
The cubeb-coreaudio-rs git repository is: https://github.com/ChunMinChang/cubeb-coreaudio-rs
|
||||
|
||||
The git commit ID used was ee0f9814230f4a236516444eff9406857ab6c70d (2019-08-01 10:57:48 -0700)
|
||||
The git commit ID used was 71faddb88ad9cdc51147451ebb57316c159278e1 (2019-08-16 08:46:59 -0700)
|
||||
|
@ -1,7 +1,11 @@
|
||||
use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull, Boolean, CFIndex};
|
||||
use core_foundation_sys::base::{
|
||||
kCFAllocatorDefault, kCFAllocatorNull, Boolean, CFIndex, CFRange, CFRelease,
|
||||
};
|
||||
use core_foundation_sys::string::{
|
||||
kCFStringEncodingUTF8, CFStringCreateWithBytes, CFStringCreateWithBytesNoCopy,
|
||||
CFStringGetBytes, CFStringGetLength, CFStringRef,
|
||||
};
|
||||
use std::ffi::CString;
|
||||
|
||||
pub fn cfstringref_from_static_string(string: &'static str) -> coreaudio_sys::CFStringRef {
|
||||
// Set deallocator to kCFAllocatorNull to prevent the the memory of the parameter `string`
|
||||
@ -32,11 +36,95 @@ pub fn cfstringref_from_string(string: &str) -> coreaudio_sys::CFStringRef {
|
||||
cfstringref as coreaudio_sys::CFStringRef
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StringRef(CFStringRef);
|
||||
impl StringRef {
|
||||
pub fn new(string_ref: CFStringRef) -> Self {
|
||||
assert!(!string_ref.is_null());
|
||||
Self(string_ref)
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
String::from_utf8(utf8_from_cfstringref(self.0)).expect("convert bytes to a String")
|
||||
}
|
||||
|
||||
pub fn into_string(self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
|
||||
pub fn to_cstring(&self) -> CString {
|
||||
unsafe {
|
||||
// Assume that bytes doesn't contain `0` in the middle.
|
||||
CString::from_vec_unchecked(utf8_from_cfstringref(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_cstring(self) -> CString {
|
||||
self.to_cstring()
|
||||
}
|
||||
|
||||
pub fn get_raw(&self) -> CFStringRef {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StringRef {
|
||||
fn drop(&mut self) {
|
||||
use std::os::raw::c_void;
|
||||
unsafe { CFRelease(self.0 as *mut c_void) };
|
||||
}
|
||||
}
|
||||
|
||||
fn utf8_from_cfstringref(string_ref: CFStringRef) -> Vec<u8> {
|
||||
use std::ptr;
|
||||
|
||||
assert!(!string_ref.is_null());
|
||||
|
||||
let length: CFIndex = unsafe { CFStringGetLength(string_ref) };
|
||||
assert!(length > 0);
|
||||
|
||||
// Get the buffer size of the string.
|
||||
let range: CFRange = CFRange {
|
||||
location: 0,
|
||||
length,
|
||||
};
|
||||
let mut size: CFIndex = 0;
|
||||
let mut converted_chars: CFIndex = unsafe {
|
||||
CFStringGetBytes(
|
||||
string_ref,
|
||||
range,
|
||||
kCFStringEncodingUTF8,
|
||||
0,
|
||||
false as Boolean,
|
||||
ptr::null_mut() as *mut u8,
|
||||
0,
|
||||
&mut size,
|
||||
)
|
||||
};
|
||||
assert!(converted_chars > 0 && size > 0);
|
||||
|
||||
// Then, allocate the buffer with the required size and actually copy data into it.
|
||||
let mut buffer = vec![b'\x00'; size as usize];
|
||||
converted_chars = unsafe {
|
||||
CFStringGetBytes(
|
||||
string_ref,
|
||||
range,
|
||||
kCFStringEncodingUTF8,
|
||||
0,
|
||||
false as Boolean,
|
||||
buffer.as_mut_ptr(),
|
||||
size,
|
||||
ptr::null_mut() as *mut CFIndex,
|
||||
)
|
||||
};
|
||||
assert!(converted_chars > 0);
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use core_foundation_sys::base::{CFRange, CFRelease};
|
||||
use core_foundation_sys::string::{CFStringGetBytes, CFStringGetLength, CFStringRef};
|
||||
|
||||
const STATIC_STRING: &str = "static string for testing";
|
||||
|
||||
@ -44,7 +132,11 @@ mod test {
|
||||
fn test_create_static_cfstring_ref() {
|
||||
let stringref =
|
||||
StringRef::new(cfstringref_from_static_string(STATIC_STRING) as CFStringRef);
|
||||
assert_eq!(STATIC_STRING, stringref.into_string());
|
||||
assert_eq!(STATIC_STRING, stringref.to_string());
|
||||
assert_eq!(
|
||||
CString::new(STATIC_STRING).unwrap(),
|
||||
stringref.into_cstring()
|
||||
);
|
||||
// TODO: Find a way to check the string's inner pointer is same.
|
||||
}
|
||||
|
||||
@ -52,77 +144,8 @@ mod test {
|
||||
fn test_create_cfstring_ref() {
|
||||
let expected = "Rustaceans 🦀";
|
||||
let stringref = StringRef::new(cfstringref_from_string(expected) as CFStringRef);
|
||||
assert_eq!(expected, stringref.into_string());
|
||||
assert_eq!(expected, stringref.to_string());
|
||||
assert_eq!(CString::new(expected).unwrap(), stringref.into_cstring());
|
||||
// TODO: Find a way to check the string's inner pointer is different.
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StringRef(CFStringRef);
|
||||
impl StringRef {
|
||||
fn new(string_ref: CFStringRef) -> Self {
|
||||
assert!(!string_ref.is_null());
|
||||
Self(string_ref)
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
String::from_utf8(utf8_from_cfstringref(self.0)).unwrap()
|
||||
}
|
||||
|
||||
fn into_string(self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StringRef {
|
||||
fn drop(&mut self) {
|
||||
use std::os::raw::c_void;
|
||||
unsafe { CFRelease(self.0 as *mut c_void) };
|
||||
}
|
||||
}
|
||||
|
||||
fn utf8_from_cfstringref(string_ref: CFStringRef) -> Vec<u8> {
|
||||
use std::ptr;
|
||||
|
||||
assert!(!string_ref.is_null());
|
||||
|
||||
let length: CFIndex = unsafe { CFStringGetLength(string_ref) };
|
||||
assert!(length > 0);
|
||||
|
||||
let range: CFRange = CFRange {
|
||||
location: 0,
|
||||
length,
|
||||
};
|
||||
let mut size: CFIndex = 0;
|
||||
let mut converted_chars: CFIndex = unsafe {
|
||||
CFStringGetBytes(
|
||||
string_ref,
|
||||
range,
|
||||
kCFStringEncodingUTF8,
|
||||
0,
|
||||
false as Boolean,
|
||||
ptr::null_mut() as *mut u8,
|
||||
0,
|
||||
&mut size,
|
||||
)
|
||||
};
|
||||
assert!(converted_chars > 0 && size > 0);
|
||||
|
||||
// Then, allocate the buffer with the required size and actually copy data into it.
|
||||
let mut buffer = vec![b'\x00'; size as usize];
|
||||
converted_chars = unsafe {
|
||||
CFStringGetBytes(
|
||||
string_ref,
|
||||
range,
|
||||
kCFStringEncodingUTF8,
|
||||
0,
|
||||
false as Boolean,
|
||||
buffer.as_mut_ptr(),
|
||||
size,
|
||||
ptr::null_mut() as *mut CFIndex,
|
||||
)
|
||||
};
|
||||
assert!(converted_chars > 0);
|
||||
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use super::*;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
const APPLE_EVENT_TIMEOUT: OSStatus = -1712;
|
||||
pub const DRIFT_COMPENSATION: u32 = 1;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AggregateDevice {
|
||||
@ -349,17 +350,13 @@ impl AggregateDevice {
|
||||
// The order of the items in the array is significant and is used to determine the order of the streams
|
||||
// of the AudioAggregateDevice.
|
||||
for device in output_sub_devices {
|
||||
let uid = get_device_name(device);
|
||||
assert!(!uid.is_null());
|
||||
CFArrayAppendValue(sub_devices, uid as *const c_void);
|
||||
CFRelease(uid as *const c_void);
|
||||
let uid = get_device_global_uid(device)?;
|
||||
CFArrayAppendValue(sub_devices, uid.get_raw() as *const c_void);
|
||||
}
|
||||
|
||||
for device in input_sub_devices {
|
||||
let uid = get_device_name(device);
|
||||
assert!(!uid.is_null());
|
||||
CFArrayAppendValue(sub_devices, uid as *const c_void);
|
||||
CFRelease(uid as *const c_void);
|
||||
let uid = get_device_global_uid(device)?;
|
||||
CFArrayAppendValue(sub_devices, uid.get_raw() as *const c_void);
|
||||
}
|
||||
|
||||
let address = AudioObjectPropertyAddress {
|
||||
@ -432,14 +429,10 @@ impl AggregateDevice {
|
||||
assert_ne!(output_device_id, kAudioObjectUnknown);
|
||||
let output_sub_devices = Self::get_sub_devices(output_device_id)?;
|
||||
assert!(!output_sub_devices.is_empty());
|
||||
let master_sub_device = get_device_name(output_sub_devices[0]);
|
||||
let master_sub_device_uid = get_device_global_uid(output_sub_devices[0]).unwrap();
|
||||
let master_sub_device = master_sub_device_uid.get_raw();
|
||||
let size = mem::size_of::<CFStringRef>();
|
||||
let status = audio_object_set_property_data(device_id, &address, size, &master_sub_device);
|
||||
if !master_sub_device.is_null() {
|
||||
unsafe {
|
||||
CFRelease(master_sub_device as *const c_void);
|
||||
}
|
||||
}
|
||||
if status == NO_ERR {
|
||||
Ok(())
|
||||
} else {
|
||||
@ -499,12 +492,11 @@ impl AggregateDevice {
|
||||
|
||||
// Start from the second device since the first is the master clock
|
||||
for device in &sub_devices[1..] {
|
||||
let drift_compensation_value: u32 = 1;
|
||||
let status = audio_object_set_property_data(
|
||||
*device,
|
||||
&address,
|
||||
mem::size_of::<u32>(),
|
||||
&drift_compensation_value,
|
||||
&DRIFT_COMPENSATION,
|
||||
);
|
||||
if status != NO_ERR {
|
||||
cubeb_log!(
|
||||
@ -555,43 +547,19 @@ impl AggregateDevice {
|
||||
assert_ne!(output_id, kAudioObjectUnknown);
|
||||
assert_ne!(input_id, output_id);
|
||||
|
||||
let mut input_device_info = ffi::cubeb_device_info::default();
|
||||
audiounit_create_device_from_hwdev(&mut input_device_info, input_id, DeviceType::INPUT);
|
||||
let label = get_device_label(input_id, DeviceType::INPUT)?;
|
||||
let input_label = label.into_string();
|
||||
|
||||
let mut output_device_info = ffi::cubeb_device_info::default();
|
||||
audiounit_create_device_from_hwdev(&mut output_device_info, output_id, DeviceType::OUTPUT);
|
||||
let label = get_device_label(output_id, DeviceType::OUTPUT)?;
|
||||
let output_label = label.into_string();
|
||||
|
||||
let input_name_str = unsafe {
|
||||
CString::from_raw(input_device_info.friendly_name as *mut c_char)
|
||||
.into_string()
|
||||
.expect("Fail to convert input name from CString into String")
|
||||
};
|
||||
input_device_info.friendly_name = ptr::null();
|
||||
|
||||
let output_name_str = unsafe {
|
||||
CString::from_raw(output_device_info.friendly_name as *mut c_char)
|
||||
.into_string()
|
||||
.expect("Fail to convert output name from CString into String")
|
||||
};
|
||||
output_device_info.friendly_name = ptr::null();
|
||||
|
||||
let _teardown = finally(|| {
|
||||
// Retrieve the rest lost memory.
|
||||
// No need to retrieve the memory of {input,output}_device_info.friendly_name
|
||||
// since they are already retrieved/retaken above.
|
||||
assert!(input_device_info.friendly_name.is_null());
|
||||
audiounit_device_destroy(&mut input_device_info);
|
||||
assert!(output_device_info.friendly_name.is_null());
|
||||
audiounit_device_destroy(&mut output_device_info);
|
||||
});
|
||||
|
||||
if input_name_str.contains("AirPods") && output_name_str.contains("AirPods") {
|
||||
if input_label.contains("AirPods") && output_label.contains("AirPods") {
|
||||
let mut input_min_rate = 0;
|
||||
let mut input_max_rate = 0;
|
||||
let mut input_nominal_rate = 0;
|
||||
audiounit_get_available_samplerate(
|
||||
input_id,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
DeviceType::INPUT | DeviceType::OUTPUT,
|
||||
&mut input_min_rate,
|
||||
&mut input_max_rate,
|
||||
&mut input_nominal_rate,
|
||||
@ -599,7 +567,7 @@ impl AggregateDevice {
|
||||
cubeb_log!(
|
||||
"Input device {}, name: {}, min: {}, max: {}, nominal rate: {}",
|
||||
input_id,
|
||||
input_name_str,
|
||||
input_label,
|
||||
input_min_rate,
|
||||
input_max_rate,
|
||||
input_nominal_rate
|
||||
@ -610,7 +578,7 @@ impl AggregateDevice {
|
||||
let mut output_nominal_rate = 0;
|
||||
audiounit_get_available_samplerate(
|
||||
output_id,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
DeviceType::INPUT | DeviceType::OUTPUT,
|
||||
&mut output_min_rate,
|
||||
&mut output_max_rate,
|
||||
&mut output_nominal_rate,
|
||||
@ -618,7 +586,7 @@ impl AggregateDevice {
|
||||
cubeb_log!(
|
||||
"Output device {}, name: {}, min: {}, max: {}, nominal rate: {}",
|
||||
output_id,
|
||||
output_name_str,
|
||||
output_label,
|
||||
output_min_rate,
|
||||
output_max_rate,
|
||||
output_nominal_rate
|
||||
|
@ -133,7 +133,7 @@ fn test_auto_array_impl<T: Clone + Debug + PartialEq + Zero>(buf: &[T]) {
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_auto_array_wrapper<T: Clone + Debug + PartialEq + Zero>(buf: &[T]) {
|
||||
let mut auto_array: Option<Box<AutoArrayWrapper>> = None;
|
||||
let mut auto_array: Option<Box<dyn AutoArrayWrapper>> = None;
|
||||
auto_array = Some(Box::new(AutoArrayImpl::<T>::new(5)));
|
||||
|
||||
assert_eq!(auto_array.as_ref().unwrap().elements(), 0);
|
||||
|
159
media/libcubeb/cubeb-coreaudio-rs/src/backend/device_property.rs
Normal file
159
media/libcubeb/cubeb-coreaudio-rs/src/backend/device_property.rs
Normal file
@ -0,0 +1,159 @@
|
||||
use super::*;
|
||||
|
||||
pub fn get_device_global_uid(id: AudioDeviceID) -> std::result::Result<StringRef, OSStatus> {
|
||||
get_device_uid(id, DeviceType::INPUT | DeviceType::OUTPUT)
|
||||
}
|
||||
|
||||
pub fn get_device_uid(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<StringRef, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(Property::DeviceUID, devtype);
|
||||
let mut size = mem::size_of::<CFStringRef>();
|
||||
let mut uid: CFStringRef = ptr::null();
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut uid);
|
||||
if err == NO_ERR {
|
||||
Ok(StringRef::new(uid as _))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_source(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<u32, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(Property::DeviceSource, devtype);
|
||||
let mut size = mem::size_of::<u32>();
|
||||
let mut source: u32 = 0;
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut source);
|
||||
if err == NO_ERR {
|
||||
Ok(source)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_source_name(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<StringRef, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let mut source: u32 = get_device_source(id, devtype)?;
|
||||
let address = get_property_address(Property::DeviceSourceName, devtype);
|
||||
let mut size = mem::size_of::<AudioValueTranslation>();
|
||||
let mut name: CFStringRef = ptr::null();
|
||||
let mut trl = AudioValueTranslation {
|
||||
mInputData: &mut source as *mut u32 as *mut c_void,
|
||||
mInputDataSize: mem::size_of::<u32>() as u32,
|
||||
mOutputData: &mut name as *mut CFStringRef as *mut c_void,
|
||||
mOutputDataSize: mem::size_of::<CFStringRef>() as u32,
|
||||
};
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut trl);
|
||||
if err == NO_ERR {
|
||||
Ok(StringRef::new(name as _))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_name(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<StringRef, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(Property::DeviceName, devtype);
|
||||
let mut size = mem::size_of::<CFStringRef>();
|
||||
let mut name: CFStringRef = ptr::null();
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut name);
|
||||
if err == NO_ERR {
|
||||
Ok(StringRef::new(name as _))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_label(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<StringRef, OSStatus> {
|
||||
get_device_source_name(id, devtype).or(get_device_name(id, devtype))
|
||||
}
|
||||
|
||||
pub fn get_device_manufacturer(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<StringRef, OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(Property::DeviceManufacturer, devtype);
|
||||
let mut size = mem::size_of::<CFStringRef>();
|
||||
let mut manufacturer: CFStringRef = ptr::null();
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut manufacturer);
|
||||
if err == NO_ERR {
|
||||
Ok(StringRef::new(manufacturer as _))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_buffer_frame_size_range(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<(f64, f64), OSStatus> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
|
||||
let address = get_property_address(Property::DeviceBufferFrameSizeRange, devtype);
|
||||
let mut size = mem::size_of::<AudioValueRange>();
|
||||
let mut range = AudioValueRange::default();
|
||||
let err = audio_object_get_property_data(id, &address, &mut size, &mut range);
|
||||
if err == NO_ERR {
|
||||
Ok((range.mMinimum, range.mMaximum))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
enum Property {
|
||||
DeviceBufferFrameSizeRange,
|
||||
DeviceManufacturer,
|
||||
DeviceName,
|
||||
DeviceSource,
|
||||
DeviceSourceName,
|
||||
DeviceUID,
|
||||
}
|
||||
|
||||
impl From<Property> for AudioObjectPropertySelector {
|
||||
fn from(p: Property) -> Self {
|
||||
match p {
|
||||
Property::DeviceBufferFrameSizeRange => kAudioDevicePropertyBufferFrameSizeRange,
|
||||
Property::DeviceManufacturer => kAudioObjectPropertyManufacturer,
|
||||
Property::DeviceName => kAudioObjectPropertyName,
|
||||
Property::DeviceSource => kAudioDevicePropertyDataSource,
|
||||
Property::DeviceSourceName => kAudioDevicePropertyDataSourceNameForIDCFString,
|
||||
Property::DeviceUID => kAudioDevicePropertyDeviceUID,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property_address(property: Property, devtype: DeviceType) -> AudioObjectPropertyAddress {
|
||||
const GLOBAL: ffi::cubeb_device_type =
|
||||
ffi::CUBEB_DEVICE_TYPE_INPUT | ffi::CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
let scope = match devtype.bits() {
|
||||
ffi::CUBEB_DEVICE_TYPE_INPUT => kAudioDevicePropertyScopeInput,
|
||||
ffi::CUBEB_DEVICE_TYPE_OUTPUT => kAudioDevicePropertyScopeOutput,
|
||||
GLOBAL => kAudioObjectPropertyScopeGlobal,
|
||||
_ => panic!("Invalid type"),
|
||||
};
|
||||
AudioObjectPropertyAddress {
|
||||
mSelector: property.into(),
|
||||
mScope: scope,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ extern crate libc;
|
||||
mod aggregate_device;
|
||||
mod auto_array;
|
||||
mod auto_release;
|
||||
mod device_property;
|
||||
mod mixer;
|
||||
mod property_address;
|
||||
mod resampler;
|
||||
@ -26,6 +27,7 @@ use self::coreaudio_sys_utils::cf_mutable_dict::*;
|
||||
use self::coreaudio_sys_utils::dispatch::*;
|
||||
use self::coreaudio_sys_utils::string::*;
|
||||
use self::coreaudio_sys_utils::sys::*;
|
||||
use self::device_property::*;
|
||||
use self::mixer::*;
|
||||
use self::property_address::*;
|
||||
use self::resampler::*;
|
||||
@ -41,7 +43,7 @@ use std::cmp;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, Ordering};
|
||||
@ -317,7 +319,7 @@ fn create_auto_array(
|
||||
desc: AudioStreamBasicDescription,
|
||||
latency_frames: u32,
|
||||
capacity: usize,
|
||||
) -> Result<Box<AutoArrayWrapper>> {
|
||||
) -> Result<Box<dyn AutoArrayWrapper>> {
|
||||
assert_ne!(desc.mFormatFlags, 0);
|
||||
assert_ne!(desc.mChannelsPerFrame, 0);
|
||||
assert_ne!(latency_frames, 0);
|
||||
@ -1168,22 +1170,6 @@ fn audiounit_set_channel_layout(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_device_name(id: AudioDeviceID) -> CFStringRef {
|
||||
let mut size = mem::size_of::<CFStringRef>();
|
||||
let mut uiname: CFStringRef = ptr::null();
|
||||
let address_uuid = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioDevicePropertyDeviceUID,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let err = audio_object_get_property_data(id, &address_uuid, &mut size, &mut uiname);
|
||||
if err == NO_ERR {
|
||||
uiname
|
||||
} else {
|
||||
ptr::null()
|
||||
}
|
||||
}
|
||||
|
||||
fn start_audiounit(unit: AudioUnit) -> Result<()> {
|
||||
let status = audio_output_unit_start(unit);
|
||||
if status == NO_ERR {
|
||||
@ -1523,7 +1509,7 @@ fn convert_uint32_into_string(data: u32) -> CString {
|
||||
CString::new(buffer).unwrap_or(empty)
|
||||
}
|
||||
|
||||
fn audiounit_get_default_datasource(side: io_side) -> Result<(u32)> {
|
||||
fn audiounit_get_default_datasource(side: io_side) -> Result<u32> {
|
||||
let (devtype, address) = match side {
|
||||
io_side::INPUT => (DeviceType::INPUT, INPUT_DATA_SOURCE_PROPERTY_ADDRESS),
|
||||
io_side::OUTPUT => (DeviceType::OUTPUT, OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS),
|
||||
@ -1549,82 +1535,62 @@ fn audiounit_get_default_datasource_string(side: io_side) -> Result<CString> {
|
||||
Ok(convert_uint32_into_string(data))
|
||||
}
|
||||
|
||||
fn audiounit_strref_to_cstr_utf8(strref: CFStringRef) -> CString {
|
||||
let empty = CString::default();
|
||||
if strref.is_null() {
|
||||
return empty;
|
||||
}
|
||||
|
||||
let len = unsafe { CFStringGetLength(strref) };
|
||||
// Add 1 to size to allow for '\0' termination character.
|
||||
let size = unsafe { CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1 };
|
||||
let mut buffer = vec![b'\x00'; size as usize];
|
||||
|
||||
let success = unsafe {
|
||||
CFStringGetCString(
|
||||
strref,
|
||||
buffer.as_mut_ptr() as *mut c_char,
|
||||
size,
|
||||
kCFStringEncodingUTF8,
|
||||
) != 0
|
||||
};
|
||||
if !success {
|
||||
buffer.clear();
|
||||
return empty;
|
||||
}
|
||||
|
||||
// CString::new() will consume the input bytes vec and add a '\0' at the
|
||||
// end of the bytes. We need to remove the '\0' from the bytes data
|
||||
// returned from CFStringGetCString by ourselves to avoid memory leaks.
|
||||
// The size returned from CFStringGetMaximumSizeForEncoding is always
|
||||
// greater than or equal to the string length, where the string length
|
||||
// is the number of characters from the beginning to nul-terminator('\0'),
|
||||
// so we should shrink the string vector to fit that size.
|
||||
let str_len = unsafe { libc::strlen(buffer.as_ptr() as *mut c_char) };
|
||||
buffer.truncate(str_len); // Drop the elements from '\0'(including '\0').
|
||||
|
||||
CString::new(buffer).unwrap_or(empty)
|
||||
}
|
||||
|
||||
fn audiounit_get_channel_count(devid: AudioObjectID, scope: AudioObjectPropertyScope) -> u32 {
|
||||
let mut count: u32 = 0;
|
||||
let mut size: usize = 0;
|
||||
fn get_channel_count(devid: AudioObjectID, devtype: DeviceType) -> Result<u32> {
|
||||
assert_ne!(devid, kAudioObjectUnknown);
|
||||
|
||||
let adr = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioDevicePropertyStreamConfiguration,
|
||||
mScope: scope,
|
||||
mScope: match devtype {
|
||||
DeviceType::INPUT => kAudioDevicePropertyScopeInput,
|
||||
DeviceType::OUTPUT => kAudioDevicePropertyScopeOutput,
|
||||
_ => panic!("Invalid type"),
|
||||
},
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
|
||||
if audio_object_get_property_data_size(devid, &adr, &mut size) == NO_ERR && size > 0 {
|
||||
let mut data: Vec<u8> = allocate_array_by_size(size);
|
||||
let ptr = data.as_mut_ptr() as *mut AudioBufferList;
|
||||
if audio_object_get_property_data(devid, &adr, &mut size, ptr) == NO_ERR {
|
||||
let list: &AudioBufferList = unsafe { &(*ptr) };
|
||||
let ptr = list.mBuffers.as_ptr() as *const AudioBuffer;
|
||||
let len = list.mNumberBuffers as usize;
|
||||
if len == 0 {
|
||||
return 0;
|
||||
}
|
||||
let buffers = unsafe { slice::from_raw_parts(ptr, len) };
|
||||
for buffer in buffers {
|
||||
count += buffer.mNumberChannels;
|
||||
}
|
||||
}
|
||||
let mut size: usize = 0;
|
||||
let r = audio_object_get_property_data_size(devid, &adr, &mut size);
|
||||
if r != NO_ERR {
|
||||
return Err(Error::error());
|
||||
}
|
||||
count
|
||||
assert_ne!(size, 0);
|
||||
|
||||
let mut data: Vec<u8> = allocate_array_by_size(size);
|
||||
let ptr = data.as_mut_ptr() as *mut AudioBufferList;
|
||||
let r = audio_object_get_property_data(devid, &adr, &mut size, ptr);
|
||||
if r != NO_ERR {
|
||||
return Err(Error::error());
|
||||
}
|
||||
|
||||
let list = unsafe { &(*ptr) };
|
||||
let ptr = list.mBuffers.as_ptr() as *const AudioBuffer;
|
||||
let len = list.mNumberBuffers as usize;
|
||||
|
||||
let mut count = 0;
|
||||
let buffers = unsafe { slice::from_raw_parts(ptr, len) };
|
||||
for buffer in buffers {
|
||||
count += buffer.mNumberChannels;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn audiounit_get_available_samplerate(
|
||||
devid: AudioObjectID,
|
||||
scope: AudioObjectPropertyScope,
|
||||
devtype: DeviceType,
|
||||
min: &mut u32,
|
||||
max: &mut u32,
|
||||
def: &mut u32,
|
||||
) {
|
||||
const GLOBAL: ffi::cubeb_device_type =
|
||||
ffi::CUBEB_DEVICE_TYPE_INPUT | ffi::CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
let mut adr = AudioObjectPropertyAddress {
|
||||
mSelector: 0,
|
||||
mScope: scope,
|
||||
mScope: match devtype.bits() {
|
||||
ffi::CUBEB_DEVICE_TYPE_INPUT => kAudioDevicePropertyScopeInput,
|
||||
ffi::CUBEB_DEVICE_TYPE_OUTPUT => kAudioDevicePropertyScopeOutput,
|
||||
GLOBAL => kAudioObjectPropertyScopeGlobal,
|
||||
_ => panic!("Invalid type"),
|
||||
},
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
|
||||
@ -1664,13 +1630,17 @@ fn audiounit_get_available_samplerate(
|
||||
}
|
||||
}
|
||||
|
||||
fn audiounit_get_device_presentation_latency(
|
||||
devid: AudioObjectID,
|
||||
scope: AudioObjectPropertyScope,
|
||||
) -> u32 {
|
||||
fn audiounit_get_device_presentation_latency(devid: AudioObjectID, devtype: DeviceType) -> u32 {
|
||||
const GLOBAL: ffi::cubeb_device_type =
|
||||
ffi::CUBEB_DEVICE_TYPE_INPUT | ffi::CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
let mut adr = AudioObjectPropertyAddress {
|
||||
mSelector: 0,
|
||||
mScope: scope,
|
||||
mScope: match devtype.bits() {
|
||||
ffi::CUBEB_DEVICE_TYPE_INPUT => kAudioDevicePropertyScopeInput,
|
||||
ffi::CUBEB_DEVICE_TYPE_OUTPUT => kAudioDevicePropertyScopeOutput,
|
||||
GLOBAL => kAudioObjectPropertyScopeGlobal,
|
||||
_ => panic!("Invalid type"),
|
||||
},
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let mut size: usize = 0;
|
||||
@ -1696,109 +1666,76 @@ fn audiounit_get_device_presentation_latency(
|
||||
dev + stream
|
||||
}
|
||||
|
||||
fn audiounit_create_device_from_hwdev(
|
||||
dev_info: &mut ffi::cubeb_device_info,
|
||||
fn create_cubeb_device_info(
|
||||
devid: AudioObjectID,
|
||||
devtype: DeviceType,
|
||||
) -> Result<()> {
|
||||
assert!(devtype == DeviceType::INPUT || devtype == DeviceType::OUTPUT);
|
||||
|
||||
let mut adr = AudioObjectPropertyAddress {
|
||||
mSelector: 0,
|
||||
mScope: 0,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let mut size: usize = 0;
|
||||
|
||||
adr.mScope = if devtype == DeviceType::OUTPUT {
|
||||
kAudioDevicePropertyScopeOutput
|
||||
} else {
|
||||
kAudioDevicePropertyScopeInput
|
||||
};
|
||||
|
||||
let ch = audiounit_get_channel_count(devid, adr.mScope);
|
||||
if ch == 0 {
|
||||
) -> Result<ffi::cubeb_device_info> {
|
||||
let channels = get_channel_count(devid, devtype)?;
|
||||
if channels == 0 {
|
||||
// Invalid type for this device.
|
||||
return Err(Error::error());
|
||||
}
|
||||
|
||||
*dev_info = ffi::cubeb_device_info::default();
|
||||
let mut dev_info = ffi::cubeb_device_info::default();
|
||||
dev_info.max_channels = channels;
|
||||
|
||||
let mut device_id_str: CFStringRef = ptr::null();
|
||||
size = mem::size_of::<CFStringRef>();
|
||||
adr.mSelector = kAudioDevicePropertyDeviceUID;
|
||||
let mut ret = audio_object_get_property_data(devid, &adr, &mut size, &mut device_id_str);
|
||||
if ret == NO_ERR && !device_id_str.is_null() {
|
||||
let c_string = audiounit_strref_to_cstr_utf8(device_id_str);
|
||||
dev_info.device_id = c_string.into_raw();
|
||||
assert!(
|
||||
mem::size_of::<ffi::cubeb_devid>() >= mem::size_of_val(&devid),
|
||||
"cubeb_devid can't represent devid"
|
||||
);
|
||||
dev_info.devid = devid as ffi::cubeb_devid;
|
||||
|
||||
assert!(
|
||||
mem::size_of::<ffi::cubeb_devid>() >= mem::size_of_val(&devid),
|
||||
"cubeb_devid can't represent devid"
|
||||
);
|
||||
dev_info.devid = devid as ffi::cubeb_devid;
|
||||
|
||||
dev_info.group_id = dev_info.device_id;
|
||||
|
||||
unsafe {
|
||||
CFRelease(device_id_str as *const c_void);
|
||||
match get_device_uid(devid, devtype) {
|
||||
Ok(uid) => {
|
||||
let c_string = uid.into_cstring();
|
||||
dev_info.device_id = c_string.into_raw();
|
||||
dev_info.group_id = dev_info.device_id;
|
||||
}
|
||||
Err(e) => {
|
||||
cubeb_log!(
|
||||
"Cannot get the uid for device {} in {:?} scope. Error: {}",
|
||||
devid,
|
||||
devtype,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut friendly_name_str: CFStringRef = ptr::null();
|
||||
let mut ds: u32 = 0;
|
||||
size = mem::size_of::<u32>();
|
||||
adr.mSelector = kAudioDevicePropertyDataSource;
|
||||
ret = audio_object_get_property_data(devid, &adr, &mut size, &mut ds);
|
||||
if ret == NO_ERR {
|
||||
let mut trl = AudioValueTranslation {
|
||||
mInputData: &mut ds as *mut u32 as *mut c_void,
|
||||
mInputDataSize: mem::size_of_val(&ds) as u32,
|
||||
mOutputData: &mut friendly_name_str as *mut CFStringRef as *mut c_void,
|
||||
mOutputDataSize: mem::size_of::<CFStringRef>() as u32,
|
||||
};
|
||||
adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
|
||||
size = mem::size_of::<AudioValueTranslation>();
|
||||
audio_object_get_property_data(devid, &adr, &mut size, &mut trl);
|
||||
}
|
||||
|
||||
// If there is no datasource for this device, fall back to the
|
||||
// device name.
|
||||
if friendly_name_str.is_null() {
|
||||
size = mem::size_of::<CFStringRef>();
|
||||
adr.mSelector = kAudioObjectPropertyName;
|
||||
audio_object_get_property_data(devid, &adr, &mut size, &mut friendly_name_str);
|
||||
}
|
||||
|
||||
if friendly_name_str.is_null() {
|
||||
// Couldn't get a datasource name nor a device name, return a
|
||||
// valid string of length 0.
|
||||
let c_string = CString::default();
|
||||
dev_info.friendly_name = c_string.into_raw();
|
||||
} else {
|
||||
let c_string = audiounit_strref_to_cstr_utf8(friendly_name_str);
|
||||
dev_info.friendly_name = c_string.into_raw();
|
||||
unsafe {
|
||||
CFRelease(friendly_name_str as *const c_void);
|
||||
let label = match get_device_label(devid, devtype) {
|
||||
Ok(label) => label.into_cstring(),
|
||||
Err(e) => {
|
||||
cubeb_log!(
|
||||
"Cannot get the label for device {} in {:?} scope. Error: {}",
|
||||
devid,
|
||||
devtype,
|
||||
e
|
||||
);
|
||||
CString::default()
|
||||
}
|
||||
};
|
||||
dev_info.friendly_name = label.into_raw();
|
||||
|
||||
let mut vendor_name_str: CFStringRef = ptr::null();
|
||||
size = mem::size_of::<CFStringRef>();
|
||||
adr.mSelector = kAudioObjectPropertyManufacturer;
|
||||
ret = audio_object_get_property_data(devid, &adr, &mut size, &mut vendor_name_str);
|
||||
if ret == NO_ERR && !vendor_name_str.is_null() {
|
||||
let c_string = audiounit_strref_to_cstr_utf8(vendor_name_str);
|
||||
dev_info.vendor_name = c_string.into_raw();
|
||||
unsafe {
|
||||
CFRelease(vendor_name_str as *const c_void);
|
||||
match get_device_manufacturer(devid, devtype) {
|
||||
Ok(vendor) => {
|
||||
let vendor = vendor.into_cstring();
|
||||
dev_info.vendor_name = vendor.into_raw();
|
||||
}
|
||||
Err(e) => {
|
||||
cubeb_log!(
|
||||
"Cannot get the manufacturer for device {} in {:?} scope. Error: {}",
|
||||
devid,
|
||||
devtype,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
dev_info.device_type = if devtype == DeviceType::OUTPUT {
|
||||
ffi::CUBEB_DEVICE_TYPE_OUTPUT
|
||||
} else {
|
||||
ffi::CUBEB_DEVICE_TYPE_INPUT
|
||||
dev_info.device_type = match devtype {
|
||||
DeviceType::INPUT => ffi::CUBEB_DEVICE_TYPE_INPUT,
|
||||
DeviceType::OUTPUT => ffi::CUBEB_DEVICE_TYPE_OUTPUT,
|
||||
_ => panic!("invalid type"),
|
||||
};
|
||||
|
||||
dev_info.state = ffi::CUBEB_DEVICE_STATE_ENABLED;
|
||||
dev_info.preferred = if devid == audiounit_get_default_device_id(devtype) {
|
||||
ffi::CUBEB_DEVICE_PREF_ALL
|
||||
@ -1806,31 +1743,32 @@ fn audiounit_create_device_from_hwdev(
|
||||
ffi::CUBEB_DEVICE_PREF_NONE
|
||||
};
|
||||
|
||||
dev_info.max_channels = ch;
|
||||
dev_info.format = ffi::CUBEB_DEVICE_FMT_ALL;
|
||||
dev_info.default_format = ffi::CUBEB_DEVICE_FMT_F32NE;
|
||||
audiounit_get_available_samplerate(
|
||||
devid,
|
||||
adr.mScope,
|
||||
devtype,
|
||||
&mut dev_info.min_rate,
|
||||
&mut dev_info.max_rate,
|
||||
&mut dev_info.default_rate,
|
||||
);
|
||||
|
||||
let latency = audiounit_get_device_presentation_latency(devid, adr.mScope);
|
||||
let mut range = AudioValueRange::default();
|
||||
adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
|
||||
size = mem::size_of::<AudioValueRange>();
|
||||
ret = audio_object_get_property_data(devid, &adr, &mut size, &mut range);
|
||||
if ret == NO_ERR {
|
||||
dev_info.latency_lo = latency + range.mMinimum as u32;
|
||||
dev_info.latency_hi = latency + range.mMaximum as u32;
|
||||
} else {
|
||||
dev_info.latency_lo = 10 * dev_info.default_rate / 1000; // Default to 10ms
|
||||
dev_info.latency_hi = 100 * dev_info.default_rate / 1000; // Default to 10ms
|
||||
}
|
||||
let latency = audiounit_get_device_presentation_latency(devid, devtype);
|
||||
|
||||
Ok(())
|
||||
let (latency_low, latency_high) = match get_device_buffer_frame_size_range(devid, devtype) {
|
||||
Ok((min, max)) => (latency + min as u32, latency + max as u32),
|
||||
Err(e) => {
|
||||
cubeb_log!("Cannot get the buffer frame size for device {} in {:?} scope. Use default value instead. Error: {}", devid, devtype, e);
|
||||
(
|
||||
10 * dev_info.default_rate / 1000,
|
||||
100 * dev_info.default_rate / 1000,
|
||||
)
|
||||
}
|
||||
};
|
||||
dev_info.latency_lo = latency_low;
|
||||
dev_info.latency_hi = latency_high;
|
||||
|
||||
Ok(dev_info)
|
||||
}
|
||||
|
||||
fn is_aggregate_device(device_info: &ffi::cubeb_device_info) -> bool {
|
||||
@ -1900,16 +1838,12 @@ fn audiounit_get_devices_of_type(devtype: DeviceType) -> Vec<AudioObjectID> {
|
||||
|
||||
// Remove the aggregate device from the list of devices (if any).
|
||||
devices.retain(|&device| {
|
||||
let name = get_device_name(device);
|
||||
if name.is_null() {
|
||||
return true;
|
||||
}
|
||||
let private_device = cfstringref_from_static_string(PRIVATE_AGGREGATE_DEVICE_NAME);
|
||||
unsafe {
|
||||
let found = CFStringFind(name, private_device, 0).location;
|
||||
CFRelease(private_device as *const c_void);
|
||||
CFRelease(name as *const c_void);
|
||||
found == kCFNotFound
|
||||
if let Ok(uid) = get_device_global_uid(device) {
|
||||
let uid = uid.into_string();
|
||||
uid != PRIVATE_AGGREGATE_DEVICE_NAME
|
||||
} else {
|
||||
// Fail to get device uid.
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
@ -1919,14 +1853,9 @@ fn audiounit_get_devices_of_type(devtype: DeviceType) -> Vec<AudioObjectID> {
|
||||
return devices;
|
||||
}
|
||||
|
||||
let scope = if devtype == DeviceType::INPUT {
|
||||
kAudioDevicePropertyScopeInput
|
||||
} else {
|
||||
kAudioDevicePropertyScopeOutput
|
||||
};
|
||||
let mut devices_in_scope = Vec::new();
|
||||
for device in devices {
|
||||
if audiounit_get_channel_count(device, scope) > 0 {
|
||||
if get_channel_count(device, devtype).unwrap() > 0 {
|
||||
devices_in_scope.push(device);
|
||||
}
|
||||
}
|
||||
@ -2331,64 +2260,29 @@ impl ContextOps for AudioUnitContext {
|
||||
devtype: DeviceType,
|
||||
collection: &DeviceCollectionRef,
|
||||
) -> Result<()> {
|
||||
let input_devs = if devtype.contains(DeviceType::INPUT) {
|
||||
audiounit_get_devices_of_type(DeviceType::INPUT)
|
||||
} else {
|
||||
Vec::<AudioObjectID>::new()
|
||||
};
|
||||
|
||||
let output_devs = if devtype.contains(DeviceType::OUTPUT) {
|
||||
audiounit_get_devices_of_type(DeviceType::OUTPUT)
|
||||
} else {
|
||||
Vec::<AudioObjectID>::new()
|
||||
};
|
||||
|
||||
// Count number of input and output devices. This is not necessarily the same as
|
||||
// the count of raw devices supported by the system since, for example, with Soundflower
|
||||
// installed, some devices may report as being both input *and* output and cubeb
|
||||
// separates those into two different devices.
|
||||
|
||||
let mut devices: Vec<ffi::cubeb_device_info> =
|
||||
allocate_array(output_devs.len() + input_devs.len());
|
||||
|
||||
let mut count = 0;
|
||||
if devtype.contains(DeviceType::OUTPUT) {
|
||||
for dev in output_devs {
|
||||
let device = &mut devices[count];
|
||||
if audiounit_create_device_from_hwdev(device, dev, DeviceType::OUTPUT).is_err()
|
||||
|| is_aggregate_device(device)
|
||||
{
|
||||
continue;
|
||||
let mut device_infos = Vec::new();
|
||||
let dev_types = [DeviceType::INPUT, DeviceType::OUTPUT];
|
||||
for dev_type in dev_types.iter() {
|
||||
if !devtype.contains(*dev_type) {
|
||||
continue;
|
||||
}
|
||||
let devices = audiounit_get_devices_of_type(*dev_type);
|
||||
for device in devices {
|
||||
if let Ok(info) = create_cubeb_device_info(device, *dev_type) {
|
||||
if !is_aggregate_device(&info) {
|
||||
device_infos.push(info);
|
||||
}
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if devtype.contains(DeviceType::INPUT) {
|
||||
for dev in input_devs {
|
||||
let device = &mut devices[count];
|
||||
if audiounit_create_device_from_hwdev(device, dev, DeviceType::INPUT).is_err()
|
||||
|| is_aggregate_device(device)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the redundant space, set len to count.
|
||||
devices.truncate(count);
|
||||
|
||||
let (ptr, len) = if device_infos.is_empty() {
|
||||
(ptr::null_mut(), 0)
|
||||
} else {
|
||||
forget_vec(device_infos)
|
||||
};
|
||||
let coll = unsafe { &mut *collection.as_ptr() };
|
||||
if count > 0 {
|
||||
let (ptr, len) = forget_vec(devices);
|
||||
coll.device = ptr;
|
||||
coll.count = len;
|
||||
} else {
|
||||
coll.device = ptr::null_mut();
|
||||
coll.count = 0;
|
||||
}
|
||||
|
||||
coll.device = ptr;
|
||||
coll.count = len;
|
||||
Ok(())
|
||||
}
|
||||
fn device_collection_destroy(&mut self, collection: &mut DeviceCollectionRef) -> Result<()> {
|
||||
@ -2556,7 +2450,7 @@ struct CoreStreamData<'ctx> {
|
||||
device_layout: ChannelLayout,
|
||||
// Hold the input samples in every input callback iteration.
|
||||
// Only accessed on input/output callback thread and during initial configure.
|
||||
input_linear_buffer: Option<Box<AutoArrayWrapper>>,
|
||||
input_linear_buffer: Option<Box<dyn AutoArrayWrapper>>,
|
||||
// Listeners indicating what system events are monitored.
|
||||
default_input_listener: Option<device_property_listener>,
|
||||
default_output_listener: Option<device_property_listener>,
|
||||
@ -3066,7 +2960,7 @@ impl<'ctx> CoreStreamData<'ctx> {
|
||||
stream.current_latency_frames.store(
|
||||
audiounit_get_device_presentation_latency(
|
||||
self.output_device.id,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
DeviceType::OUTPUT,
|
||||
),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
|
@ -62,16 +62,8 @@ namespace recordreplay {
|
||||
(const PLDHashTableOps* aFirstOps, \
|
||||
const PLDHashTableOps* aSecondOps), \
|
||||
(aFirstOps, aSecondOps)) \
|
||||
Macro(SetWeakPointerJSRoot, \
|
||||
(const void* aPtr, JSObject* aJSObj), (aPtr, aJSObj)) \
|
||||
Macro(RegisterTrigger, \
|
||||
(void* aObj, const std::function<void()>& aCallback), \
|
||||
(aObj, \
|
||||
aCallback)) Macro(UnregisterTrigger, (void* aObj), \
|
||||
(aObj)) Macro(ActivateTrigger, \
|
||||
(void* aObj), (aObj)) \
|
||||
Macro(ExecuteTriggers, (), ()) Macro( \
|
||||
InternalRecordReplayAssert, \
|
||||
Macro(InternalHoldJSObject, (JSObject* aJSObj), (aJSObj)) \
|
||||
Macro(InternalRecordReplayAssert, \
|
||||
(const char* aFormat, va_list aArgs), \
|
||||
(aFormat, \
|
||||
aArgs)) Macro(InternalRecordReplayAssertBytes, \
|
||||
|
@ -187,54 +187,11 @@ static inline void DestroyPLDHashTableCallbacks(const PLDHashTableOps* aOps);
|
||||
static inline void MovePLDHashTableContents(const PLDHashTableOps* aFirstOps,
|
||||
const PLDHashTableOps* aSecondOps);
|
||||
|
||||
// Associate an arbitrary pointer with a JS object root while replaying. This
|
||||
// is useful for replaying the behavior of weak pointers.
|
||||
MFBT_API void SetWeakPointerJSRoot(const void* aPtr, JSObject* aJSObj);
|
||||
|
||||
// API for ensuring that a function executes at a consistent point when
|
||||
// recording or replaying. This is primarily needed for finalizers and other
|
||||
// activity during a GC that can perform recorded events (because GCs can
|
||||
// occur at different times and behave differently between recording and
|
||||
// replay, thread events are disallowed during a GC). Triggers can be
|
||||
// registered at a point where thread events are allowed, then activated at
|
||||
// a point where thread events are not allowed. When recording, the trigger's
|
||||
// callback will execute at the next point when ExecuteTriggers is called on
|
||||
// the thread which originally registered the trigger (typically at the top of
|
||||
// the thread's event loop), and when replaying the callback will execute at
|
||||
// the same point, even if it was never activated.
|
||||
//
|
||||
// Below is an example of how this API can be used.
|
||||
//
|
||||
// // This structure's lifetime is managed by the GC.
|
||||
// struct GarbageCollectedHolder {
|
||||
// GarbageCollectedHolder() {
|
||||
// RegisterTrigger(this, [=]() { this->DestroyContents(); });
|
||||
// }
|
||||
// ~GarbageCollectedHolder() {
|
||||
// UnregisterTrigger(this);
|
||||
// }
|
||||
//
|
||||
// void Finalize() {
|
||||
// // During finalization, thread events are disallowed.
|
||||
// if (IsRecordingOrReplaying()) {
|
||||
// ActivateTrigger(this);
|
||||
// } else {
|
||||
// DestroyContents();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // This is free to release resources held by the system, communicate with
|
||||
// // other threads or processes, and so forth. When replaying, this may
|
||||
// // be called before the GC has actually collected this object, but since
|
||||
// // the GC will have already collected this object at this point in the
|
||||
// // recording, this object will never be accessed again.
|
||||
// void DestroyContents();
|
||||
// };
|
||||
MFBT_API void RegisterTrigger(void* aObj,
|
||||
const std::function<void()>& aCallback);
|
||||
MFBT_API void UnregisterTrigger(void* aObj);
|
||||
MFBT_API void ActivateTrigger(void* aObj);
|
||||
MFBT_API void ExecuteTriggers();
|
||||
// Prevent a JS object from ever being collected while recording or replaying.
|
||||
// GC behavior is non-deterministic when recording/replaying, and preventing
|
||||
// an object from being collected ensures that finalizers which might interact
|
||||
// with the recording will not execute.
|
||||
static inline void HoldJSObject(JSObject* aJSObj);
|
||||
|
||||
// Some devtools operations which execute in a replaying process can cause code
|
||||
// to run which did not run while recording. For example, the JS debugger can
|
||||
@ -419,15 +376,8 @@ MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(MovePLDHashTableContents,
|
||||
(aFirstOps, aSecondOps))
|
||||
MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(InvalidateRecording, (const char* aWhy),
|
||||
(aWhy))
|
||||
MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(
|
||||
RegisterWeakPointer,
|
||||
(const void* aPtr, const std::function<void(bool)>& aCallback),
|
||||
(aPtr, aCallback))
|
||||
MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(UnregisterWeakPointer, (const void* aPtr),
|
||||
(aPtr))
|
||||
MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(WeakPointerAccess,
|
||||
(const void* aPtr, bool aSuccess),
|
||||
(aPtr, aSuccess))
|
||||
MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(HoldJSObject, (JSObject* aObject),
|
||||
(aObject))
|
||||
MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(RecordReplayAssertBytes,
|
||||
(const void* aData, size_t aSize),
|
||||
(aData, aSize))
|
||||
|
@ -27,9 +27,17 @@ public class TelemetryActivationPingDelegate extends BrowserAppDelegate {
|
||||
private TelemetryDispatcher telemetryDispatcher; // lazy
|
||||
|
||||
|
||||
// We don't upload in onCreate because that's only called when the Activity needs to be instantiated
|
||||
// and it's possible the system will never free the Activity from memory.
|
||||
//
|
||||
// We don't upload in onResume/onPause because that will be called each time the Activity is obscured,
|
||||
// including by our own Activities/dialogs, and there is no reason to upload each time we're unobscured.
|
||||
//
|
||||
// We're left with onStart/onStop and we upload in onStart because onStop is not guaranteed to be called
|
||||
// and we want to upload the first run ASAP (e.g. to get install data before the app may crash).
|
||||
@Override
|
||||
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
|
||||
super.onCreate(browserApp, savedInstanceState);
|
||||
public void onStart(BrowserApp browserApp) {
|
||||
super.onStart(browserApp);
|
||||
uploadActivationPing(browserApp);
|
||||
}
|
||||
|
||||
|
@ -4880,6 +4880,17 @@
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Prefs starting with "mathml."
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# Whether to disable legacy names "thin", "thick" and "medium" for the
|
||||
# linethickness attribute of the mfrac element.
|
||||
- name: mathml.mfrac_linethickness_names.disabled
|
||||
type: bool
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Prefs starting with "media."
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -54,6 +54,7 @@ pref_groups = [
|
||||
'keyword',
|
||||
'layers',
|
||||
'layout',
|
||||
'mathml',
|
||||
'media',
|
||||
'mousewheel',
|
||||
'network',
|
||||
|
@ -36,7 +36,6 @@ from mozbuild.frontend.data import (
|
||||
IPDLCollection,
|
||||
LocalizedPreprocessedFiles,
|
||||
LocalizedFiles,
|
||||
RustLibrary,
|
||||
SharedLibrary,
|
||||
StaticLibrary,
|
||||
UnifiedSources,
|
||||
@ -252,7 +251,7 @@ class CommonBackend(BuildBackend):
|
||||
|
||||
def expand(lib, recurse_objs, system_libs):
|
||||
if isinstance(lib, (HostLibrary, StaticLibrary)):
|
||||
if not isinstance(lib, HostLibrary) and lib.no_expand_lib:
|
||||
if lib.no_expand_lib:
|
||||
static_libs.append(lib)
|
||||
recurse_objs = False
|
||||
elif recurse_objs:
|
||||
@ -276,9 +275,7 @@ class CommonBackend(BuildBackend):
|
||||
|
||||
system_libs = not isinstance(input_bin, (HostLibrary, StaticLibrary))
|
||||
for lib in input_bin.linked_libraries:
|
||||
if isinstance(lib, RustLibrary):
|
||||
continue
|
||||
elif isinstance(lib, (HostLibrary, StaticLibrary)):
|
||||
if isinstance(lib, (HostLibrary, StaticLibrary)):
|
||||
expand(lib, True, system_libs)
|
||||
elif isinstance(lib, SharedLibrary):
|
||||
if lib not in seen_libs:
|
||||
|
@ -31,6 +31,7 @@ from .common import CommonBackend
|
||||
from ..frontend.data import (
|
||||
BaseLibrary,
|
||||
BaseProgram,
|
||||
BaseRustLibrary,
|
||||
ChromeManifestEntry,
|
||||
ComputedFlags,
|
||||
ConfigFileSubstitution,
|
||||
@ -59,9 +60,7 @@ from ..frontend.data import (
|
||||
ObjdirPreprocessedFiles,
|
||||
PerSourceFlag,
|
||||
Program,
|
||||
RustLibrary,
|
||||
HostSharedLibrary,
|
||||
HostRustLibrary,
|
||||
RustProgram,
|
||||
RustTests,
|
||||
SharedLibrary,
|
||||
@ -479,6 +478,8 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
f = mozpath.relpath(f, base)
|
||||
for var in variables:
|
||||
backend_file.write('%s += %s\n' % (var, f))
|
||||
self._compile_graph[mozpath.join(
|
||||
backend_file.relobjdir, 'target-objects')]
|
||||
elif isinstance(obj, (HostSources, HostGeneratedSources)):
|
||||
suffix_map = {
|
||||
'.c': 'HOST_CSRCS',
|
||||
@ -495,6 +496,8 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
f = mozpath.relpath(f, base)
|
||||
for var in variables:
|
||||
backend_file.write('%s += %s\n' % (var, f))
|
||||
self._compile_graph[mozpath.join(
|
||||
backend_file.relobjdir, 'host-objects')]
|
||||
elif isinstance(obj, VariablePassthru):
|
||||
# Sorted so output is consistent and we don't bump mtimes.
|
||||
for k, v in sorted(obj.variables.items()):
|
||||
@ -678,7 +681,7 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
elif isinstance(obj, InstallationTarget):
|
||||
self._process_installation_target(obj, backend_file)
|
||||
|
||||
elif isinstance(obj, RustLibrary):
|
||||
elif isinstance(obj, BaseRustLibrary):
|
||||
self.backend_input_files.add(obj.cargo_file)
|
||||
self._process_rust_library(obj, backend_file)
|
||||
# No need to call _process_linked_libraries, because Rust
|
||||
@ -840,38 +843,19 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
# rest of the compile graph.
|
||||
target_name = mozpath.basename(root)
|
||||
|
||||
if target_name not in ('target', 'target-shared', 'host'):
|
||||
if target_name not in ('target', 'target-objects',
|
||||
'host', 'host-objects'):
|
||||
non_default_roots[target_name].append(root)
|
||||
non_default_graphs[target_name][root] = self._compile_graph[root]
|
||||
del self._compile_graph[root]
|
||||
|
||||
targets_per_directory = defaultdict(list)
|
||||
for target in self._compile_graph.iterkeys():
|
||||
targets_per_directory[mozpath.dirname(target)].append(
|
||||
mozpath.basename(target))
|
||||
|
||||
# In directories that have both a shared and a static library, there
|
||||
# may also be source files that are applied to both. What can happen
|
||||
# then is that both the shared and static library are attempted to be
|
||||
# built in parallel via other dependencies, and both try to build the
|
||||
# same source files at the same time, and that can lead to race
|
||||
# conditions and build failures. This means we need an implicit
|
||||
# dependency between the shared and the static library that the library
|
||||
# graph doesn't necessarily know about, so we encode it in the compile
|
||||
# graph manually.
|
||||
for directory, targets in targets_per_directory.iteritems():
|
||||
if 'target-shared' in targets and 'target' in targets:
|
||||
self._compile_graph[mozpath.join(directory, 'target-shared')].add(
|
||||
mozpath.join(directory, 'target'))
|
||||
|
||||
for root in chain(*non_default_roots.values()):
|
||||
compile_roots.remove(root)
|
||||
dirname = mozpath.dirname(root)
|
||||
# If a directory only contains non-default compile targets, we don't
|
||||
# attempt to dump symbols there.
|
||||
if (dirname in self._no_skip['syms'] and
|
||||
'%s/target' % dirname not in self._compile_graph and
|
||||
'%s/target-shared' % dirname not in self._compile_graph):
|
||||
'%s/target' % dirname not in self._compile_graph):
|
||||
self._no_skip['syms'].remove(dirname)
|
||||
|
||||
add_category_rules('compile', compile_roots, self._compile_graph)
|
||||
@ -1088,6 +1072,9 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
backend_file.write('%s += %s\n' % (
|
||||
non_unified_var, ' '.join(source_files)))
|
||||
|
||||
self._compile_graph[mozpath.join(
|
||||
backend_file.relobjdir, 'target-objects')]
|
||||
|
||||
def _process_directory_traversal(self, obj, backend_file):
|
||||
"""Process a data.DirectoryTraversal instance."""
|
||||
fh = backend_file.fh
|
||||
@ -1389,9 +1376,6 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
def _build_target_for_obj(self, obj):
|
||||
if hasattr(obj, 'output_category') and obj.output_category:
|
||||
target_name = obj.output_category
|
||||
elif isinstance(obj, SharedLibrary):
|
||||
assert obj.KIND == 'target'
|
||||
target_name = 'target-shared'
|
||||
else:
|
||||
target_name = obj.KIND
|
||||
return '%s/%s' % (mozpath.relpath(obj.objdir,
|
||||
@ -1402,10 +1386,6 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
return os.path.normpath(mozpath.join(mozpath.relpath(lib.objdir, obj.objdir),
|
||||
name))
|
||||
|
||||
# This will create the node even if there aren't any linked libraries.
|
||||
build_target = self._build_target_for_obj(obj)
|
||||
self._compile_graph[build_target]
|
||||
|
||||
objs, no_pgo_objs, shared_libs, os_libs, static_libs = self._expand_libs(obj)
|
||||
|
||||
obj_target = obj.name
|
||||
@ -1475,54 +1455,51 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
write_obj_deps(obj_target, objs_ref, pgo_objs_ref)
|
||||
|
||||
for lib in shared_libs:
|
||||
assert obj.KIND != 'host'
|
||||
backend_file.write_once('SHARED_LIBS += %s\n' %
|
||||
pretty_relpath(lib, lib.import_name))
|
||||
for lib in static_libs:
|
||||
backend_file.write_once('STATIC_LIBS += %s\n' %
|
||||
pretty_relpath(lib, lib.import_name))
|
||||
|
||||
# We have to link any Rust libraries after all intermediate static
|
||||
# libraries have been listed to ensure that the Rust libraries are
|
||||
# searched after the C/C++ objects that might reference Rust symbols.
|
||||
var = 'HOST_LIBS' if obj.KIND == 'host' else 'STATIC_LIBS'
|
||||
for lib in chain(
|
||||
(l for l in static_libs if not isinstance(l, BaseRustLibrary)),
|
||||
(l for l in static_libs if isinstance(l, BaseRustLibrary)),
|
||||
):
|
||||
backend_file.write_once('%s += %s\n' %
|
||||
(var, pretty_relpath(lib, lib.import_name)))
|
||||
|
||||
for lib in os_libs:
|
||||
if obj.KIND == 'target':
|
||||
backend_file.write_once('OS_LIBS += %s\n' % lib)
|
||||
else:
|
||||
backend_file.write_once('HOST_EXTRA_LIBS += %s\n' % lib)
|
||||
|
||||
for lib in obj.linked_libraries:
|
||||
if not isinstance(lib, ExternalLibrary):
|
||||
self._compile_graph[build_target].add(
|
||||
self._build_target_for_obj(lib))
|
||||
if isinstance(lib, HostRustLibrary):
|
||||
backend_file.write_once('HOST_LIBS += %s\n' %
|
||||
pretty_relpath(lib, lib.import_name))
|
||||
if not isinstance(obj, (StaticLibrary, HostLibrary)) or obj.no_expand_lib:
|
||||
# This will create the node even if there aren't any linked libraries.
|
||||
build_target = self._build_target_for_obj(obj)
|
||||
self._compile_graph[build_target]
|
||||
|
||||
# We have to link any Rust libraries after all intermediate static
|
||||
# libraries have been listed to ensure that the Rust libraries are
|
||||
# searched after the C/C++ objects that might reference Rust symbols.
|
||||
if isinstance(obj, (SharedLibrary, Program)):
|
||||
self._process_rust_libraries(obj, backend_file, pretty_relpath)
|
||||
# Make the build target depend on all the target/host-objects that
|
||||
# recursively are linked into it.
|
||||
def recurse_libraries(obj):
|
||||
for lib in obj.linked_libraries:
|
||||
if isinstance(lib, (StaticLibrary, HostLibrary)) and not lib.no_expand_lib:
|
||||
recurse_libraries(lib)
|
||||
elif not isinstance(lib, ExternalLibrary):
|
||||
self._compile_graph[build_target].add(
|
||||
self._build_target_for_obj(lib))
|
||||
relobjdir = mozpath.relpath(obj.objdir, self.environment.topobjdir)
|
||||
objects_target = mozpath.join(relobjdir, '%s-objects' % obj.KIND)
|
||||
if objects_target in self._compile_graph:
|
||||
self._compile_graph[build_target].add(objects_target)
|
||||
|
||||
recurse_libraries(obj)
|
||||
|
||||
# Process library-based defines
|
||||
self._process_defines(obj.lib_defines, backend_file)
|
||||
|
||||
def _process_rust_libraries(self, obj, backend_file, pretty_relpath):
|
||||
assert isinstance(obj, (SharedLibrary, Program))
|
||||
|
||||
# If this library does not depend on any Rust libraries, then we are done.
|
||||
direct_linked = [l for l in obj.linked_libraries if isinstance(l, RustLibrary)]
|
||||
if not direct_linked:
|
||||
return
|
||||
|
||||
# We should have already checked this in Linkable.link_library.
|
||||
assert len(direct_linked) == 1
|
||||
|
||||
# TODO: see bug 1310063 for checking dependencies are set up correctly.
|
||||
|
||||
direct_linked = direct_linked[0]
|
||||
backend_file.write('RUST_STATIC_LIB := %s\n' %
|
||||
pretty_relpath(direct_linked, direct_linked.import_name))
|
||||
|
||||
for lib in direct_linked.linked_system_libs:
|
||||
backend_file.write_once('OS_LIBS += %s\n' % lib)
|
||||
|
||||
def _process_final_target_files(self, obj, files, backend_file):
|
||||
target = obj.install_target
|
||||
path = mozpath.basedir(target, (
|
||||
|
@ -395,10 +395,6 @@ class LinkageWrongKindError(Exception):
|
||||
"""Error thrown when trying to link objects of the wrong kind"""
|
||||
|
||||
|
||||
class LinkageMultipleRustLibrariesError(Exception):
|
||||
"""Error thrown when trying to link multiple Rust libraries to an object"""
|
||||
|
||||
|
||||
class Linkable(ContextDerived):
|
||||
"""Generic context derived container object for programs and libraries"""
|
||||
__slots__ = (
|
||||
@ -425,13 +421,6 @@ class Linkable(ContextDerived):
|
||||
assert isinstance(obj, BaseLibrary)
|
||||
if obj.KIND != self.KIND:
|
||||
raise LinkageWrongKindError('%s != %s' % (obj.KIND, self.KIND))
|
||||
# Linking multiple Rust libraries into an object would result in
|
||||
# multiple copies of the Rust standard library, as well as linking
|
||||
# errors from duplicate symbols.
|
||||
if isinstance(obj, RustLibrary) and any(isinstance(l, RustLibrary)
|
||||
for l in self.linked_libraries):
|
||||
raise LinkageMultipleRustLibrariesError("Cannot link multiple Rust libraries into %s"
|
||||
% self)
|
||||
self.linked_libraries.append(obj)
|
||||
if obj.cxx_link and not isinstance(obj, SharedLibrary):
|
||||
self.cxx_link = True
|
||||
@ -681,9 +670,8 @@ class StaticLibrary(Library):
|
||||
self.no_expand_lib = no_expand_lib
|
||||
|
||||
|
||||
class RustLibrary(StaticLibrary):
|
||||
"""Context derived container object for a static library"""
|
||||
__slots__ = (
|
||||
class BaseRustLibrary(object):
|
||||
slots = (
|
||||
'cargo_file',
|
||||
'crate_type',
|
||||
'dependencies',
|
||||
@ -692,13 +680,9 @@ class RustLibrary(StaticLibrary):
|
||||
'target_dir',
|
||||
'output_category',
|
||||
)
|
||||
TARGET_SUBST_VAR = 'RUST_TARGET'
|
||||
FEATURES_VAR = 'RUST_LIBRARY_FEATURES'
|
||||
LIB_FILE_VAR = 'RUST_LIBRARY_FILE'
|
||||
|
||||
def __init__(self, context, basename, cargo_file, crate_type, dependencies,
|
||||
features, target_dir, **args):
|
||||
StaticLibrary.__init__(self, context, basename, **args)
|
||||
def init(self, context, basename, cargo_file, crate_type, dependencies,
|
||||
features, target_dir):
|
||||
self.cargo_file = cargo_file
|
||||
self.crate_type = crate_type
|
||||
# We need to adjust our naming here because cargo replaces '-' in
|
||||
@ -726,6 +710,24 @@ class RustLibrary(StaticLibrary):
|
||||
self.deps_path = mozpath.join(build_dir, 'deps')
|
||||
|
||||
|
||||
class RustLibrary(StaticLibrary, BaseRustLibrary):
|
||||
"""Context derived container object for a rust static library"""
|
||||
KIND = 'target'
|
||||
TARGET_SUBST_VAR = 'RUST_TARGET'
|
||||
FEATURES_VAR = 'RUST_LIBRARY_FEATURES'
|
||||
LIB_FILE_VAR = 'RUST_LIBRARY_FILE'
|
||||
__slots__ = BaseRustLibrary.slots
|
||||
|
||||
def __init__(self, context, basename, cargo_file, crate_type, dependencies,
|
||||
features, target_dir, link_into=None):
|
||||
StaticLibrary.__init__(self, context, basename, link_into=link_into,
|
||||
# A rust library is a real static library ; make
|
||||
# it known to the build system.
|
||||
no_expand_lib=True)
|
||||
BaseRustLibrary.init(self, context, basename, cargo_file,
|
||||
crate_type, dependencies, features, target_dir)
|
||||
|
||||
|
||||
class SharedLibrary(Library):
|
||||
"""Context derived container object for a shared library"""
|
||||
__slots__ = (
|
||||
@ -839,14 +841,23 @@ class ExternalSharedLibrary(SharedLibrary, ExternalLibrary):
|
||||
class HostLibrary(HostMixin, BaseLibrary):
|
||||
"""Context derived container object for a host library"""
|
||||
KIND = 'host'
|
||||
no_expand_lib = False
|
||||
|
||||
|
||||
class HostRustLibrary(HostMixin, RustLibrary):
|
||||
class HostRustLibrary(HostLibrary, BaseRustLibrary):
|
||||
"""Context derived container object for a host rust library"""
|
||||
KIND = 'host'
|
||||
TARGET_SUBST_VAR = 'RUST_HOST_TARGET'
|
||||
FEATURES_VAR = 'HOST_RUST_LIBRARY_FEATURES'
|
||||
LIB_FILE_VAR = 'HOST_RUST_LIBRARY_FILE'
|
||||
__slots__ = BaseRustLibrary.slots
|
||||
no_expand_lib = True
|
||||
|
||||
def __init__(self, context, basename, cargo_file, crate_type, dependencies,
|
||||
features, target_dir):
|
||||
HostLibrary.__init__(self, context, basename)
|
||||
BaseRustLibrary.init(self, context, basename, cargo_file,
|
||||
crate_type, dependencies, features, target_dir)
|
||||
|
||||
|
||||
class TestManifest(ContextDerived):
|
||||
|
@ -289,6 +289,31 @@ class TreeMetadataEmitter(LoggingMixin):
|
||||
'\n '.join(shared_libs), lib.basename),
|
||||
contexts[lib.objdir])
|
||||
|
||||
@memoize
|
||||
def rust_libraries(obj):
|
||||
libs = []
|
||||
for o in obj.linked_libraries:
|
||||
if isinstance(o, (HostRustLibrary, RustLibrary)):
|
||||
libs.append(o)
|
||||
elif isinstance(o, (HostLibrary, StaticLibrary)):
|
||||
libs.extend(rust_libraries(o))
|
||||
return libs
|
||||
|
||||
def check_rust_libraries(obj):
|
||||
rust_libs = set(rust_libraries(obj))
|
||||
if len(rust_libs) <= 1:
|
||||
return
|
||||
if isinstance(obj, (Library, HostLibrary)):
|
||||
what = '"%s" library' % obj.basename
|
||||
else:
|
||||
what = '"%s" program' % obj.name
|
||||
raise SandboxValidationError(
|
||||
'Cannot link the following Rust libraries into the %s:\n'
|
||||
'%s\nOnly one is allowed.'
|
||||
% (what, '\n'.join(' - %s' % r.basename
|
||||
for r in sorted(rust_libs))),
|
||||
contexts[obj.objdir])
|
||||
|
||||
# Propagate LIBRARY_DEFINES to all child libraries recursively.
|
||||
def propagate_defines(outerlib, defines):
|
||||
outerlib.lib_defines.update(defines)
|
||||
@ -302,6 +327,7 @@ class TreeMetadataEmitter(LoggingMixin):
|
||||
for lib in (l for libs in self._libs.values() for l in libs):
|
||||
if isinstance(lib, Library):
|
||||
propagate_defines(lib, lib.lib_defines)
|
||||
check_rust_libraries(lib)
|
||||
yield lib
|
||||
|
||||
for lib in (l for libs in self._libs.values() for l in libs):
|
||||
@ -321,6 +347,8 @@ class TreeMetadataEmitter(LoggingMixin):
|
||||
yield flags_obj
|
||||
|
||||
for obj in self._binaries.values():
|
||||
if isinstance(obj, Linkable):
|
||||
check_rust_libraries(obj)
|
||||
yield obj
|
||||
|
||||
LIBRARY_NAME_VAR = {
|
||||
|
@ -169,7 +169,9 @@ def process_gyp_result(gyp_result, gyp_dir_attrs, path, config, output,
|
||||
for t in s.get('dependencies', []) + s.get('dependencies_original', []):
|
||||
ty = targets[t]['type']
|
||||
if ty in ('static_library', 'shared_library'):
|
||||
use_libs.append(targets[t]['target_name'])
|
||||
l = targets[t]['target_name']
|
||||
if l not in use_libs:
|
||||
use_libs.append(l)
|
||||
# Manually expand out transitive dependencies--
|
||||
# gyp won't do this for static libs or none targets.
|
||||
if ty in ('static_library', 'none'):
|
||||
@ -184,12 +186,17 @@ def process_gyp_result(gyp_result, gyp_dir_attrs, path, config, output,
|
||||
os_libs = []
|
||||
for l in libs:
|
||||
if l.startswith('-'):
|
||||
os_libs.append(l)
|
||||
if l not in os_libs:
|
||||
os_libs.append(l)
|
||||
elif l.endswith('.lib'):
|
||||
os_libs.append(l[:-4])
|
||||
l = l[:-4]
|
||||
if l not in os_libs:
|
||||
os_libs.append(l)
|
||||
elif l:
|
||||
# For library names passed in from moz.build.
|
||||
use_libs.append(os.path.basename(l))
|
||||
l = os.path.basename(l)
|
||||
if l not in use_libs:
|
||||
use_libs.append(l)
|
||||
|
||||
if spec['type'] == 'none':
|
||||
if not ('actions' in spec or 'copies' in spec):
|
||||
|
@ -28,7 +28,6 @@ from mozbuild.frontend.data import (
|
||||
HostSources,
|
||||
IPDLCollection,
|
||||
JARManifest,
|
||||
LinkageMultipleRustLibrariesError,
|
||||
LocalInclude,
|
||||
LocalizedFiles,
|
||||
LocalizedPreprocessedFiles,
|
||||
@ -1519,8 +1518,9 @@ class TestEmitterBasic(unittest.TestCase):
|
||||
'''Test that linking multiple Rust libraries throws an error'''
|
||||
reader = self.reader('multiple-rust-libraries',
|
||||
extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc'))
|
||||
with self.assertRaisesRegexp(LinkageMultipleRustLibrariesError,
|
||||
'Cannot link multiple Rust libraries'):
|
||||
with self.assertRaisesRegexp(
|
||||
SandboxValidationError,
|
||||
'Cannot link the following Rust libraries'):
|
||||
self.read_topsrcdir(reader)
|
||||
|
||||
def test_rust_library_features(self):
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt
|
||||
#include "ssl.h" // For SSL_ClearSessionCache
|
||||
|
||||
static mozilla::LazyLogModule gSDRLog("sdrlog");
|
||||
|
||||
using namespace mozilla;
|
||||
using dom::Promise;
|
||||
|
||||
@ -73,8 +75,22 @@ void BackgroundSdrDecryptStrings(const nsTArray<nsCString>& encryptedStrings,
|
||||
nsCString plainText;
|
||||
rv = sdrService->DecryptString(encryptedString, plainText);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
if (NS_FAILED(rv)) {
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// Master Password entry was canceled. Don't keep prompting again.
|
||||
break;
|
||||
}
|
||||
|
||||
// NS_ERROR_ILLEGAL_VALUE or NS_ERROR_FAILURE could be due to bad data for
|
||||
// a single string but we still want to decrypt the others.
|
||||
// Callers of `decryptMany` in crypto-SDR.js assume there will be an
|
||||
// equal number of usernames and passwords so use an empty string to keep
|
||||
// this assumption true.
|
||||
MOZ_LOG(gSDRLog, LogLevel::Warning,
|
||||
("Couldn't decrypt string: %s", encryptedString.get()));
|
||||
plainTexts.AppendElement(nullptr);
|
||||
rv = NS_OK;
|
||||
continue;
|
||||
}
|
||||
|
||||
plainTexts.AppendElement(NS_ConvertUTF8toUTF16(plainText));
|
||||
|
@ -21,6 +21,32 @@ const gTokenPasswordDialogs = {
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsITokenPasswordDialogs]),
|
||||
};
|
||||
|
||||
let gMockPrompter = {
|
||||
promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
|
||||
// Returning false simulates the user canceling the password prompt.
|
||||
return false;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
|
||||
};
|
||||
|
||||
// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
|
||||
// to call promptPassword. We return the mock one, above.
|
||||
let gWindowWatcher = {
|
||||
getNewPrompter: () => gMockPrompter,
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowWatcher]),
|
||||
};
|
||||
|
||||
add_task(function setup() {
|
||||
let windowWatcherCID = MockRegistrar.register(
|
||||
"@mozilla.org/embedcomp/window-watcher;1",
|
||||
gWindowWatcher
|
||||
);
|
||||
registerCleanupFunction(() => {
|
||||
MockRegistrar.unregister(windowWatcherCID);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function testEncryptString() {
|
||||
let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
|
||||
Ci.nsISecretDecoderRing
|
||||
@ -187,3 +213,60 @@ add_task(async function testAsyncDecryptStrings() {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function testAsyncDecryptInvalidStrings() {
|
||||
let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
|
||||
Ci.nsISecretDecoderRing
|
||||
);
|
||||
|
||||
// Test invalid inputs for sdr.asyncDecryptStrings
|
||||
let testCases = [
|
||||
"~bmV0cGxheQ==", // invalid base64 encoding
|
||||
"bmV0cGxheQ==", // valid base64 characters but not encrypted
|
||||
"https://www.example.com", // website address from erroneous migration
|
||||
];
|
||||
|
||||
let decrypteds = await sdr.asyncDecryptStrings(testCases);
|
||||
equal(
|
||||
decrypteds.length,
|
||||
testCases.length,
|
||||
"each testcase should still return a response"
|
||||
);
|
||||
for (let i = 0; i < decrypteds.length; i++) {
|
||||
let decrypted = decrypteds[i];
|
||||
|
||||
equal(
|
||||
decrypted,
|
||||
"",
|
||||
"decrypted string should be empty when trying to decrypt an invalid input with asyncDecryptStrings"
|
||||
);
|
||||
|
||||
Assert.throws(
|
||||
() => sdr.decryptString(testCases[i]),
|
||||
/NS_ERROR_ILLEGAL_VALUE|NS_ERROR_FAILURE/,
|
||||
`Check testcase would have thrown: ${testCases[i]}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function testAsyncDecryptLoggedOut() {
|
||||
// Set a master password.
|
||||
let token = Cc["@mozilla.org/security/pk11tokendb;1"]
|
||||
.getService(Ci.nsIPK11TokenDB)
|
||||
.getInternalKeyToken();
|
||||
token.initPassword("password");
|
||||
token.logoutSimple();
|
||||
|
||||
let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
|
||||
Ci.nsISecretDecoderRing
|
||||
);
|
||||
|
||||
await Assert.rejects(
|
||||
sdr.asyncDecryptStrings(["irrelevant"]),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"Check error is thrown instead of returning empty strings"
|
||||
);
|
||||
|
||||
token.reset();
|
||||
token.initPassword("");
|
||||
});
|
||||
|
@ -1,24 +0,0 @@
|
||||
<!doctype html>
|
||||
<script src="util.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
const styles = `:host { background: red; width: 100px; height: 100px; display: block; }`.repeat(10000);
|
||||
customElements.define("custom-element", class CustomElement extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.attachShadow({ mode: "open" });
|
||||
const style = document.createElement("style");
|
||||
style.textContent = styles;
|
||||
this.shadowRoot.appendChild(style);
|
||||
}
|
||||
});
|
||||
|
||||
perf_start();
|
||||
|
||||
for (let i = 0; i < 1000; ++i)
|
||||
document.body.appendChild(document.createElement("custom-element"));
|
||||
|
||||
onload = function() {
|
||||
flush_layout(document.body);
|
||||
perf_finish();
|
||||
};
|
||||
</script>
|
@ -1,26 +0,0 @@
|
||||
<!doctype html>
|
||||
<script src="util.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
const styles = `:host { background: red; width: 100px; height: 100px; display: block; }`.repeat(10000);
|
||||
const blob = URL.createObjectURL(new Blob([styles], { type: 'text/css' }));
|
||||
customElements.define("custom-element", class CustomElement extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.attachShadow({ mode: "open" });
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = blob;
|
||||
this.shadowRoot.appendChild(link);
|
||||
}
|
||||
});
|
||||
|
||||
perf_start();
|
||||
|
||||
for (let i = 0; i < 1000; ++i)
|
||||
document.body.appendChild(document.createElement("custom-element"));
|
||||
|
||||
onload = function() {
|
||||
flush_layout(document.body);
|
||||
perf_finish();
|
||||
};
|
||||
</script>
|
@ -23,5 +23,3 @@
|
||||
% http://localhost/tests/perf-reftest-singletons/id-getter-7.html
|
||||
% http://localhost/tests/perf-reftest-singletons/abspos-reflow-1.html
|
||||
% http://localhost/tests/perf-reftest-singletons/scrollbar-styles-1.html
|
||||
% http://localhost/tests/perf-reftest-singletons/inline-style-cache-1.html
|
||||
% http://localhost/tests/perf-reftest-singletons/link-style-cache-1.html
|
||||
|
@ -0,0 +1,2 @@
|
||||
[text-decoration-skip-ink-sidewayslr-001.html]
|
||||
prefs: [layout.css.text-decoration-skip-ink.enabled:true]
|
@ -0,0 +1,3 @@
|
||||
[text-decoration-skip-ink-sidewayslr-002.html]
|
||||
prefs: [layout.css.text-decoration-skip-ink.enabled:true,
|
||||
layout.css.text-underline-offset.enabled:true]
|
@ -0,0 +1,2 @@
|
||||
[text-decoration-skip-ink-sidewaysrl-001.html]
|
||||
prefs: [layout.css.text-decoration-skip-ink.enabled:true]
|
@ -0,0 +1,3 @@
|
||||
[text-decoration-skip-ink-sidewaysrl-002.html]
|
||||
prefs: [layout.css.text-decoration-skip-ink.enabled:true,
|
||||
layout.css.text-underline-offset.enabled:true]
|
@ -0,0 +1,6 @@
|
||||
[text-decoration-skip-ink-upright-001.html]
|
||||
prefs: [layout.css.text-decoration-skip-ink.enabled:true,
|
||||
layout.css.text-underline-offset.enabled:true]
|
||||
[reference/text-decoration-skip-ink-upright-001-notref.html]
|
||||
expected: fail
|
||||
bug: 1572294
|
@ -0,0 +1,7 @@
|
||||
[text-decoration-skip-ink-upright-002.html]
|
||||
prefs: [layout.css.text-decoration-skip-ink.enabled:true,
|
||||
layout.css.text-underline-offset.enabled:true]
|
||||
[reference/text-decoration-skip-ink-002-ref.html]
|
||||
expected: FAIL
|
||||
bug: 1572294
|
||||
|
@ -0,0 +1,3 @@
|
||||
[text-decoration-skip-ink-vertical-001.html]
|
||||
prefs: [layout.css.text-decoration-skip-ink.enabled:true,
|
||||
layout.css.text-underline-offset.enabled:true]
|
@ -0,0 +1,3 @@
|
||||
[text-decoration-skip-ink-vertical-002.html]
|
||||
prefs: [layout.css.text-decoration-skip-ink.enabled:true,
|
||||
layout.css.text-underline-offset.enabled:true]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user