author | Wes Kocher <wkocher@mozilla.com> |
Fri, 25 Jul 2014 15:59:01 -0700 | |
changeset 196177 | e07264876182f1f407e99df3fbd0759e8f0ab4fa |
parent 196176 | 68e96eb00d3e81c5cca8e40742aa61ceec915a9a (current diff) |
parent 196069 | 42369dee3289aec68f5f06ba55a059d3a683044a (diff) |
child 196178 | f61a9a2aa3bc92a0f153f5903144c1bc0fa4c524 |
child 196278 | cc4b1c9427f26fc2973498c898ae2330f018aeea |
child 196288 | f4f95c3ba8290542ea16597781b8b3ac828c94e8 |
child 196329 | f59f7e13d384e841d07b4862ea761d3956ec5e41 |
push id | 46797 |
push user | kwierso@gmail.com |
push date | Fri, 25 Jul 2014 23:24:25 +0000 |
treeherder | mozilla-inbound@f61a9a2aa3bc [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 34.0a1 |
first release with | nightly linux32
e07264876182
/
34.0a1
/
20140726030204
/
files
nightly linux64
e07264876182
/
34.0a1
/
20140726030204
/
files
nightly mac
e07264876182
/
34.0a1
/
20140726030204
/
files
nightly win32
e07264876182
/
34.0a1
/
20140726030204
/
files
nightly win64
e07264876182
/
34.0a1
/
20140726030204
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
34.0a1
/
20140726030204
/
pushlog to previous
nightly linux64
34.0a1
/
20140726030204
/
pushlog to previous
nightly mac
34.0a1
/
20140726030204
/
pushlog to previous
nightly win32
34.0a1
/
20140726030204
/
pushlog to previous
nightly win64
34.0a1
/
20140726030204
/
pushlog to previous
|
--- a/Makefile.in +++ b/Makefile.in @@ -312,10 +312,24 @@ source-package install: config/export: endif # Interdependencies for parallel export. js/xpconnect/src/export: dom/bindings/export xpcom/xpidl/export accessible/xpcom/export: xpcom/xpidl/export ifdef ENABLE_CLANG_PLUGIN -js/src/export config/export: build/clang-plugin/export +js/src/export config/host: build/clang-plugin/export +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: widget/gtk/mozgtk/gtk3/target endif +ifndef MOZ_FOLD_LIBS +ifndef MOZ_NATIVE_SQLITE +security/build/target: db/sqlite3/src/target +endif +endif +ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library) +mozglue/build/target: memory/replace/dummy/target +endif
--- a/browser/modules/test/browser.ini +++ b/browser/modules/test/browser.ini @@ -21,16 +21,16 @@ skip-if = e10s # Bug 941428 - UITour.jsm skip-if = os == "linux" || e10s # Linux: Bug 986760, Bug 989101; e10s: Bug 941428 - UITour.jsm not e10s friendly [browser_UITour_availableTargets.js] skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly [browser_UITour_detach_tab.js] skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly [browser_UITour_annotation_size_attributes.js] skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly. [browser_UITour_panel_close_annotation.js] -skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly +skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137 [browser_UITour_registerPageID.js] skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly [browser_UITour_sync.js] skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly [browser_taskbar_preview.js] run-if = os == "win" skip-if = e10s # Bug 666808 - AeroPeek support for e10s
--- a/build/autoconf/nspr-build.m4 +++ b/build/autoconf/nspr-build.m4 @@ -46,26 +46,22 @@ MOZ_ARG_WITH_BOOL(system-nspr, [ --with-system-nspr Use an NSPR that is already built and installed. Use the 'nspr-config' script in the current path, or look for the script in the directories given with --with-nspr-exec-prefix or --with-nspr-prefix. (Those flags are only checked if you specify --with-system-nspr.)], _USE_SYSTEM_NSPR=1 ) -if test -z "$BUILDING_JS"; then - JS_THREADSAFE=1 -fi - JS_POSIX_NSPR=unset ifdef([CONFIGURING_JS],[ if test -n "$JS_STANDALONE"; then case "$target" in *linux*|*darwin*|*dragonfly*|*freebsd*|*netbsd*|*openbsd*) - if test -z "$_HAS_NSPR" && test "$JS_THREADSAFE"; then + if test -z "$_HAS_NSPR"; then JS_POSIX_NSPR_DEFAULT=1 fi ;; esac fi MOZ_ARG_ENABLE_BOOL(posix-nspr-emulation, [ --enable-posix-nspr-emulation @@ -105,26 +101,22 @@ fi if test -z "$nspr_opts"; then if test -z "$BUILDING_JS"; then dnl Toplevel configure defaults to using nsprpub from the source tree MOZ_BUILD_NSPR=1 which_nspr="source-tree" else dnl JS configure defaults to emulated NSPR if available, falling back dnl to nsprpub. - if test -n "$JS_THREADSAFE"; then - JS_POSIX_NSPR="$JS_POSIX_NSPR_DEFAULT" - if test -z "$JS_POSIX_NSPR"; then - MOZ_BUILD_NSPR=1 - which_nspr="source-tree" - else - which_nspr="posix-wrapper" - fi + JS_POSIX_NSPR="$JS_POSIX_NSPR_DEFAULT" + if test -z "$JS_POSIX_NSPR"; then + MOZ_BUILD_NSPR=1 + which_nspr="source-tree" else - which_nspr="none" + which_nspr="posix-wrapper" fi fi fi if test -z "$nspr_opts" || test "$nspr_opts" = x; then AC_MSG_RESULT($which_nspr) else AC_MSG_ERROR([only one way of using NSPR may be selected. See 'configure --help'.]) @@ -159,17 +151,17 @@ if test -n "$MOZ_NATIVE_NSPR" -o -n "$NS AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT or including prtypes.h does not provide it])) AC_TRY_COMPILE([#include "prtypes.h"], [#ifndef PR_UINT64 #error PR_UINT64 not defined or requires including prtypes.h #endif], , AC_MSG_ERROR([system NSPR does not support PR_UINT64 or including prtypes.h does not provide it])) CFLAGS=$_SAVE_CFLAGS -elif test -z "$JS_POSIX_NSPR" -a -n "$JS_THREADSAFE"; then +elif test -z "$JS_POSIX_NSPR"; then if test -z "$LIBXUL_SDK"; then NSPR_CFLAGS="-I${LIBXUL_DIST}/include/nspr" if test -n "$GNU_CC"; then NSPR_LIBS="-L${LIBXUL_DIST}/lib -lnspr${NSPR_VERSION} -lplc${NSPR_VERSION} -lplds${NSPR_VERSION}" else NSPR_LIBS="${LIBXUL_DIST}/lib/nspr${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plc${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plds${NSPR_VERSION}.lib " fi else
--- a/build/automationutils.py +++ b/build/automationutils.py @@ -1,21 +1,25 @@ # # 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/. from __future__ import with_statement -import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2, zipfile -import base64 -import re +import logging +from operator import itemgetter import os +import platform +import re +import signal +import subprocess +import sys +import tempfile from urlparse import urlparse -from operator import itemgetter -import signal +import zipfile try: import mozinfo except ImportError: # Stub out fake mozinfo since this is not importable on Android 4.0 Opt. # This should be fixed; see # https://bugzilla.mozilla.org/show_bug.cgi?id=650881 mozinfo = type('mozinfo', (), dict(info={}))() @@ -552,17 +556,16 @@ def environment(xrePath, env=None, crash else: log.info(message) return env def dumpScreen(utilityPath): """dumps a screenshot of the entire screen to a directory specified by the MOZ_UPLOAD_DIR environment variable""" - import mozfile # Need to figure out which OS-dependent tool to use if mozinfo.isUnix: utility = [os.path.join(utilityPath, "screentopng")] utilityname = "screentopng" elif mozinfo.isMac: utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png'] utilityname = "screencapture"
--- a/build/unix/elfhack/Makefile.in +++ b/build/unix/elfhack/Makefile.in @@ -22,16 +22,17 @@ test-array$(DLL_SUFFIX) test-ctors$(DLL_ $(TOOLCHAIN_PREFIX)readelf -d $@ | grep '($(DT_TYPE))' @rm -f $@.bak $(CURDIR)/elfhack -b -f $@ # Fail if the backup file doesn't exist [ -f '$@.bak' ] # Fail if the new library doesn't contain less relocations [ $$($(TOOLCHAIN_PREFIX)objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ] +test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): DSO_SONAME=$@ test-array$(DLL_SUFFIX): DT_TYPE=INIT_ARRAY test-ctors$(DLL_SUFFIX): DT_TYPE=INIT .PRECIOUS: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) GARBAGE += test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) test-array$(DLL_SUFFIX).bak test-ctors$(DLL_SUFFIX).bak ifndef CROSS_COMPILE
--- a/build/unix/elfhack/moz.build +++ b/build/unix/elfhack/moz.build @@ -2,24 +2,21 @@ # 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/. NO_DIST_INSTALL = True DIRS += ['inject'] -SOURCES += [ - 'test-array.c', - 'test-ctors.c', -] - if not CONFIG['CROSS_COMPILE']: SOURCES += [ 'dummy.c', + 'test-array.c', + 'test-ctors.c', ] HOST_SOURCES += [ 'elf.cpp', 'elfhack.cpp', ] HOST_PROGRAM = 'elfhack'
--- a/config/Makefile.in +++ b/config/Makefile.in @@ -1,18 +1,14 @@ # -*- Makefile -*- # # 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/. -# STDCXX_COMPAT is not needed here, and will actually fail because -# libstdc++-compat is not built yet. -MOZ_LIBSTDCXX_HOST_VERSION = - # IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have # a recursive rule for finding nsinstall and the Perl scripts. ifdef NSBUILDROOT override NSBUILDROOT := endif ifdef GNU_CC MODULE_OPTIMIZE_FLAGS = -O3 @@ -24,17 +20,17 @@ ifneq (WINNT,$(HOST_OS_ARCH)) ifdef COMPILE_ENVIRONMENT # Ensure nsinstall is atomically created nsinstall$(HOST_BIN_SUFFIX): $(HOST_PROGRAM) cp $^ $@.tmp mv $@.tmp $@ NSINSTALL_EXECUTABLES := nsinstall$(HOST_BIN_SUFFIX) NSINSTALL_DEST := $(DIST)/bin -NSINSTALL_TARGET := export +NSINSTALL_TARGET := host INSTALL_TARGETS += NSINSTALL endif endif ifndef JS_STANDALONE HEADERS_FILES = \ $(DEPTH)/mozilla-config.h \ $(NULL)
--- a/config/config.mk +++ b/config/config.mk @@ -767,23 +767,16 @@ ifdef SYMBOL_ORDER EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER) endif EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB) ifneq (,$(MOZ_LIBSTDCXX_TARGET_VERSION)$(MOZ_LIBSTDCXX_HOST_VERSION)) ifneq ($(OS_ARCH),Darwin) CHECK_STDCXX = @$(TOOLCHAIN_PREFIX)objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo 'TEST-UNEXPECTED-FAIL | check_stdcxx | We do not want these libstdc++ symbols to be used:' && $(TOOLCHAIN_PREFIX)objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && false || true endif - -ifdef MOZ_LIBSTDCXX_TARGET_VERSION -OS_LIBS += $(call EXPAND_LIBNAME_PATH,stdc++compat,$(DEPTH)/build/unix/stdc++compat) -endif -ifdef MOZ_LIBSTDCXX_HOST_VERSION -HOST_EXTRA_LIBS += $(call EXPAND_LIBNAME_PATH,host_stdc++compat,$(DEPTH)/build/unix/stdc++compat) -endif endif ifeq (,$(filter $(OS_TARGET),WINNT Darwin)) CHECK_TEXTREL = @$(TOOLCHAIN_PREFIX)readelf -d $(1) | grep TEXTREL > /dev/null && echo 'TEST-UNEXPECTED-FAIL | check_textrel | We do not want text relocations in libraries and programs' || true endif define CHECK_BINARY $(call CHECK_STDCXX,$(1))
--- a/config/makefiles/target_binaries.mk +++ b/config/makefiles/target_binaries.mk @@ -18,69 +18,69 @@ endif # IS_COMPONENT endif # SHARED_LIBRARY endif # !NO_DIST_INSTALL ifndef NO_DIST_INSTALL ifneq (,$(strip $(PROGRAM)$(SIMPLE_PROGRAMS))) PROGRAMS_EXECUTABLES = $(SIMPLE_PROGRAMS) $(PROGRAM) PROGRAMS_DEST ?= $(FINAL_TARGET) -PROGRAMS_TARGET := binaries libs +PROGRAMS_TARGET := binaries libs target INSTALL_TARGETS += PROGRAMS endif ifdef LIBRARY ifdef DIST_INSTALL ifdef IS_COMPONENT $(error Shipping static component libs makes no sense.) else DIST_LIBRARY_FILES = $(LIBRARY) DIST_LIBRARY_DEST ?= $(DIST)/lib -DIST_LIBRARY_TARGET = binaries libs +DIST_LIBRARY_TARGET = binaries libs target INSTALL_TARGETS += DIST_LIBRARY endif endif # DIST_INSTALL endif # LIBRARY ifdef SHARED_LIBRARY ifndef IS_COMPONENT SHARED_LIBRARY_FILES = $(SHARED_LIBRARY) SHARED_LIBRARY_DEST ?= $(FINAL_TARGET) -SHARED_LIBRARY_TARGET = binaries libs +SHARED_LIBRARY_TARGET = binaries libs target INSTALL_TARGETS += SHARED_LIBRARY ifneq (,$(filter WINNT,$(OS_ARCH))) ifndef NO_INSTALL_IMPORT_LIBRARY IMPORT_LIB_FILES = $(IMPORT_LIBRARY) endif # NO_INSTALL_IMPORT_LIBRARY else IMPORT_LIB_FILES = $(SHARED_LIBRARY) endif IMPORT_LIB_DEST ?= $(DIST)/lib -IMPORT_LIB_TARGET = binaries libs +IMPORT_LIB_TARGET = binaries libs target ifdef IMPORT_LIB_FILES INSTALL_TARGETS += IMPORT_LIB endif endif # ! IS_COMPONENT endif # SHARED_LIBRARY ifneq (,$(strip $(HOST_SIMPLE_PROGRAMS)$(HOST_PROGRAM))) HOST_PROGRAMS_EXECUTABLES = $(HOST_SIMPLE_PROGRAMS) $(HOST_PROGRAM) HOST_PROGRAMS_DEST ?= $(DIST)/host/bin -HOST_PROGRAMS_TARGET = binaries libs +HOST_PROGRAMS_TARGET = binaries libs host INSTALL_TARGETS += HOST_PROGRAMS endif ifdef HOST_LIBRARY HOST_LIBRARY_FILES = $(HOST_LIBRARY) HOST_LIBRARY_DEST ?= $(DIST)/host/lib -HOST_LIBRARY_TARGET = binaries libs +HOST_LIBRARY_TARGET = binaries libs host INSTALL_TARGETS += HOST_LIBRARY endif endif # !NO_DIST_INSTALL BINARIES_INSTALL_TARGETS := $(foreach category,$(INSTALL_TARGETS),$(if $(filter binaries,$($(category)_TARGET)),$(category))) # Fill a dependency file with all the binaries installed somewhere in $(DIST)
--- a/config/nsinstall.py +++ b/config/nsinstall.py @@ -122,17 +122,18 @@ def _nsinstall_internal(argv): entries = [os.path.join(srcpath, e) for e in os.listdir(srcpath)] copy_all_entries(entries, targetpath) # options.t is not relevant for directories if options.m: os.chmod(targetpath, options.m) else: if os.path.exists(targetpath): # On Windows, read-only files can't be deleted - os.chmod(targetpath, stat.S_IWUSR) + if sys.platform == "win32": + os.chmod(targetpath, stat.S_IWUSR) os.remove(targetpath) if options.t: shutil.copy2(srcpath, targetpath) else: shutil.copy(srcpath, targetpath) # the last argument is the target directory target = args.pop()
--- a/config/nspr/Makefile.in +++ b/config/nspr/Makefile.in @@ -24,20 +24,21 @@ EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPO # See bug #838566. EXTRA_MAKE_FLAGS += XP_DEFINE=-DlibVersionPoint='libVersionPoint$$(LIBRARY_NAME)' else # nspr's make export compiles and links everything, but linking can't happen # during export on platforms where nspr is linked against mozcrt/mozglue. export:: EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB= endif -libs export clean distclean:: +clean distclean export:: $(MAKE) -C $(DEPTH)/nsprpub $@ $(EXTRA_MAKE_FLAGS) -libs:: +target:: + $(MAKE) -C $(DEPTH)/nsprpub libs $(EXTRA_MAKE_FLAGS) $(MAKE) -C $(DEPTH)/nsprpub install prefix=$(ABS_DIST)/sdk exec_prefix=$(ABS_DIST)/sdk bindir=$(ABS_DIST)/sdk/dummy includedir=$(ABS_DIST)/include/nspr libdir=$(ABS_DIST)/sdk/lib datadir=$(ABS_DIST)/sdk/dummy DESTDIR= $(EXTRA_MAKE_FLAGS) $(INSTALL) $(DEPTH)/nsprpub/config/nspr-config $(DIST)/sdk/bin $(RM) -rf $(DIST)/sdk/dummy ifneq (,$(filter WINNT,$(OS_ARCH))) # { $(RM) -f $(DIST)/sdk/lib/$(DLL_PREFIX)nspr4$(DLL_SUFFIX) $(DIST)/sdk/lib/$(DLL_PREFIX)plc4$(DLL_SUFFIX) $(DIST)/sdk/lib/$(DLL_PREFIX)plds4$(DLL_SUFFIX) $(RM) -f $(DIST)/sdk/lib/$(LIB_PREFIX)nspr4_s.$(LIB_SUFFIX) $(DIST)/sdk/lib/$(LIB_PREFIX)plc4_s.$(LIB_SUFFIX) $(DIST)/sdk/lib/$(LIB_PREFIX)plds4_s.$(LIB_SUFFIX) else # } { $(RM) -f $(DIST)/sdk/lib/$(LIB_PREFIX)nspr4.$(LIB_SUFFIX) $(DIST)/sdk/lib/$(LIB_PREFIX)plc4.$(LIB_SUFFIX) $(DIST)/sdk/lib/$(LIB_PREFIX)plds4.$(LIB_SUFFIX)
--- a/config/recurse.mk +++ b/config/recurse.mk @@ -71,35 +71,62 @@ endif # TIERS (like for js/src). CURRENT_DIRS := $($(CURRENT_TIER)_dirs) ifneq (,$(filter binaries libs,$(CURRENT_TIER))) WANT_STAMPS = 1 STAMP_TOUCH = $(TOUCH) $(@D)/binaries endif +# The compile tier has different rules from other tiers. +ifeq ($(CURRENT_TIER),compile) + +# Need a list of compile targets because we can't use pattern rules: +# https://savannah.gnu.org/bugs/index.php?42833 +.PHONY: $(compile_targets) +$(compile_targets): + $(call SUBMAKE,$(if $(filter $(@D),$(staticdirs)),,$(@F)),$(@D)) + +else + # Recursion rule for all directories traversed for all subtiers in the # current tier. $(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS)): %/$(CURRENT_TIER): - $(call SUBMAKE,$(if $(filter $*,$(staticdirs)),,$(CURRENT_TIER)),$*) + $(call SUBMAKE,$(CURRENT_TIER),$*) # Ensure existing stamps are up-to-date, but don't create one if submake didn't create one. $(if $(wildcard $@),@$(STAMP_TOUCH)) ifndef STAMP_TOUCH .PHONY: $(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS)) endif # Dummy rules for possibly inexisting dependencies for the above tier targets $(addsuffix /Makefile,$(CURRENT_DIRS)) $(addsuffix /backend.mk,$(CURRENT_DIRS)): +# At least build/export requires config/export for buildid, but who knows what +# else, so keep this global dependency to make config/export first for now. +$(addsuffix /$(CURRENT_TIER),$(filter-out config,$(CURRENT_DIRS))): config/$(CURRENT_TIER) + # The export tier requires nsinstall, which is built from config. So every -# subdirectory traversal needs to happen after traversing config. +# subdirectory traversal needs to happen after building nsinstall in config, which +# is done with the config/host target. Note the config/host target only exists if +# nsinstall is actually built, which it is not on Windows, because we use +# nsinstall.py there. ifeq ($(CURRENT_TIER),export) -$(addsuffix /$(CURRENT_TIER),$(filter-out config,$(CURRENT_DIRS))): config/$(CURRENT_TIER) +ifneq (,$(filter config/host, $(compile_targets))) +$(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS)): config/host + +# Ensure rules for config/host and its possible dependencies. +.PHONY: $(filter %/host, $(compile_targets)) +$(filter %/host, $(compile_targets)): + $(call SUBMAKE,host,$(@D)) endif +endif + +endif # ifeq ($(CURRENT_TIER),compile) ifdef COMPILE_ENVIRONMENT # Disable dependency aggregation on PGO builds because of bug 934166. ifeq (,$(MOZ_PGO)$(MOZ_PROFILE_USE)$(MOZ_PROFILE_GENERATE)) ifneq (,$(filter libs binaries,$(CURRENT_TIER))) # When doing a "libs" build, target_libs.mk ensures the interesting dependency data # is available in the "binaries" stamp. Once recursion is done, aggregate all that # dependency info so that stamps depend on relevant files and relevant other stamps. @@ -166,17 +193,17 @@ endif $(1):: $$(SUBMAKEFILES) ifdef PARALLEL_DIRS +@$(MAKE) $$(PARALLEL_DIRS_$(1)) endif $$(LOOP_OVER_DIRS) endef -$(foreach subtier,export compile binaries libs tools,$(eval $(call CREATE_SUBTIER_TRAVERSAL_RULE,$(subtier)))) +$(foreach subtier,export binaries libs tools,$(eval $(call CREATE_SUBTIER_TRAVERSAL_RULE,$(subtier)))) tools export:: $(SUBMAKEFILES) $(LOOP_OVER_TOOL_DIRS) endif # ifdef TIERS endif # ifeq ($(NO_RECURSE_MAKELEVEL),$(MAKELEVEL))
--- a/config/rules.mk +++ b/config/rules.mk @@ -81,18 +81,20 @@ ifdef COMPILE_ENVIRONMENT # Compile the tests to $(DIST)/bin. Make lots of niceties available by default # through TestHarness.h, by modifying the list of includes and the libs against # which stuff links. SIMPLE_PROGRAMS += $(CPP_UNIT_TESTS) INCLUDES += -I$(DIST)/include/testing LIBS += $(NSPR_LIBS) ifndef MOZ_PROFILE_GENERATE -libs:: $(CPP_UNIT_TESTS) $(call mkdir_deps,$(DIST)/cppunittests) - $(NSINSTALL) $(CPP_UNIT_TESTS) $(DIST)/cppunittests +CPP_UNIT_TESTS_FILES = $(CPP_UNIT_TESTS) +CPP_UNIT_TESTS_DEST = $(DIST)/cppunittests +CPP_UNIT_TESTS_TARGET = binaries libs target +INSTALL_TARGETS += CPP_UNIT_TESTS endif run-cppunittests:: @$(PYTHON) $(topsrcdir)/testing/runcppunittests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(CPP_UNIT_TESTS) cppunittests-remote: DM_TRANS?=adb cppunittests-remote: @if [ '${TEST_DEVICE}' != '' -o '$(DM_TRANS)' = 'adb' ]; then \ @@ -584,17 +586,21 @@ HOST_LIBS_DEPS = $(filter %.$(LIB_SUFFIX # Dependencies which, if modified, should cause everything to rebuild GLOBAL_DEPS += Makefile $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk ############################################## ifdef COMPILE_ENVIRONMENT OBJ_TARGETS = $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS) -compile:: $(OBJ_TARGETS) +compile:: host target + +host:: $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS) + +target:: $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) include $(topsrcdir)/config/makefiles/target_binaries.mk endif ifdef IS_TOOL_DIR # One would think "tools:: libs" would work, but it turns out that combined with # bug 907365, this makes make forget to run some rules sometimes. tools:: @@ -710,17 +716,17 @@ ifdef MSMANIFEST_TOOL fi endif # MSVC with manifest tool ifdef MOZ_PROFILE_GENERATE # touch it a few seconds into the future to work around FAT's # 2-second granularity touch -t `date +%Y%m%d%H%M.%S -d 'now+5seconds'` pgo.relink endif else # !WINNT || GNU_CC - $(EXPAND_CCC) -o $@ $(CXXFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(LIBS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) $(STLPORT_LIBS) + $(EXPAND_CCC) -o $@ $(CXXFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) $(STLPORT_LIBS) $(call CHECK_BINARY,$@) endif # WINNT && !GNU_CC ifdef ENABLE_STRIP $(STRIP) $(STRIP_FLAGS) $@ endif ifdef MOZ_POST_PROGRAM_COMMAND $(MOZ_POST_PROGRAM_COMMAND) $@ @@ -766,17 +772,17 @@ ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH)) $(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) ifdef MSMANIFEST_TOOL @if test -f $@.manifest; then \ mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \ rm -f $@.manifest; \ fi endif # MSVC with manifest tool else - $(EXPAND_CCC) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(LIBS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(STLPORT_LIBS) + $(EXPAND_CCC) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(STLPORT_LIBS) $(call CHECK_BINARY,$@) endif # WINNT && !GNU_CC ifdef ENABLE_STRIP $(STRIP) $(STRIP_FLAGS) $@ endif ifdef MOZ_POST_PROGRAM_COMMAND $(MOZ_POST_PROGRAM_COMMAND) $@ @@ -1262,24 +1268,27 @@ endif ################################################################################ # SDK ifneq (,$(SDK_LIBRARY)) ifndef NO_DIST_INSTALL SDK_LIBRARY_FILES := $(SDK_LIBRARY) SDK_LIBRARY_DEST := $(SDK_LIB_DIR) +SDK_LIBRARY_TARGET := binaries libs target INSTALL_TARGETS += SDK_LIBRARY endif endif # SDK_LIBRARY ifneq (,$(strip $(SDK_BINARY))) ifndef NO_DIST_INSTALL SDK_BINARY_FILES := $(SDK_BINARY) SDK_BINARY_DEST := $(SDK_BIN_DIR) +# SDK_BINARY_TARGET is set in xpcom/idl-parser/Makefile.in +SDK_BINARY_TARGET ?= binaries libs target INSTALL_TARGETS += SDK_BINARY endif endif # SDK_BINARY ################################################################################ # CHROME PACKAGING chrome::
--- a/content/base/public/Element.h +++ b/content/base/public/Element.h @@ -76,28 +76,42 @@ enum { // Set if the element has a pending animation style change. ELEMENT_HAS_PENDING_ANIMATION_RESTYLE = ELEMENT_FLAG_BIT(2), // Set if the element is a potential animation restyle root (that is, // has an animation style change pending _and_ that style change // will attempt to restyle descendants). ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT = ELEMENT_FLAG_BIT(3), + // Set if the element has a pending animation-only style change as + // part of an animation-only style update (where we update styles from + // animation to the current refresh tick, but leave everything else as + // it was). + ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE = ELEMENT_FLAG_BIT(4), + + // Set if the element is a potential animation-only restyle root (that + // is, has an animation-only style change pending _and_ that style + // change will attempt to restyle descendants). + ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT = ELEMENT_FLAG_BIT(5), + // All of those bits together, for convenience. ELEMENT_ALL_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE | ELEMENT_IS_POTENTIAL_RESTYLE_ROOT | ELEMENT_HAS_PENDING_ANIMATION_RESTYLE | - ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT, + ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT | + ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE | + ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT, // Just the HAS_PENDING bits, for convenience ELEMENT_PENDING_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE | - ELEMENT_HAS_PENDING_ANIMATION_RESTYLE, + ELEMENT_HAS_PENDING_ANIMATION_RESTYLE | + ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE, // Remaining bits are for subclasses - ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 4 + ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 6 }; #undef ELEMENT_FLAG_BIT // Make sure we have space for our bits ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET); namespace mozilla {
--- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -2178,16 +2178,22 @@ public: /** * Returns whether a given header is forbidden for a system XHR * request. */ static bool IsForbiddenSystemRequestHeader(const nsACString& aHeader); /** + * Returns whether a given Content-Type header value is allowed + * for a non-CORS XHR or fetch request. + */ + static bool IsAllowedNonCorsContentType(const nsACString& aHeaderValue); + + /** * Returns whether a given header is forbidden for an XHR or fetch * response. */ static bool IsForbiddenResponseHeader(const nsACString& aHeader); private: static bool InitializeEventTable();
--- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -146,45 +146,43 @@ enum { // child's later siblings must also be restyled. NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS = NODE_FLAG_BIT(13), NODE_ALL_SELECTOR_FLAGS = NODE_HAS_EMPTY_SELECTOR | NODE_HAS_SLOW_SELECTOR | NODE_HAS_EDGE_CHILD_SELECTOR | NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS, - NODE_ATTACH_BINDING_ON_POSTCREATE = NODE_FLAG_BIT(14), - // This node needs to go through frame construction to get a frame (or // undisplayed entry). - NODE_NEEDS_FRAME = NODE_FLAG_BIT(15), + NODE_NEEDS_FRAME = NODE_FLAG_BIT(14), // At least one descendant in the flattened tree has NODE_NEEDS_FRAME set. // This should be set on every node on the flattened tree path between the // node(s) with NODE_NEEDS_FRAME and the root content. - NODE_DESCENDANTS_NEED_FRAMES = NODE_FLAG_BIT(16), + NODE_DESCENDANTS_NEED_FRAMES = NODE_FLAG_BIT(15), // Set if the node has the accesskey attribute set. - NODE_HAS_ACCESSKEY = NODE_FLAG_BIT(17), + NODE_HAS_ACCESSKEY = NODE_FLAG_BIT(16), // Set if the node has right-to-left directionality - NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(18), + NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(17), // Set if the node has left-to-right directionality - NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(19), + NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(18), NODE_ALL_DIRECTION_FLAGS = NODE_HAS_DIRECTION_LTR | NODE_HAS_DIRECTION_RTL, - NODE_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(20), + NODE_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(19), - NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(21), + NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(20), // Remaining bits are node type specific. - NODE_TYPE_SPECIFIC_BITS_OFFSET = 22 + NODE_TYPE_SPECIFIC_BITS_OFFSET = 21 }; // Make sure we have space for our bits #define ASSERT_NODE_FLAGS_SPACE(n) \ static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <= \ sizeof(nsWrapperCache::FlagsType) * 8, \ "Not enough space for our bits") ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET); @@ -989,17 +987,16 @@ public: } #endif void SetFlags(FlagsType aFlagsToSet) { NS_ASSERTION(!(aFlagsToSet & (NODE_IS_ANONYMOUS_ROOT | NODE_IS_NATIVE_ANONYMOUS_ROOT | NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE | - NODE_ATTACH_BINDING_ON_POSTCREATE | NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME | NODE_CHROME_ONLY_ACCESS)) || IsNodeOfType(eCONTENT), "Flag only permitted on nsIContent nodes"); nsWrapperCache::SetFlags(aFlagsToSet); }
--- a/content/base/src/WebSocket.cpp +++ b/content/base/src/WebSocket.cpp @@ -674,27 +674,38 @@ WebSocket::Init(JSContext* aCx, // parses the url rv = ParseURL(PromiseFlatString(aURL)); NS_ENSURE_SUCCESS(rv, rv); nsIScriptContext* sc = GetContextForEventHandlers(&rv); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIDocument> originDoc = nsContentUtils::GetDocumentFromScriptContext(sc); - // Don't allow https:// to open ws:// if (!mSecure && !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS", false)) { // Confirmed we are opening plain ws:// and want to prevent this from a - // secure context (e.g. https). Check the security context of the document - // associated with this script, which is the same as associated with mOwner. - if (originDoc && originDoc->GetSecurityInfo()) { - return NS_ERROR_DOM_SECURITY_ERR; + // secure context (e.g. https). Check the principal's uri to determine if + // we were loaded from https. + nsCOMPtr<nsIGlobalObject> globalObject(BrokenGetEntryGlobal()); + if (globalObject) { + nsCOMPtr<nsIPrincipal> principal(globalObject->PrincipalOrNull()); + if (principal) { + nsCOMPtr<nsIURI> uri; + principal->GetURI(getter_AddRefs(uri)); + if (uri) { + bool originIsHttps = false; + rv = uri->SchemeIs("https", &originIsHttps); + NS_ENSURE_SUCCESS(rv,rv); + if (originIsHttps) { + return NS_ERROR_DOM_SECURITY_ERR; + } + } + } } } // Assign the sub protocol list and scan it for illegal values for (uint32_t index = 0; index < aProtocolArray.Length(); ++index) { for (uint32_t i = 0; i < aProtocolArray[index].Length(); ++i) { if (aProtocolArray[index][i] < static_cast<char16_t>(0x0021) || aProtocolArray[index][i] > static_cast<char16_t>(0x007E)) @@ -705,16 +716,17 @@ WebSocket::Init(JSContext* aCx, mRequestedProtocolList.AppendLiteral(", "); } AppendUTF16toUTF8(aProtocolArray[index], mRequestedProtocolList); } // Check content policy. int16_t shouldLoad = nsIContentPolicy::ACCEPT; + nsCOMPtr<nsIDocument> originDoc = nsContentUtils::GetDocumentFromScriptContext(sc); rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET, mURI, mPrincipal, originDoc, EmptyCString(), nullptr, &shouldLoad, nsContentUtils::GetContentPolicy(),
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -6900,16 +6900,33 @@ nsContentUtils::IsForbiddenSystemRequest // static bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) { return (aHeader.LowerCaseEqualsASCII("set-cookie") || aHeader.LowerCaseEqualsASCII("set-cookie2")); } +// static +bool +nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue) +{ + nsAutoCString contentType; + nsAutoCString unused; + + nsresult rv = NS_ParseContentType(aHeaderValue, contentType, unused); + if (NS_FAILED(rv)) { + return false; + } + + return contentType.LowerCaseEqualsLiteral("text/plain") || + contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") || + contentType.LowerCaseEqualsLiteral("multipart/form-data"); +} + bool nsContentUtils::DOMWindowDumpEnabled() { #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP)) // In optimized builds we check a pref that controls if we should // enable output from dump() or not, in debug builds it's always // enabled. return nsContentUtils::sDOMWindowDumpEnabled;
--- a/content/base/src/nsCrossSiteListenerProxy.cpp +++ b/content/base/src/nsCrossSiteListenerProxy.cpp @@ -499,56 +499,16 @@ nsCORSListenerProxy::OnStartRequest(nsIR mOuterListener->OnStartRequest(aRequest, aContext); return NS_ERROR_DOM_BAD_URI; } return mOuterListener->OnStartRequest(aRequest, aContext); } -bool -IsValidHTTPToken(const nsCSubstring& aToken) -{ - if (aToken.IsEmpty()) { - return false; - } - - nsCSubstring::const_char_iterator iter, end; - - aToken.BeginReading(iter); - aToken.EndReading(end); - - while (iter != end) { - if (*iter <= 32 || - *iter >= 127 || - *iter == '(' || - *iter == ')' || - *iter == '<' || - *iter == '>' || - *iter == '@' || - *iter == ',' || - *iter == ';' || - *iter == ':' || - *iter == '\\' || - *iter == '\"' || - *iter == '/' || - *iter == '[' || - *iter == ']' || - *iter == '?' || - *iter == '=' || - *iter == '{' || - *iter == '}') { - return false; - } - ++iter; - } - - return true; -} - nsresult nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest) { // Check if this was actually a cross domain request if (!mHasBeenCrossSite) { return NS_OK; } @@ -611,17 +571,17 @@ nsCORSListenerProxy::CheckRequestApprove mPreflightMethod.EqualsLiteral("HEAD") || mPreflightMethod.EqualsLiteral("POST"); nsCCharSeparatedTokenizer methodTokens(headerVal, ','); while(methodTokens.hasMoreTokens()) { const nsDependentCSubstring& method = methodTokens.nextToken(); if (method.IsEmpty()) { continue; } - if (!IsValidHTTPToken(method)) { + if (!NS_IsValidHTTPToken(method)) { return NS_ERROR_DOM_BAD_URI; } foundMethod |= mPreflightMethod.Equals(method); } NS_ENSURE_TRUE(foundMethod, NS_ERROR_DOM_BAD_URI); // The "Access-Control-Allow-Headers" header contains a comma separated // list of header names. @@ -630,17 +590,17 @@ nsCORSListenerProxy::CheckRequestApprove headerVal); nsTArray<nsCString> headers; nsCCharSeparatedTokenizer headerTokens(headerVal, ','); while(headerTokens.hasMoreTokens()) { const nsDependentCSubstring& header = headerTokens.nextToken(); if (header.IsEmpty()) { continue; } - if (!IsValidHTTPToken(header)) { + if (!NS_IsValidHTTPToken(header)) { return NS_ERROR_DOM_BAD_URI; } headers.AppendElement(header); } for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) { if (!headers.Contains(mPreflightHeaders[i], nsCaseInsensitiveCStringArrayComparator())) { return NS_ERROR_DOM_BAD_URI;
--- a/content/base/src/nsCrossSiteListenerProxy.h +++ b/content/base/src/nsCrossSiteListenerProxy.h @@ -16,19 +16,16 @@ #include "nsIChannelEventSink.h" #include "nsIAsyncVerifyRedirectCallback.h" #include "mozilla/Attributes.h" class nsIURI; class nsIParser; class nsIPrincipal; -extern bool -IsValidHTTPToken(const nsCSubstring& aToken); - nsresult NS_StartCORSPreflight(nsIChannel* aRequestChannel, nsIStreamListener* aListener, nsIPrincipal* aPrincipal, bool aWithCredentials, nsTArray<nsCString>& aACUnsafeHeaders, nsIChannel** aPreflightChannel);
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -1290,17 +1290,17 @@ nsXMLHttpRequest::IsSafeHeader(const nsA headerVal); nsCCharSeparatedTokenizer exposeTokens(headerVal, ','); bool isSafe = false; while (exposeTokens.hasMoreTokens()) { const nsDependentCSubstring& token = exposeTokens.nextToken(); if (token.IsEmpty()) { continue; } - if (!IsValidHTTPToken(token)) { + if (!NS_IsValidHTTPToken(token)) { return false; } if (header.Equals(token, nsCaseInsensitiveCStringComparator())) { isSafe = true; } } return isSafe; } @@ -2864,23 +2864,17 @@ nsXMLHttpRequest::Send(nsIVariant* aVari } } if (httpChannel) { nsAutoCString contentTypeHeader; rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), contentTypeHeader); if (NS_SUCCEEDED(rv)) { - nsAutoCString contentType, charset; - rv = NS_ParseContentType(contentTypeHeader, contentType, charset); - NS_ENSURE_SUCCESS(rv, rv); - - if (!contentType.LowerCaseEqualsLiteral("text/plain") && - !contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") && - !contentType.LowerCaseEqualsLiteral("multipart/form-data")) { + if (!nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader)) { mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type")); } } } ResetResponse(); rv = CheckChannelForCrossSiteRequest(mChannel); @@ -3094,17 +3088,17 @@ nsXMLHttpRequest::SetRequestHeader(const // Step 1 and 2 if (!(mState & XML_HTTP_REQUEST_OPENED)) { return NS_ERROR_DOM_INVALID_STATE_ERR; } NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED."); // Step 3 // Make sure we don't store an invalid header name in mCORSUnsafeHeaders - if (!IsValidHTTPToken(header)) { // XXX nsHttp::IsValidToken? + if (!NS_IsValidHTTPToken(header)) { return NS_ERROR_DOM_SYNTAX_ERR; } // Check that we haven't already opened the channel. We can't rely on // the channel throwing from mChannel->SetRequestHeader since we might // still be waiting for mCORSPreflightChannel to actually open mChannel if (mCORSPreflightChannel) { bool pending;
--- a/content/media/AudioSink.cpp +++ b/content/media/AudioSink.cpp @@ -134,21 +134,23 @@ AudioSink::AudioLoop() SINK_LOG("AudioLoop started"); if (NS_FAILED(InitializeAudioStream())) { NS_WARNING("Initializing AudioStream failed."); return; } while (1) { - WaitForAudioToPlay(); - if (!IsPlaybackContinuing()) { - break; + { + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + WaitForAudioToPlay(); + if (!IsPlaybackContinuing()) { + break; + } } - // See if there's a gap in the audio. If there is, push silence into the // audio hardware, so we can play across the gap. // Calculate the timestamp of the next chunk of audio in numbers of // samples. NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play"); CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate); // Calculate the number of frames that have been pushed onto the audio hardware. @@ -171,17 +173,18 @@ AudioSink::AudioLoop() mWritten += PlayFromAudioQueue(); } int64_t endTime = GetEndTime(); if (endTime != -1) { mStateMachine->OnAudioEndTimeUpdate(endTime); } } ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - if (!mStopAudioThread && AudioQueue().AtEndOfStream()) { + MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream()); + if (!mStopAudioThread && mPlaying) { Drain(); } SINK_LOG("AudioLoop complete"); Cleanup(); SINK_LOG("AudioLoop exit"); } nsresult @@ -233,29 +236,29 @@ AudioSink::ExpectMoreAudioData() return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished(); } void AudioSink::WaitForAudioToPlay() { // Wait while we're not playing, and we're not shutting down, or we're // playing and we've got no audio to play. - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + AssertCurrentThreadInMonitor(); while (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) { if (!mPlaying && !mAudioStream->IsPaused()) { mAudioStream->Pause(); } - mon.Wait(); + GetReentrantMonitor().Wait(); } } bool AudioSink::IsPlaybackContinuing() { - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + AssertCurrentThreadInMonitor(); if (mPlaying && mAudioStream->IsPaused()) { mAudioStream->Resume(); } // If we're shutting down, captured, or at EOS, break out and exit the audio // thread. if (mStopAudioThread || AudioQueue().AtEndOfStream()) { return false;
--- a/content/media/AudioStream.cpp +++ b/content/media/AudioStream.cpp @@ -545,16 +545,23 @@ AudioStream::Init(int32_t aNumChannels, } // This code used to live inside AudioStream::Init(), but on Mac (others?) // it has been known to take 300-800 (or even 8500) ms to execute(!) nsresult AudioStream::OpenCubeb(cubeb_stream_params &aParams, LatencyRequest aLatencyRequest) { + { + MonitorAutoLock mon(mMonitor); + if (mState == AudioStream::SHUTDOWN) { + return NS_ERROR_FAILURE; + } + } + cubeb* cubebContext = GetCubebContext(); if (!cubebContext) { MonitorAutoLock mon(mMonitor); mState = AudioStream::ERRORED; return NS_ERROR_FAILURE; } // If the latency pref is set, use it. Otherwise, if this stream is intended
--- a/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp +++ b/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp @@ -133,23 +133,21 @@ ISOMediaWriter::WriteEncodedTrack(const // inside the for-loop. if (frag && (aFlags & END_OF_STREAM)) { frag->SetEndOfStream(); } nsresult rv; bool EOS; if (ReadyToRunState(EOS)) { - // TODO: - // The MediaEncoder doesn't use nsRunnable, so thread will be - // stocked on that part and the new added nsRunnable won't get to run - // before MediaEncoder completing. Before MediaEncoder change, it needs - // to call RunState directly. - // https://bugzilla.mozilla.org/show_bug.cgi?id=950429 - rv = RunState(); + // Because track encoder won't generate new data after EOS, it needs to make + // sure the state reaches MUXING_DONE when EOS is signaled. + do { + rv = RunState(); + } while (EOS && mState != MUXING_DONE); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } bool ISOMediaWriter::ReadyToRunState(bool& aEOS)
--- a/content/media/fmp4/BlankDecoderModule.cpp +++ b/content/media/fmp4/BlankDecoderModule.cpp @@ -72,16 +72,17 @@ public: return NS_OK; } virtual nsresult Flush() MOZ_OVERRIDE { return NS_OK; } virtual nsresult Drain() MOZ_OVERRIDE { + mCallback->DrainComplete(); return NS_OK; } private: nsAutoPtr<BlankMediaDataCreator> mCreator; nsAutoPtr<MediaData> mOutput; RefPtr<MediaTaskQueue> mTaskQueue; MediaDataDecoderCallback* mCallback;
--- a/content/media/fmp4/MP4Reader.cpp +++ b/content/media/fmp4/MP4Reader.cpp @@ -344,51 +344,55 @@ MP4Reader::Decode(TrackType aTrack) // if we need output. while (prevNumFramesOutput == data.mNumSamplesOutput && (data.mInputExhausted || (data.mNumSamplesInput - data.mNumSamplesOutput) < data.mDecodeAhead)) { data.mMonitor.AssertCurrentThreadOwns(); data.mMonitor.Unlock(); nsAutoPtr<MP4Sample> compressed(PopSample(aTrack)); if (!compressed) { - // EOS, or error. Let the state machine know there are no more - // frames coming. + // EOS, or error. Send the decoder a signal to drain. LOG("Draining %s", TrackTypeToStr(aTrack)); + data.mMonitor.Lock(); + data.mDrainComplete = false; + data.mMonitor.Unlock(); data.mDecoder->Drain(); - return false; } else { #ifdef LOG_SAMPLE_DECODE LOG("PopSample %s time=%lld dur=%lld", TrackTypeToStr(aTrack), compressed->composition_timestamp, compressed->duration); #endif - } - data.mMonitor.Lock(); - data.mInputExhausted = false; - data.mNumSamplesInput++; - data.mMonitor.Unlock(); + data.mMonitor.Lock(); + data.mDrainComplete = false; + data.mInputExhausted = false; + data.mNumSamplesInput++; + data.mMonitor.Unlock(); - if (NS_FAILED(data.mDecoder->Input(compressed))) { - return false; + if (NS_FAILED(data.mDecoder->Input(compressed))) { + return false; + } + // If Input() failed, we let the auto pointer delete |compressed|. + // Otherwise, we assume the decoder will delete it when it's finished + // with it. + compressed.forget(); } - // If Input() failed, we let the auto pointer delete |compressed|. - // Otherwise, we assume the decoder will delete it when it's finished - // with it. - compressed.forget(); data.mMonitor.Lock(); } data.mMonitor.AssertCurrentThreadOwns(); while (!data.mError && prevNumFramesOutput == data.mNumSamplesOutput && - !data.mInputExhausted ) { + !data.mInputExhausted && + !data.mDrainComplete) { data.mMonitor.Wait(); } } data.mMonitor.AssertCurrentThreadOwns(); + bool drainComplete = data.mDrainComplete; data.mMonitor.Unlock(); - return true; + return !drainComplete; } void MP4Reader::Output(TrackType aTrack, MediaData* aSample) { #ifdef LOG_SAMPLE_DECODE LOG("Decoded %s sample time=%lld dur=%lld", TrackTypeToStr(aTrack), aSample->mTime, aSample->mDuration); @@ -417,16 +421,25 @@ MP4Reader::Output(TrackType aTrack, Medi break; } data.mNumSamplesOutput++; mon.NotifyAll(); } void +MP4Reader::DrainComplete(TrackType aTrack) +{ + DecoderData& data = GetDecoderData(aTrack); + MonitorAutoLock mon(data.mMonitor); + data.mDrainComplete = true; + mon.NotifyAll(); +} + +void MP4Reader::InputExhausted(TrackType aTrack) { DecoderData& data = GetDecoderData(aTrack); MonitorAutoLock mon(data.mMonitor); data.mInputExhausted = true; mon.NotifyAll(); }
--- a/content/media/fmp4/MP4Reader.h +++ b/content/media/fmp4/MP4Reader.h @@ -69,16 +69,17 @@ private: bool SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed); void Output(mp4_demuxer::TrackType aType, MediaData* aSample); void InputExhausted(mp4_demuxer::TrackType aTrack); void Error(mp4_demuxer::TrackType aTrack); bool Decode(mp4_demuxer::TrackType aTrack); void Flush(mp4_demuxer::TrackType aTrack); + void DrainComplete(mp4_demuxer::TrackType aTrack); nsAutoPtr<mp4_demuxer::MP4Demuxer> mDemuxer; nsAutoPtr<PlatformDecoderModule> mPlatform; class DecoderCallback : public MediaDataDecoderCallback { public: DecoderCallback(MP4Reader* aReader, mp4_demuxer::TrackType aType) @@ -90,32 +91,36 @@ private: mReader->Output(mType, aSample); } virtual void InputExhausted() MOZ_OVERRIDE { mReader->InputExhausted(mType); } virtual void Error() MOZ_OVERRIDE { mReader->Error(mType); } + virtual void DrainComplete() MOZ_OVERRIDE { + mReader->DrainComplete(mType); + } private: MP4Reader* mReader; mp4_demuxer::TrackType mType; }; struct DecoderData { DecoderData(const char* aMonitorName, uint32_t aDecodeAhead) : mMonitor(aMonitorName) , mNumSamplesInput(0) , mNumSamplesOutput(0) , mDecodeAhead(aDecodeAhead) , mActive(false) , mInputExhausted(false) , mError(false) , mIsFlushing(false) + , mDrainComplete(false) { } // The platform decoder. RefPtr<MediaDataDecoder> mDecoder; // TaskQueue on which decoder can choose to decode. // Only non-null up until the decoder is created. RefPtr<MediaTaskQueue> mTaskQueue; @@ -127,16 +132,17 @@ private: uint64_t mNumSamplesInput; uint64_t mNumSamplesOutput; uint32_t mDecodeAhead; // Whether this stream exists in the media. bool mActive; bool mInputExhausted; bool mError; bool mIsFlushing; + bool mDrainComplete; }; DecoderData mAudio; DecoderData mVideo; // Queued frame extracted by the demuxer, but not yet sent to the platform // decoder. nsAutoPtr<mp4_demuxer::MP4Sample> mQueuedVideoSample; // The last number of decoded output frames that we've reported to
--- a/content/media/fmp4/PlatformDecoderModule.h +++ b/content/media/fmp4/PlatformDecoderModule.h @@ -124,16 +124,18 @@ public: // Denotes an error in the decoding process. The reader will stop calling // the decoder. virtual void Error() = 0; // Denotes that the last input sample has been inserted into the decoder, // and no more output can be produced unless more input is sent. virtual void InputExhausted() = 0; + + virtual void DrainComplete() = 0; }; // MediaDataDecoder is the interface exposed by decoders created by the // PlatformDecoderModule's Create*Decoder() functions. The type of // media data that the decoder accepts as valid input and produces as // output is determined when the MediaDataDecoder is created. // // All functions must be threadsafe, and be able to be called on an @@ -168,24 +170,26 @@ public: // this function returns, the decoder must be ready to accept new input // for decoding. This function is called when the demuxer seeks, before // decoding resumes after the seek. // While the reader calls Flush(), it ignores all output sent to it; // it is safe (but pointless) to send output while Flush is called. // The MP4Reader will not call Input() while it's calling Flush(). virtual nsresult Flush() = 0; + // Causes all complete samples in the pipeline that can be decoded to be // output. If the decoder can't produce samples from the current output, // it drops the input samples. The decoder may be holding onto samples // that are required to decode samples that it expects to get in future. // This is called when the demuxer reaches end of stream. // The MP4Reader will not call Input() while it's calling Drain(). - // This function is synchronous. Once it's returned, all samples to be - // output should have been returned via callback to the MP4Reader. + // This function is asynchronous. The MediaDataDecoder must call + // MediaDataDecoderCallback::DrainComplete() once all remaining + // samples have been output. virtual nsresult Drain() = 0; // Cancels all init/input/drain operations, and shuts down the // decoder. The platform decoder should clean up any resources it's using // and release memory etc. Shutdown() must block until the decoder has // completed shutdown. The reader calls Flush() before calling Shutdown(). // The reader will delete the decoder once Shutdown() returns. // The MediaDataDecoderCallback *must* not be called after Shutdown() has
--- a/content/media/fmp4/ffmpeg/FFmpegAACDecoder.cpp +++ b/content/media/fmp4/ffmpeg/FFmpegAACDecoder.cpp @@ -112,17 +112,17 @@ FFmpegAACDecoder<LIBAV_VER>::Input(MP4Sa this, &FFmpegAACDecoder::DecodePacket, nsAutoPtr<MP4Sample>(aSample))); return NS_OK; } nsresult FFmpegAACDecoder<LIBAV_VER>::Drain() { - // AAC is never delayed; nothing to do here. + mCallback->DrainComplete(); return NS_OK; } FFmpegAACDecoder<LIBAV_VER>::~FFmpegAACDecoder() { MOZ_COUNT_DTOR(FFmpegAACDecoder); }
--- a/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp +++ b/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp @@ -218,29 +218,37 @@ FFmpegH264Decoder<LIBAV_VER>::Input(mp4_ mTaskQueue->Dispatch( NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample> >( this, &FFmpegH264Decoder<LIBAV_VER>::DecodeFrame, nsAutoPtr<mp4_demuxer::MP4Sample>(aSample))); return NS_OK; } +void +FFmpegH264Decoder<LIBAV_VER>::NotifyDrain() +{ + mCallback->DrainComplete(); +} + nsresult FFmpegH264Decoder<LIBAV_VER>::Drain() { // The maximum number of frames that can be waiting to be decoded is // max_b_frames + 1: One P frame and max_b_frames B frames. for (int32_t i = 0; i <= mCodecContext.max_b_frames; i++) { // An empty frame tells FFmpeg to decode the next delayed frame it has in // its queue, if it has any. nsAutoPtr<MP4Sample> empty(new MP4Sample()); nsresult rv = Input(empty.forget()); NS_ENSURE_SUCCESS(rv, rv); } + mTaskQueue->Dispatch( + NS_NewRunnableMethod(this, &FFmpegH264Decoder<LIBAV_VER>::NotifyDrain)); return NS_OK; } nsresult FFmpegH264Decoder<LIBAV_VER>::Flush() { nsresult rv = FFmpegDataDecoder::Flush();
--- a/content/media/fmp4/ffmpeg/FFmpegH264Decoder.h +++ b/content/media/fmp4/ffmpeg/FFmpegH264Decoder.h @@ -32,16 +32,17 @@ public: virtual nsresult Init() MOZ_OVERRIDE; virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE; virtual nsresult Drain() MOZ_OVERRIDE; virtual nsresult Flush() MOZ_OVERRIDE; private: void DecodeFrame(mp4_demuxer::MP4Sample* aSample); + void NotifyDrain(); void OutputDelayedFrames(); /** * This method allocates a buffer for FFmpeg's decoder, wrapped in an Image. * Currently it only supports Planar YUV420, which appears to be the only * non-hardware accelerated image format that FFmpeg's H264 decoder is * capable of outputting. */
--- a/content/media/fmp4/wmf/WMFMediaDataDecoder.cpp +++ b/content/media/fmp4/wmf/WMFMediaDataDecoder.cpp @@ -119,16 +119,17 @@ void WMFMediaDataDecoder::ProcessDrain() { // Order the decoder to drain... if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) { NS_WARNING("Failed to send DRAIN command to MFT"); } // Then extract all available output. ProcessOutput(); + mCallback->DrainComplete(); } nsresult WMFMediaDataDecoder::Drain() { mTaskQueue->Dispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain)); return NS_OK; }
new file mode 100644 --- /dev/null +++ b/content/media/gmp/GMPCallbackBase.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GMPCallbackBase_h_ +#define GMPCallbackBase_h_ + +class GMPCallbackBase +{ +public: + virtual ~GMPCallbackBase() {} + + // The GMP code will call this if the codec crashes or shuts down. It's + // expected that the consumer (destination of this callback) will respond + // by dropping their reference to the proxy, allowing the proxy/parent to + // be destroyed. + virtual void Terminated() = 0; +}; + +#endif
--- a/content/media/gmp/GMPChild.cpp +++ b/content/media/gmp/GMPChild.cpp @@ -42,27 +42,28 @@ GMPChild::~GMPChild() } bool GMPChild::Init(const std::string& aPluginPath, base::ProcessHandle aParentProcessHandle, MessageLoop* aIOLoop, IPC::Channel* aChannel) { -#ifdef GMP_CRASHREPORTER_READY -// See bug 1041226 + if (!Open(aChannel, aParentProcessHandle, aIOLoop)) { + return false; + } + #ifdef MOZ_CRASHREPORTER SendPCrashReporterConstructor(CrashReporter::CurrentThreadId()); #endif -#endif #if defined(XP_WIN) mozilla::SandboxTarget::Instance()->StartSandbox(); #endif - return LoadPluginLibrary(aPluginPath) && - Open(aChannel, aParentProcessHandle, aIOLoop); + + return LoadPluginLibrary(aPluginPath); } bool GMPChild::LoadPluginLibrary(const std::string& aPluginPath) { nsDependentCString pluginPath(aPluginPath.c_str()); nsCOMPtr<nsIFile> libFile;
--- a/content/media/gmp/GMPParent.cpp +++ b/content/media/gmp/GMPParent.cpp @@ -1,14 +1,15 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GMPParent.h" +#include "prlog.h" #include "nsComponentManagerUtils.h" #include "nsComponentManagerUtils.h" #include "nsIInputStream.h" #include "nsILineInputStream.h" #include "nsNetUtil.h" #include "nsCharSeparatedTokenizer.h" #include "nsThreadUtils.h" #include "nsIRunnable.h" @@ -21,41 +22,66 @@ using mozilla::dom::CrashReporterParent; #ifdef MOZ_CRASHREPORTER using CrashReporter::AnnotationTable; using CrashReporter::GetIDFromMinidump; #endif namespace mozilla { + +#ifdef LOG +#undef LOG +#endif + +#ifdef PR_LOGGING +extern PRLogModuleInfo* GetGMPLog(); + +#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg) +#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg) +#else +#define LOGD(msg) +#define LOG(level, msg) +#endif + +#ifdef __CLASS__ +#undef __CLASS__ +#endif +#define __CLASS__ "GMPParent" + namespace gmp { GMPParent::GMPParent() : mState(GMPStateNotLoaded) , mProcess(nullptr) + , mDeleteProcessOnUnload(false) { } GMPParent::~GMPParent() { + // Can't Close or Destroy the process here, since destruction is MainThread only + MOZ_ASSERT(NS_IsMainThread()); } nsresult GMPParent::Init(nsIFile* aPluginDir) { MOZ_ASSERT(aPluginDir); MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); mDirectory = aPluginDir; nsAutoString leafname; nsresult rv = aPluginDir->GetLeafName(leafname); if (NS_FAILED(rv)) { return rv; } + LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, + NS_LossyConvertUTF16toASCII(leafname).get())); MOZ_ASSERT(leafname.Length() > 4); mName = Substring(leafname, 4); return ReadGMPMetaData(); } nsresult @@ -64,92 +90,107 @@ GMPParent::LoadProcess() MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!"); MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mState == GMPStateNotLoaded); nsAutoCString path; if (NS_FAILED(mDirectory->GetNativePath(path))) { return NS_ERROR_FAILURE; } + LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, path.get())); - mProcess = new GMPProcessParent(path.get()); - if (!mProcess->Launch(30 * 1000)) { - mProcess->Delete(); - mProcess = nullptr; - return NS_ERROR_FAILURE; - } + if (!mProcess) { + mProcess = new GMPProcessParent(path.get()); + if (!mProcess->Launch(30 * 1000)) { + mProcess->Delete(); + mProcess = nullptr; + return NS_ERROR_FAILURE; + } - bool opened = Open(mProcess->GetChannel(), mProcess->GetChildProcessHandle()); - if (!opened) { - mProcess->Delete(); - mProcess = nullptr; - return NS_ERROR_FAILURE; + bool opened = Open(mProcess->GetChannel(), mProcess->GetChildProcessHandle()); + if (!opened) { + mProcess->Delete(); + mProcess = nullptr; + return NS_ERROR_FAILURE; + } } mState = GMPStateLoaded; return NS_OK; } void GMPParent::CloseIfUnused() { MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); - if ((mState == GMPStateLoaded || + if ((mDeleteProcessOnUnload || + mState == GMPStateLoaded || mState == GMPStateUnloading) && mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty()) { Shutdown(); } } void -GMPParent::CloseActive() +GMPParent::CloseActive(bool aDieWhenUnloaded) { + LOGD(("%s::%s: %p state %d", __CLASS__, __FUNCTION__, this, mState)); + if (aDieWhenUnloaded) { + mDeleteProcessOnUnload = true; // don't allow this to go back... + } if (mState == GMPStateLoaded) { mState = GMPStateUnloading; } // Invalidate and remove any remaining API objects. for (uint32_t i = mVideoDecoders.Length(); i > 0; i--) { - mVideoDecoders[i - 1]->DecodingComplete(); + mVideoDecoders[i - 1]->Shutdown(); } // Invalidate and remove any remaining API objects. for (uint32_t i = mVideoEncoders.Length(); i > 0; i--) { - mVideoEncoders[i - 1]->EncodingComplete(); + mVideoEncoders[i - 1]->Shutdown(); } // Note: the shutdown of the codecs is async! don't kill // the plugin-container until they're all safely shut down via // CloseIfUnused(); CloseIfUnused(); } void GMPParent::Shutdown() { + LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty()); if (mState == GMPStateNotLoaded || mState == GMPStateClosing) { return; } - mState = GMPStateClosing; - Close(); - DeleteProcess(); + // XXX Get rid of mDeleteProcessOnUnload and do this on all Shutdowns once + // Bug 1043671 is fixed (backout this patch) + if (mDeleteProcessOnUnload) { + mState = GMPStateClosing; + DeleteProcess(); + } else { + mState = GMPStateNotLoaded; + } MOZ_ASSERT(mState == GMPStateNotLoaded); } void GMPParent::DeleteProcess() { - MOZ_ASSERT(mState == GMPStateClosing); + LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); + Close(); mProcess->Delete(); mProcess = nullptr; mState = GMPStateNotLoaded; } void GMPParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder) { @@ -256,16 +297,19 @@ GMPParent::GetGMPVideoDecoder(GMPVideoDe } // returned with one anonymous AddRef that locks it until Destroy PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor(); if (!pvdp) { return NS_ERROR_FAILURE; } GMPVideoDecoderParent *vdp = static_cast<GMPVideoDecoderParent*>(pvdp); + // This addref corresponds to the Proxy pointer the consumer is returned. + // It's dropped by calling Close() on the interface. + NS_ADDREF(vdp); *aGMPVD = vdp; mVideoDecoders.AppendElement(vdp); return NS_OK; } nsresult GMPParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE) @@ -277,16 +321,19 @@ GMPParent::GetGMPVideoEncoder(GMPVideoEn } // returned with one anonymous AddRef that locks it until Destroy PGMPVideoEncoderParent* pvep = SendPGMPVideoEncoderConstructor(); if (!pvep) { return NS_ERROR_FAILURE; } GMPVideoEncoderParent *vep = static_cast<GMPVideoEncoderParent*>(pvep); + // This addref corresponds to the Proxy pointer the consumer is returned. + // It's dropped by calling Close() on the interface. + NS_ADDREF(vep); *aGMPVE = vep; mVideoEncoders.AppendElement(vep); return NS_OK; } #ifdef MOZ_CRASHREPORTER void @@ -327,20 +374,20 @@ GMPNotifyObservers(nsAString& aData) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (obs) { nsString temp(aData); obs->NotifyObservers(nullptr, "gmp-plugin-crash", temp.get()); } } #endif - void GMPParent::ActorDestroy(ActorDestroyReason aWhy) { + LOGD(("%s::%s: %p (%d)", __CLASS__, __FUNCTION__, this, (int) aWhy)); #ifdef MOZ_CRASHREPORTER if (AbnormalShutdown == aWhy) { nsString dumpID; GetCrashID(dumpID); nsString id; // use the parent address to identify it // We could use any unique-to-the-parent value id.AppendInt(reinterpret_cast<uint64_t>(this)); @@ -351,17 +398,17 @@ GMPParent::ActorDestroy(ActorDestroyReas // NotifyObservers is mainthread-only NS_DispatchToMainThread(WrapRunnableNM(&GMPNotifyObservers, id), NS_DISPATCH_NORMAL); } #endif // warn us off trying to close again mState = GMPStateClosing; - CloseActive(); + CloseActive(false); // Normal Shutdown() will delete the process on unwind. if (AbnormalShutdown == aWhy) { NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &GMPParent::DeleteProcess)); } } mozilla::dom::PCrashReporterParent*
--- a/content/media/gmp/GMPParent.h +++ b/content/media/gmp/GMPParent.h @@ -59,17 +59,17 @@ public: nsresult Init(nsIFile* aPluginDir); nsresult LoadProcess(); // Called internally to close this if we don't need it void CloseIfUnused(); // Notify all active de/encoders that we are closing, either because of // normal shutdown or unexpected shutdown/crash. - void CloseActive(); + void CloseActive(bool aDieWhenUnloaded); // Called by the GMPService to forcibly close active de/encoders at shutdown void Shutdown(); // This must not be called while we're in the middle of abnormal ActorDestroy void DeleteProcess(); bool SupportsAPI(const nsCString& aAPI, const nsCString& aTag); @@ -127,16 +127,17 @@ private: GMPState mState; nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk nsString mName; // base name of plugin on disk, UTF-16 because used for paths nsCString mDisplayName; // name of plugin displayed to users nsCString mDescription; // description of plugin for display to users nsCString mVersion; nsTArray<nsAutoPtr<GMPCapability>> mCapabilities; GMPProcessParent* mProcess; + bool mDeleteProcessOnUnload; nsTArray<nsRefPtr<GMPVideoDecoderParent>> mVideoDecoders; nsTArray<nsRefPtr<GMPVideoEncoderParent>> mVideoEncoders; #ifdef DEBUG nsCOMPtr<nsIThread> mGMPThread; #endif // Origin the plugin is assigned to, or empty if the the plugin is not // assigned to an origin.
--- a/content/media/gmp/GMPService.cpp +++ b/content/media/gmp/GMPService.cpp @@ -1,27 +1,50 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GMPService.h" +#include "prlog.h" #include "GMPParent.h" #include "GMPVideoDecoderParent.h" #include "nsIObserverService.h" #include "GeckoChildProcessHost.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/SyncRunnable.h" #include "nsXPCOMPrivate.h" #include "mozilla/Services.h" #include "nsNativeCharsetUtils.h" #include "nsIConsoleService.h" #include "mozilla/unused.h" namespace mozilla { + +#ifdef LOG +#undef LOG +#endif + +#ifdef PR_LOGGING +PRLogModuleInfo* +GetGMPLog() +{ + static PRLogModuleInfo *sLog; + if (!sLog) + sLog = PR_NewLogModule("GMP"); + return sLog; +} + +#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg) +#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg) +#else +#define LOGD(msg) +#define LOG(leve1, msg) +#endif + namespace gmp { static StaticRefPtr<GeckoMediaPluginService> sSingletonService; class GMPServiceCreateHelper MOZ_FINAL : public nsRunnable { nsRefPtr<GeckoMediaPluginService> mService; @@ -198,20 +221,25 @@ GeckoMediaPluginService::GetGMPVideoDeco if (mShuttingDownOnGMPThread) { return NS_ERROR_FAILURE; } nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aOrigin, NS_LITERAL_CSTRING("decode-video"), *aTags); +#ifdef PR_LOGGING + nsCString api = (*aTags)[0]; + LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get())); +#endif if (!gmp) { return NS_ERROR_FAILURE; } + GMPVideoDecoderParent* gmpVDP; nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP); if (NS_FAILED(rv)) { return rv; } *aGMPVD = gmpVDP; *aOutVideoHost = &gmpVDP->Host(); @@ -232,16 +260,20 @@ GeckoMediaPluginService::GetGMPVideoEnco if (mShuttingDownOnGMPThread) { return NS_ERROR_FAILURE; } nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aOrigin, NS_LITERAL_CSTRING("encode-video"), *aTags); +#ifdef PR_LOGGING + nsCString api = (*aTags)[0]; + LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get())); +#endif if (!gmp) { return NS_ERROR_FAILURE; } GMPVideoEncoderParent* gmpVEP; nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP); if (NS_FAILED(rv)) { return rv; @@ -260,17 +292,17 @@ GeckoMediaPluginService::UnloadPlugins() MOZ_ASSERT(!mShuttingDownOnGMPThread); mShuttingDownOnGMPThread = true; MutexAutoLock lock(mMutex); // Note: CloseActive is async; it will actually finish // shutting down when all the plugins have unloaded. for (uint32_t i = 0; i < mPlugins.Length(); i++) { - mPlugins[i]->CloseActive(); + mPlugins[i]->CloseActive(true); } mPlugins.Clear(); } void GeckoMediaPluginService::LoadFromEnvironment() { MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); @@ -441,17 +473,17 @@ GeckoMediaPluginService::RemoveOnGMPThre return; } MutexAutoLock lock(mMutex); for (uint32_t i = 0; i < mPlugins.Length(); ++i) { nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory(); bool equals; if (NS_SUCCEEDED(directory->Equals(pluginpath, &equals)) && equals) { - mPlugins[i]->CloseActive(); + mPlugins[i]->CloseActive(true); mPlugins.RemoveElementAt(i); return; } } NS_WARNING("Removing GMP which was never added."); nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); cs->LogStringMessage(MOZ_UTF16("Removing GMP which was never added.")); }
--- a/content/media/gmp/GMPVideoDecoderParent.cpp +++ b/content/media/gmp/GMPVideoDecoderParent.cpp @@ -1,36 +1,61 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GMPVideoDecoderParent.h" +#include "prlog.h" +#include "mozilla/unused.h" +#include "nsAutoRef.h" +#include "nsThreadUtils.h" #include "GMPVideoEncodedFrameImpl.h" #include "GMPVideoi420FrameImpl.h" #include "GMPParent.h" -#include <stdio.h> -#include "mozilla/unused.h" #include "GMPMessageUtils.h" -#include "nsAutoRef.h" -#include "nsThreadUtils.h" #include "mozilla/gmp/GMPTypes.h" template <> class nsAutoRefTraits<GMPVideoEncodedFrame> : public nsPointerRefTraits<GMPVideoEncodedFrame> { public: static void Release(GMPVideoEncodedFrame* aFrame) { aFrame->Destroy(); } }; namespace mozilla { + +#ifdef LOG +#undef LOG +#endif + +#ifdef PR_LOGGING +extern PRLogModuleInfo* GetGMPLog(); + +#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg) +#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg) +#else +#define LOGD(msg) +#define LOG(level, msg) +#endif + namespace gmp { +// States: +// Initial: mIsOpen == false +// on InitDecode success -> Open +// on Shutdown -> Dead +// Open: mIsOpen == true +// on Close -> Dead +// on ActorDestroy -> Dead +// on Shutdown -> Dead +// Dead: mIsOpen == false + GMPVideoDecoderParent::GMPVideoDecoderParent(GMPParent* aPlugin) - : mCanSendMessages(true) + : mIsOpen(false) , mPlugin(aPlugin) , mCallback(nullptr) , mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST()) { MOZ_ASSERT(mPlugin); } GMPVideoDecoderParent::~GMPVideoDecoderParent() @@ -38,150 +63,173 @@ GMPVideoDecoderParent::~GMPVideoDecoderP } GMPVideoHostImpl& GMPVideoDecoderParent::Host() { return mVideoHost; } +// Note: may be called via Terminated() +void +GMPVideoDecoderParent::Close() +{ + LOGD(("%s: %p", __FUNCTION__, this)); + MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); + // Consumer is done with us; we can shut down. No more callbacks should + // be made to mCallback. Note: do this before Shutdown()! + mCallback = nullptr; + // Let Shutdown mark us as dead so it knows if we had been alive + + // In case this is the last reference + nsRefPtr<GMPVideoDecoderParent> kungfudeathgrip(this); + NS_RELEASE(kungfudeathgrip); + Shutdown(); +} + nsresult GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings, const nsTArray<uint8_t>& aCodecSpecific, - GMPVideoDecoderCallback* aCallback, + GMPVideoDecoderCallbackProxy* aCallback, int32_t aCoreCount) { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); + if (mIsOpen) { + NS_WARNING("Trying to re-init an in-use GMP video decoder!"); return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!aCallback) { return NS_ERROR_FAILURE; } mCallback = aCallback; if (!SendInitDecode(aCodecSettings, aCodecSpecific, aCoreCount)) { return NS_ERROR_FAILURE; } + mIsOpen = true; // Async IPC, we don't have access to a return value. return NS_OK; } nsresult GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame, bool aMissingFrames, const nsTArray<uint8_t>& aCodecSpecificInfo, int64_t aRenderTimeMs) { nsAutoRef<GMPVideoEncodedFrame> autoDestroy(aInputFrame); - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video decoder"); return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); auto inputFrameImpl = static_cast<GMPVideoEncodedFrameImpl*>(aInputFrame); - GMPVideoEncodedFrameData frameData; - inputFrameImpl->RelinquishFrameData(frameData); - // Very rough kill-switch if the plugin stops processing. If it's merely // hung and continues, we'll come back to life eventually. // 3* is because we're using 3 buffers per frame for i420 data for now. if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit || NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) { return NS_ERROR_FAILURE; } + GMPVideoEncodedFrameData frameData; + inputFrameImpl->RelinquishFrameData(frameData); + if (!SendDecode(frameData, aMissingFrames, aCodecSpecificInfo, aRenderTimeMs)) { return NS_ERROR_FAILURE; } // Async IPC, we don't have access to a return value. return NS_OK; } nsresult GMPVideoDecoderParent::Reset() { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video decoder"); return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendReset()) { return NS_ERROR_FAILURE; } // Async IPC, we don't have access to a return value. return NS_OK; } nsresult GMPVideoDecoderParent::Drain() { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video decoder"); return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendDrain()) { return NS_ERROR_FAILURE; } // Async IPC, we don't have access to a return value. return NS_OK; } // Note: Consider keeping ActorDestroy sync'd up when making changes here. nsresult -GMPVideoDecoderParent::DecodingComplete() +GMPVideoDecoderParent::Shutdown() { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); - return NS_ERROR_FAILURE; - } - + LOGD(("%s: %p", __FUNCTION__, this)); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); - mCanSendMessages = false; - - mCallback = nullptr; - + // Notify client we're gone! Won't occur after Close() + if (mCallback) { + mCallback->Terminated(); + mCallback = nullptr; + } mVideoHost.DoneWithAPI(); - unused << SendDecodingComplete(); + if (mIsOpen) { + // Don't send DecodingComplete if we died + mIsOpen = false; + unused << SendDecodingComplete(); + } return NS_OK; } -// Note: Keep this sync'd up with DecodingComplete +// Note: Keep this sync'd up with Shutdown void GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) { + mIsOpen = false; + if (mCallback) { + // May call Close() (and Shutdown()) immediately or with a delay + mCallback->Terminated(); + mCallback = nullptr; + } if (mPlugin) { // Ignore any return code. It is OK for this to fail without killing the process. mPlugin->VideoDecoderDestroyed(this); mPlugin = nullptr; } - mCanSendMessages = false; - mCallback = nullptr; mVideoHost.ActorDestroyed(); } void GMPVideoDecoderParent::CheckThread() { MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); } @@ -294,16 +342,18 @@ GMPVideoDecoderParent::AnswerNeedShmem(c Shmem* aMem) { ipc::Shmem mem; if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPFrameData, aFrameBufferSize, ipc::SharedMemory::TYPE_BASIC, &mem)) { + LOG(PR_LOG_ERROR, ("%s: Failed to get a shared mem buffer for Child! size %u", + __FUNCTION__, aFrameBufferSize)); return false; } *aMem = mem; mem = ipc::Shmem(); return true; } bool
--- a/content/media/gmp/GMPVideoDecoderParent.h +++ b/content/media/gmp/GMPVideoDecoderParent.h @@ -24,29 +24,30 @@ class GMPVideoDecoderParent MOZ_FINAL : , public GMPVideoDecoderProxy { public: NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent) GMPVideoDecoderParent(GMPParent *aPlugin); GMPVideoHostImpl& Host(); + nsresult Shutdown(); // GMPVideoDecoder + virtual void Close() MOZ_OVERRIDE; virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings, const nsTArray<uint8_t>& aCodecSpecific, - GMPVideoDecoderCallback* aCallback, + GMPVideoDecoderCallbackProxy* aCallback, int32_t aCoreCount) MOZ_OVERRIDE; virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame, bool aMissingFrames, const nsTArray<uint8_t>& aCodecSpecificInfo, int64_t aRenderTimeMs = -1) MOZ_OVERRIDE; virtual nsresult Reset() MOZ_OVERRIDE; virtual nsresult Drain() MOZ_OVERRIDE; - virtual nsresult DecodingComplete() MOZ_OVERRIDE; virtual const uint64_t ParentID() MOZ_OVERRIDE { return reinterpret_cast<uint64_t>(mPlugin.get()); } // GMPSharedMemManager virtual void CheckThread(); virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) { #ifdef GMP_SAFE_SHMEM return AllocShmem(aSize, aType, aMem); @@ -71,18 +72,18 @@ private: virtual bool RecvDrainComplete() MOZ_OVERRIDE; virtual bool RecvResetComplete() MOZ_OVERRIDE; virtual bool RecvError(const GMPErr& aError) MOZ_OVERRIDE; virtual bool RecvParentShmemForPool(Shmem& aEncodedBuffer) MOZ_OVERRIDE; virtual bool AnswerNeedShmem(const uint32_t& aFrameBufferSize, Shmem* aMem) MOZ_OVERRIDE; virtual bool Recv__delete__() MOZ_OVERRIDE; - bool mCanSendMessages; + bool mIsOpen; nsRefPtr<GMPParent> mPlugin; - GMPVideoDecoderCallback* mCallback; + GMPVideoDecoderCallbackProxy* mCallback; GMPVideoHostImpl mVideoHost; }; } // namespace gmp } // namespace mozilla #endif // GMPVideoDecoderParent_h_
--- a/content/media/gmp/GMPVideoDecoderProxy.h +++ b/content/media/gmp/GMPVideoDecoderProxy.h @@ -6,29 +6,48 @@ #ifndef GMPVideoDecoderProxy_h_ #define GMPVideoDecoderProxy_h_ #include "nsTArray.h" #include "gmp-video-decode.h" #include "gmp-video-frame-i420.h" #include "gmp-video-frame-encoded.h" +#include "GMPCallbackBase.h" + +class GMPVideoDecoderCallbackProxy : public GMPCallbackBase, + public GMPVideoDecoderCallback +{ +public: + virtual ~GMPVideoDecoderCallbackProxy() {} +}; + // A proxy to GMPVideoDecoder in the child process. // GMPVideoDecoderParent exposes this to users the GMP. // This enables Gecko to pass nsTArrays to the child GMP and avoid // an extra copy when doing so. + +// The consumer must call Close() when done with the codec, or when +// Terminated() is called by the GMP plugin indicating an abnormal shutdown +// of the underlying plugin. After calling Close(), the consumer must +// not access this again. + +// This interface is not thread-safe and must only be used from GMPThread. class GMPVideoDecoderProxy { public: virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings, const nsTArray<uint8_t>& aCodecSpecific, - GMPVideoDecoderCallback* aCallback, + GMPVideoDecoderCallbackProxy* aCallback, int32_t aCoreCount) = 0; virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame, bool aMissingFrames, const nsTArray<uint8_t>& aCodecSpecificInfo, int64_t aRenderTimeMs = -1) = 0; virtual nsresult Reset() = 0; virtual nsresult Drain() = 0; - virtual nsresult DecodingComplete() = 0; virtual const uint64_t ParentID() = 0; + + // Call to tell GMP/plugin the consumer will no longer use this + // interface/codec. + virtual void Close() = 0; }; #endif
--- a/content/media/gmp/GMPVideoEncoderParent.cpp +++ b/content/media/gmp/GMPVideoEncoderParent.cpp @@ -1,36 +1,66 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GMPVideoEncoderParent.h" +#include "prlog.h" #include "GMPVideoi420FrameImpl.h" #include "GMPVideoEncodedFrameImpl.h" -#include <stdio.h> #include "mozilla/unused.h" #include "GMPMessageUtils.h" #include "nsAutoRef.h" #include "GMPParent.h" #include "mozilla/gmp/GMPTypes.h" #include "nsThreadUtils.h" template <> class nsAutoRefTraits<GMPVideoi420Frame> : public nsPointerRefTraits<GMPVideoi420Frame> { public: static void Release(GMPVideoi420Frame* aFrame) { aFrame->Destroy(); } }; namespace mozilla { + +#ifdef LOG +#undef LOG +#endif + +#ifdef PR_LOGGING +extern PRLogModuleInfo* GetGMPLog(); + +#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg) +#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg) +#else +#define LOGD(msg) +#define LOG(level, msg) +#endif + +#ifdef __CLASS__ +#undef __CLASS__ +#endif +#define __CLASS__ "GMPVideoEncoderParent" + namespace gmp { +// States: +// Initial: mIsOpen == false +// on InitDecode success -> Open +// on Shutdown -> Dead +// Open: mIsOpen == true +// on Close -> Dead +// on ActorDestroy -> Dead +// on Shutdown -> Dead +// Dead: mIsOpen == false + GMPVideoEncoderParent::GMPVideoEncoderParent(GMPParent *aPlugin) -: mCanSendMessages(true), +: mIsOpen(false), mPlugin(aPlugin), mCallback(nullptr), mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST()) { MOZ_ASSERT(mPlugin); } GMPVideoEncoderParent::~GMPVideoEncoderParent() @@ -38,84 +68,103 @@ GMPVideoEncoderParent::~GMPVideoEncoderP } GMPVideoHostImpl& GMPVideoEncoderParent::Host() { return mVideoHost; } +// Note: may be called via Terminated() +void +GMPVideoEncoderParent::Close() +{ + LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); + MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); + // Consumer is done with us; we can shut down. No more callbacks should + // be made to mCallback. Note: do this before Shutdown()! + mCallback = nullptr; + // Let Shutdown mark us as dead so it knows if we had been alive + + // In case this is the last reference + nsRefPtr<GMPVideoEncoderParent> kungfudeathgrip(this); + NS_RELEASE(kungfudeathgrip); + Shutdown(); +} + GMPErr GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings, const nsTArray<uint8_t>& aCodecSpecific, GMPVideoEncoderCallbackProxy* aCallback, int32_t aNumberOfCores, uint32_t aMaxPayloadSize) { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video encoder!"); - return GMPGenericErr; + LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); + if (mIsOpen) { + NS_WARNING("Trying to re-init an in-use GMP video encoder!"); + return GMPGenericErr;; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!aCallback) { return GMPGenericErr; } mCallback = aCallback; if (!SendInitEncode(aCodecSettings, aCodecSpecific, aNumberOfCores, aMaxPayloadSize)) { return GMPGenericErr; } + mIsOpen = true; // Async IPC, we don't have access to a return value. return GMPNoErr; } GMPErr GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame, const nsTArray<uint8_t>& aCodecSpecificInfo, const nsTArray<GMPVideoFrameType>& aFrameTypes) { nsAutoRef<GMPVideoi420Frame> frameRef(aInputFrame); - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video encoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video encoder"); return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); auto inputFrameImpl = static_cast<GMPVideoi420FrameImpl*>(aInputFrame); - GMPVideoi420FrameData frameData; - inputFrameImpl->InitFrameData(frameData); - // Very rough kill-switch if the plugin stops processing. If it's merely // hung and continues, we'll come back to life eventually. // 3* is because we're using 3 buffers per frame for i420 data for now. if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit || NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) { return GMPGenericErr; } + GMPVideoi420FrameData frameData; + inputFrameImpl->InitFrameData(frameData); + if (!SendEncode(frameData, aCodecSpecificInfo, aFrameTypes)) { return GMPGenericErr; } // Async IPC, we don't have access to a return value. return GMPNoErr; } GMPErr GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) { - if (!mCanSendMessages) { + if (!mIsOpen) { NS_WARNING("Trying to use an invalid GMP video encoder!"); return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendSetChannelParameters(aPacketLoss, aRTT)) { return GMPGenericErr; @@ -123,80 +172,85 @@ GMPVideoEncoderParent::SetChannelParamet // Async IPC, we don't have access to a return value. return GMPNoErr; } GMPErr GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video encoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video decoder"); return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendSetRates(aNewBitRate, aFrameRate)) { return GMPGenericErr; } // Async IPC, we don't have access to a return value. return GMPNoErr; } GMPErr GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable) { - if (!mCanSendMessages) { + if (!mIsOpen) { NS_WARNING("Trying to use an invalid GMP video encoder!"); return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendSetPeriodicKeyFrames(aEnable)) { return GMPGenericErr; } // Async IPC, we don't have access to a return value. return GMPNoErr; } // Note: Consider keeping ActorDestroy sync'd up when making changes here. void -GMPVideoEncoderParent::EncodingComplete() +GMPVideoEncoderParent::Shutdown() { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video encoder!"); - return; - } - + LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); - mCanSendMessages = false; - - mCallback = nullptr; - + // Notify client we're gone! Won't occur after Close() + if (mCallback) { + mCallback->Terminated(); + mCallback = nullptr; + } mVideoHost.DoneWithAPI(); - - unused << SendEncodingComplete(); + if (mIsOpen) { + // Don't send EncodingComplete if we died + mIsOpen = false; + unused << SendEncodingComplete(); + } } -// Note: Keep this sync'd up with DecodingComplete +// Note: Keep this sync'd up with Shutdown void GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy) { + LOGD(("%s::%s: %p (%d)", __CLASS__, __FUNCTION__, this, (int) aWhy)); + mIsOpen = false; + if (mCallback) { + // May call Close() (and Shutdown()) immediately or with a delay + mCallback->Terminated(); + mCallback = nullptr; + } if (mPlugin) { // Ignore any return code. It is OK for this to fail without killing the process. mPlugin->VideoEncoderDestroyed(this); mPlugin = nullptr; } - mCanSendMessages = false; - mCallback = nullptr; mVideoHost.ActorDestroyed(); } void GMPVideoEncoderParent::CheckThread() { MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); } @@ -247,16 +301,18 @@ GMPVideoEncoderParent::AnswerNeedShmem(c Shmem* aMem) { ipc::Shmem mem; if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData, aEncodedBufferSize, ipc::SharedMemory::TYPE_BASIC, &mem)) { + LOG(PR_LOG_ERROR, ("%s::%s: Failed to get a shared mem buffer for Child! size %u", + __CLASS__, __FUNCTION__, aEncodedBufferSize)); return false; } *aMem = mem; mem = ipc::Shmem(); return true; } bool
--- a/content/media/gmp/GMPVideoEncoderParent.h +++ b/content/media/gmp/GMPVideoEncoderParent.h @@ -24,30 +24,31 @@ class GMPVideoEncoderParent : public GMP public GMPSharedMemManager { public: NS_INLINE_DECL_REFCOUNTING(GMPVideoEncoderParent) GMPVideoEncoderParent(GMPParent *aPlugin); GMPVideoHostImpl& Host(); + void Shutdown(); // GMPVideoEncoderProxy + virtual void Close() MOZ_OVERRIDE; virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings, const nsTArray<uint8_t>& aCodecSpecific, GMPVideoEncoderCallbackProxy* aCallback, int32_t aNumberOfCores, uint32_t aMaxPayloadSize) MOZ_OVERRIDE; virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame, const nsTArray<uint8_t>& aCodecSpecificInfo, const nsTArray<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE; virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) MOZ_OVERRIDE; virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) MOZ_OVERRIDE; virtual GMPErr SetPeriodicKeyFrames(bool aEnable) MOZ_OVERRIDE; - virtual void EncodingComplete() MOZ_OVERRIDE; virtual const uint64_t ParentID() MOZ_OVERRIDE { return reinterpret_cast<uint64_t>(mPlugin.get()); } // GMPSharedMemManager virtual void CheckThread(); virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) { #ifdef GMP_SAFE_SHMEM return AllocShmem(aSize, aType, aMem); @@ -68,17 +69,17 @@ private: virtual bool RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame, const nsTArray<uint8_t>& aCodecSpecificInfo) MOZ_OVERRIDE; virtual bool RecvError(const GMPErr& aError) MOZ_OVERRIDE; virtual bool RecvParentShmemForPool(Shmem& aFrameBuffer) MOZ_OVERRIDE; virtual bool AnswerNeedShmem(const uint32_t& aEncodedBufferSize, Shmem* aMem) MOZ_OVERRIDE; virtual bool Recv__delete__() MOZ_OVERRIDE; - bool mCanSendMessages; + bool mIsOpen; nsRefPtr<GMPParent> mPlugin; GMPVideoEncoderCallbackProxy* mCallback; GMPVideoHostImpl mVideoHost; }; } // namespace gmp } // namespace mozilla
--- a/content/media/gmp/GMPVideoEncoderProxy.h +++ b/content/media/gmp/GMPVideoEncoderProxy.h @@ -6,37 +6,50 @@ #ifndef GMPVideoEncoderProxy_h_ #define GMPVideoEncoderProxy_h_ #include "nsTArray.h" #include "gmp-video-encode.h" #include "gmp-video-frame-i420.h" #include "gmp-video-frame-encoded.h" -class GMPVideoEncoderCallbackProxy { +#include "GMPCallbackBase.h" + +class GMPVideoEncoderCallbackProxy : public GMPCallbackBase { public: + virtual ~GMPVideoEncoderCallbackProxy() {} virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, const nsTArray<uint8_t>& aCodecSpecificInfo) = 0; virtual void Error(GMPErr aError) = 0; }; // A proxy to GMPVideoEncoder in the child process. // GMPVideoEncoderParent exposes this to users the GMP. // This enables Gecko to pass nsTArrays to the child GMP and avoid // an extra copy when doing so. + +// The consumer must call Close() when done with the codec, or when +// Terminated() is called by the GMP plugin indicating an abnormal shutdown +// of the underlying plugin. After calling Close(), the consumer must +// not access this again. + +// This interface is not thread-safe and must only be used from GMPThread. class GMPVideoEncoderProxy { public: virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings, const nsTArray<uint8_t>& aCodecSpecific, GMPVideoEncoderCallbackProxy* aCallback, int32_t aNumberOfCores, uint32_t aMaxPayloadSize) = 0; virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame, const nsTArray<uint8_t>& aCodecSpecificInfo, const nsTArray<GMPVideoFrameType>& aFrameTypes) = 0; virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0; virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0; virtual GMPErr SetPeriodicKeyFrames(bool aEnable) = 0; - virtual void EncodingComplete() = 0; virtual const uint64_t ParentID() = 0; + + // Call to tell GMP/plugin the consumer will no longer use this + // interface/codec. + virtual void Close() = 0; }; #endif // GMPVideoEncoderProxy_h_
--- a/content/media/gmp/moz.build +++ b/content/media/gmp/moz.build @@ -24,16 +24,17 @@ EXPORTS += [ 'gmp-api/gmp-video-codec.h', 'gmp-api/gmp-video-decode.h', 'gmp-api/gmp-video-encode.h', 'gmp-api/gmp-video-frame-encoded.h', 'gmp-api/gmp-video-frame-i420.h', 'gmp-api/gmp-video-frame.h', 'gmp-api/gmp-video-host.h', 'gmp-api/gmp-video-plane.h', + 'GMPCallbackBase.h', 'GMPChild.h', 'GMPMessageUtils.h', 'GMPParent.h', 'GMPPlatform.h', 'GMPProcessChild.h', 'GMPProcessParent.h', 'GMPService.h', 'GMPSharedMemManager.h',
--- a/content/media/omx/OmxDecoder.cpp +++ b/content/media/omx/OmxDecoder.cpp @@ -506,25 +506,45 @@ bool OmxDecoder::AllocateMediaResources( return false; } } return true; } void OmxDecoder::ReleaseMediaResources() { + ReleaseVideoBuffer(); + ReleaseAudioBuffer(); + + { + Mutex::Autolock autoLock(mPendingVideoBuffersLock); + MOZ_ASSERT(mPendingRecycleTexutreClients.empty()); + // Release all pending recycle TextureClients, if they are not recycled yet. + // This should not happen. See Bug 1042308. + if (!mPendingRecycleTexutreClients.empty()) { + printf_stderr("OmxDecoder::ReleaseMediaResources -- TextureClients are not recycled yet\n"); + for (std::set<TextureClient*>::iterator it=mPendingRecycleTexutreClients.begin(); + it!=mPendingRecycleTexutreClients.end(); it++) + { + GrallocTextureClientOGL* client = static_cast<GrallocTextureClientOGL*>(*it); + client->ClearRecycleCallback(); + if (client->GetMediaBuffer()) { + mPendingVideoBuffers.push(BufferItem(client->GetMediaBuffer(), client->GetReleaseFenceHandle())); + } + } + mPendingRecycleTexutreClients.clear(); + } + } + { // Free all pending video buffers. Mutex::Autolock autoLock(mSeekLock); ReleaseAllPendingVideoBuffersLocked(); } - ReleaseVideoBuffer(); - ReleaseAudioBuffer(); - if (mVideoSource.get()) { mVideoSource->stop(); mVideoSource.clear(); } if (mAudioSource.get()) { mAudioSource->stop(); mAudioSource.clear(); @@ -769,16 +789,22 @@ bool OmxDecoder::ReadVideo(VideoFrame *a if (textureClient) { // Manually increment reference count to keep MediaBuffer alive // during TextureClient is in use. mVideoBuffer->add_ref(); GrallocTextureClientOGL* grallocClient = static_cast<GrallocTextureClientOGL*>(textureClient.get()); grallocClient->SetMediaBuffer(mVideoBuffer); // Set recycle callback for TextureClient textureClient->SetRecycleCallback(OmxDecoder::RecycleCallback, this); + { + Mutex::Autolock autoLock(mPendingVideoBuffersLock); + // Store pending recycle TextureClient. + MOZ_ASSERT(mPendingRecycleTexutreClients.find(textureClient) == mPendingRecycleTexutreClients.end()); + mPendingRecycleTexutreClients.insert(textureClient); + } aFrame->mGraphicBuffer = textureClient; aFrame->mRotation = mVideoRotation; aFrame->mTimeUs = timeUs; aFrame->mKeyFrame = keyFrame; aFrame->Y.mWidth = mVideoWidth; aFrame->Y.mHeight = mVideoHeight; // Release to hold video buffer in OmxDecoder more. @@ -1019,24 +1045,42 @@ void OmxDecoder::ReleaseAllPendingVideoB metaData->setInt32(kKeyRendered, 1); #endif // Return MediaBuffer to OMXCodec. buffer->release(); } releasingVideoBuffers.clear(); } +void OmxDecoder::RecycleCallbackImp(TextureClient* aClient) +{ + aClient->ClearRecycleCallback(); + { + Mutex::Autolock autoLock(mPendingVideoBuffersLock); + if (mPendingRecycleTexutreClients.find(aClient) == mPendingRecycleTexutreClients.end()) { + printf_stderr("OmxDecoder::RecycleCallbackImp -- TextureClient is not pending recycle\n"); + return; + } + mPendingRecycleTexutreClients.erase(aClient); + GrallocTextureClientOGL* client = static_cast<GrallocTextureClientOGL*>(aClient); + if (client->GetMediaBuffer()) { + mPendingVideoBuffers.push(BufferItem(client->GetMediaBuffer(), client->GetReleaseFenceHandle())); + } + } + sp<AMessage> notify = + new AMessage(kNotifyPostReleaseVideoBuffer, mReflector->id()); + // post AMessage to OmxDecoder via ALooper. + notify->post(); +} + /* static */ void OmxDecoder::RecycleCallback(TextureClient* aClient, void* aClosure) { OmxDecoder* decoder = static_cast<OmxDecoder*>(aClosure); - GrallocTextureClientOGL* client = static_cast<GrallocTextureClientOGL*>(aClient); - - aClient->ClearRecycleCallback(); - decoder->PostReleaseVideoBuffer(client->GetMediaBuffer(), client->GetReleaseFenceHandle()); + decoder->RecycleCallbackImp(aClient); } int64_t OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion) { // We read data in chunks of 32 KiB. We can reduce this // value if media, such as sdcards, is too slow. // Because of SD card's slowness, need to keep sReadSize to small size. // See Bug 914870.
--- a/content/media/omx/OmxDecoder.h +++ b/content/media/omx/OmxDecoder.h @@ -1,8 +1,9 @@ +#include <set> #include <stagefright/foundation/ABase.h> #include <stagefright/foundation/AHandlerReflector.h> #include <stagefright/foundation/ALooper.h> #include <utils/RefBase.h> #include <stagefright/MediaExtractor.h> #include "GonkNativeWindow.h" #include "GonkNativeWindowClient.h" @@ -84,17 +85,21 @@ class OmxDecoder : public OMXCodecProxy: FenceHandle mReleaseFenceHandle; }; // Hold video's MediaBuffers that are released during video seeking. // The holded MediaBuffers are released soon after seek completion. // OMXCodec does not accept MediaBuffer during seeking. If MediaBuffer is // returned to OMXCodec during seeking, OMXCodec calls assert. Vector<BufferItem> mPendingVideoBuffers; - // The lock protects mPendingVideoBuffers. + + // Hold TextureClients that are waiting to be recycled. + std::set<TextureClient*> mPendingRecycleTexutreClients; + + // The lock protects mPendingVideoBuffers and mPendingRecycleTexutreClients. Mutex mPendingVideoBuffersLock; // Show if OMXCodec is seeking. bool mIsVideoSeeking; // The lock protects video MediaBuffer release()'s pending operations called // from multiple threads. The pending operations happen only during video // seeking. Holding mSeekLock long time could affect to video rendering. // Holding time should be minimum. @@ -204,13 +209,15 @@ public: // Receive a message from AHandlerReflector. // Called on ALooper thread. void onMessageReceived(const sp<AMessage> &msg); int64_t ProcessCachedData(int64_t aOffset, bool aWaitForCompletion); sp<MediaSource> GetAudioOffloadTrack() { return mAudioOffloadTrack; } + void RecycleCallbackImp(TextureClient* aClient); + static void RecycleCallback(TextureClient* aClient, void* aClosure); }; }
--- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -61,16 +61,20 @@ #include "private/pprio.h" #include "nsContentPermissionHelper.h" #include "mozilla/dom/DeviceStorageBinding.h" // Microsoft's API Name hackery sucks #undef CreateEvent +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#endif + #ifdef MOZ_WIDGET_GONK #include "nsIVolume.h" #include "nsIVolumeService.h" #endif #define DEVICESTORAGE_PROPERTIES \ "chrome://global/content/devicestorage.properties" #define DEFAULT_THREAD_TIMEOUT_MS 30000 @@ -632,24 +636,26 @@ OverrideRootDir::GetSingleton() return sSingleton; } // Preference changes are automatically forwarded from parent to child // in ContentParent::Observe, so we'll see the change in both the parent // and the child process. sSingleton = new OverrideRootDir(); Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir"); + Preferences::AddStrongObserver(sSingleton, "device.storage.testing"); ClearOnShutdown(&sSingleton); return sSingleton; } OverrideRootDir::~OverrideRootDir() { Preferences::RemoveObserver(this, "device.storage.overrideRootDir"); + Preferences::RemoveObserver(this, "device.storage.testing"); } NS_IMETHODIMP OverrideRootDir::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) { MOZ_ASSERT(NS_IsMainThread()); @@ -727,55 +733,86 @@ InitDirs() ClearOnShutdown(&sDirs); nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); MOZ_ASSERT(dirService); #if !defined(MOZ_WIDGET_GONK) +// Keep MOZ_WIDGET_COCOA above XP_UNIX, +// because both are defined in Darwin builds. #if defined (MOZ_WIDGET_COCOA) dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures)); dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos)); dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music)); + +// Keep MOZ_WIDGET_ANDROID above XP_UNIX, +// because both are defined in Android builds. +#elif defined (MOZ_WIDGET_ANDROID) + nsAutoString path; + if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory( + NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), path))) { + NS_NewLocalFile(path, /* aFollowLinks */ true, + getter_AddRefs(sDirs->pictures)); + } + if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory( + NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS), path))) { + NS_NewLocalFile(path, /* aFollowLinks */ true, + getter_AddRefs(sDirs->videos)); + } + if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory( + NS_LITERAL_STRING(DEVICESTORAGE_MUSIC), path))) { + NS_NewLocalFile(path, /* aFollowLinks */ true, + getter_AddRefs(sDirs->music)); + } + if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory( + NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), path))) { + NS_NewLocalFile(path, /* aFollowLinks */ true, + getter_AddRefs(sDirs->sdcard)); + } + #elif defined (XP_UNIX) dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures)); dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos)); dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music)); + #elif defined (XP_WIN) dirService->Get(NS_WIN_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures)); dirService->Get(NS_WIN_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos)); dirService->Get(NS_WIN_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music)); #endif +#ifndef MOZ_WIDGET_ANDROID // Eventually, on desktop, we want to do something smarter -- for example, // detect when an sdcard is inserted, and use that instead of this. dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->sdcard)); if (sDirs->sdcard) { sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard")); } +#endif // !MOZ_WIDGET_ANDROID #endif // !MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK NS_NewLocalFile(NS_LITERAL_STRING("/data"), false, getter_AddRefs(sDirs->apps)); #else dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
--- a/dom/devicestorage/test/mochitest.ini +++ b/dom/devicestorage/test/mochitest.ini @@ -1,28 +1,29 @@ [DEFAULT] -skip-if = toolkit == 'android' || e10s #bug 781789 & bug 782275 +skip-if = e10s # bug 781789 & bug 782275 support-files = devicestorage_common.js [test_823965.html] # [test_add.html] # man, our mime database sucks hard. followup bug # 788273 [test_addCorrectType.html] [test_available.html] [test_basic.html] -[test_overrideDir.html] +[test_dirs.html] # [test_diskSpace.html] # Possible race between the time we write a file, and the # time it takes to be reflected by statfs(). Bug # 791287 [test_dotdot.html] [test_enumerate.html] [test_enumerateMultipleContinue.html] [test_enumerateOptions.html] [test_freeSpace.html] [test_lastModificationFilter.html] +[test_overrideDir.html] [test_overwrite.html] [test_sanity.html] [test_usedSpace.html] [test_watch.html] [test_watchOther.html] # FileSystem API tests [test_fs_basic.html]
new file mode 100644 --- /dev/null +++ b/dom/devicestorage/test/test_dirs.html @@ -0,0 +1,70 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=886627 +--> +<head> + <title>Test for the device storage API</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="devicestorage_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=886627"> + Mozilla Bug 886627 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + <script class="testbody" type="text/javascript"> + +/** + * Test that common device storage directories are available. + * + * This test differs from other device storage tests in that other tests use a + * "testing mode", which relocates the device storage directories to a testing + * directory. On the other hand, this test turns off testing mode to makes sure + * that the normal, non-testing directories also work properly. + */ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv({ + 'set': [ + ["device.storage.enabled", true], + ["device.storage.testing", false], + ["device.storage.prompt.testing", true], + ] +}, function() { + ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); + ok(!navigator.getDeviceStorage("nonexistent-type"), "Should not have nonexistent storage"); + + ok(navigator.getDeviceStorage("pictures"), "Should have pictures storage"); + ok(navigator.getDeviceStorage("videos"), "Should have videos storage"); + ok(navigator.getDeviceStorage("music"), "Should have music storage"); + + // Need special permission to access "apps". We always have the permission in B2G + // mochitests, but on other platforms, we need to manually add the permission. + if (!SpecialPowers.testPermission( + "webapps-manage", SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION, document)) { + ok(!navigator.getDeviceStorage("apps"), "Should not have apps storage without permission"); + SpecialPowers.addPermission("webapps-manage", true, document); + } + ok(navigator.getDeviceStorage("apps"), "Should have apps storage with permission"); + + ok(navigator.getDeviceStorage("sdcard"), "Should have sdcard storage"); + ok(navigator.getDeviceStorage("crashes"), "Should have crashes storage"); + + // The test harness reverts our pref changes automatically. + SimpleTest.finish(); +}); + + </script> + </pre> +</body> +</html> +
--- a/dom/encoding/Makefile.in +++ b/dom/encoding/Makefile.in @@ -1,15 +1,15 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. include $(topsrcdir)/config/rules.mk -PROPS2ARRAYS = $(topsrcdir)/intl/locale/src/props2arrays.py +PROPS2ARRAYS = $(topsrcdir)/intl/locale/props2arrays.py labelsencodings.properties.h: $(PROPS2ARRAYS) labelsencodings.properties $(PYTHON) $^ $@ localesfallbacks.properties.h: $(PROPS2ARRAYS) localesfallbacks.properties $(PYTHON) $^ $@ domainsfallbacks.properties.h: $(PROPS2ARRAYS) domainsfallbacks.properties $(PYTHON) $^ $@ encodingsgroups.properties.h: $(PROPS2ARRAYS) encodingsgroups.properties $(PYTHON) $^ $@
--- a/dom/encoding/moz.build +++ b/dom/encoding/moz.build @@ -19,17 +19,17 @@ UNIFIED_SOURCES += [ 'TextDecoder.cpp', 'TextEncoder.cpp', ] FAIL_ON_WARNINGS = True FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ - '/intl/locale/src', + '/intl/locale', ] GENERATED_FILES += [ 'domainsfallbacks.properties.h', 'encodingsgroups.properties.h', 'labelsencodings.properties.h', 'localesfallbacks.properties.h', 'nonparticipatingdomains.properties.h',
--- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3600,17 +3600,17 @@ CreateMouseOrPointerWidgetEvent(WidgetMo aMouseEvent->widget, WidgetMouseEvent::eReal); aNewEvent->relatedTarget = aRelatedContent; } aNewEvent->refPoint = aMouseEvent->refPoint; aNewEvent->modifiers = aMouseEvent->modifiers; aNewEvent->button = aMouseEvent->button; aNewEvent->buttons = aMouseEvent->buttons; aNewEvent->pressure = aMouseEvent->pressure; - aNewEvent->pluginEvent = aMouseEvent->pluginEvent; + aNewEvent->mPluginEvent = aMouseEvent->mPluginEvent; aNewEvent->inputSource = aMouseEvent->inputSource; } nsIFrame* EventStateManager::DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent, uint32_t aMessage, nsIContent* aTargetContent, nsIContent* aRelatedContent)
--- a/dom/events/test/test_bug603008.html +++ b/dom/events/test/test_bug603008.html @@ -422,17 +422,17 @@ function testPreventDefault() { { name: "touchend", prevent: true }], [{ name: "touchstart", prevent: false }, { name: "touchmove", prevent: false }, { name: "touchmove", prevent: false, doPrevent: true }, { name: "touchend", prevent: false }], [{ name: "touchstart", prevent: false }, { name: "touchmove", prevent: false }, { name: "touchmove", prevent: false }, - { name: "touchend", prevent: true, doPrevent: true }] + { name: "touchend", prevent: false, doPrevent: true }] ]; var dotest = function(aTest) { if (aTest.doPrevent) { target.addEventListener(aTest.name, preventFunction, false); } if (aTest.name == "touchmove") {
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl +++ b/dom/interfaces/base/nsIServiceWorkerManager.idl @@ -3,17 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "domstubs.idl" interface nsIDocument; interface nsIURI; -[uuid(6e1382f4-3cbc-435f-8ce0-70175f6eb400)] +[uuid(cc539f1e-1ce6-4af5-bf94-195b30bde010)] interface nsIServiceWorkerManager : nsISupports { // Returns a Promise nsISupports register(in nsIDOMWindow aWindow, in DOMString aScope, in DOMString aScriptURI); // Returns a Promise nsISupports unregister(in nsIDOMWindow aWindow, in DOMString aScope); @@ -31,15 +31,21 @@ interface nsIServiceWorkerManager : nsIS /** * Documents that have called MaybeStartControlling() should call this when * they are destroyed. This function may be called multiple times, and is * idempotent. */ [notxpcom,nostdcall] void MaybeStopControlling(in nsIDocument aDoc); + // Returns a ServiceWorker + [noscript] nsISupports GetInstalling(in nsIDOMWindow aWindow); + [noscript] nsISupports GetWaiting(in nsIDOMWindow aWindow); + [noscript] nsISupports GetActive(in nsIDOMWindow aWindow); + [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow); + // Testing DOMString getScopeForUrl(in DOMString path); }; %{ C++ #define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1" %}
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -724,17 +724,16 @@ TabChild::TabChild(nsIContentChild* aMan , mAppPackageFileDescriptorRecved(false) , mLastBackgroundColor(NS_RGB(255, 255, 255)) , mDidFakeShow(false) , mNotified(false) , mTriedBrowserInit(false) , mOrientation(eScreenOrientation_PortraitPrimary) , mUpdateHitRegion(false) , mPendingTouchPreventedResponse(false) - , mTouchEndIsClick(Unknown) , mIgnoreKeyPressEvent(false) , mActiveElementManager(new ActiveElementManager()) , mHasValidInnerSize(false) , mUniqueId(0) { if (!sActiveDurationMsSet) { Preferences::AddIntVarCache(&sActiveDurationMs, "ui.touch_activation.duration_ms", @@ -1811,20 +1810,16 @@ TabChild::RecvHandleDoubleTap(const CSSP bool TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid) { if (!mGlobal || !mTabChildGlobal) { return true; } - if (mTouchEndIsClick == IsNotClick) { - return true; - } - LayoutDevicePoint currentPoint = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();; MessageLoop::current()->PostDelayedTask( FROM_HERE, NewRunnableMethod(this, &TabChild::FireSingleTapEvent, currentPoint), sActiveDurationMs); return true; } @@ -1907,17 +1902,17 @@ TabChild::RecvNotifyAPZStateChange(const } case APZStateChange::StartPanning: { mActiveElementManager->HandlePanStart(); break; } case APZStateChange::EndTouch: { - mTouchEndIsClick = (aArg ? IsClick : IsNotClick); + mActiveElementManager->HandleTouchEnd(aArg); break; } default: // APZStateChange has a 'sentinel' value, and the compiler complains // if an enumerator is not handled and there is no 'default' case. break; } return true; @@ -2126,43 +2121,34 @@ TabChild::RecvRealTouchEvent(const Widge if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) { mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget()); } bool isTouchPrevented = nsIPresShell::gPreventMouseEvents || localEvent.mFlags.mMultipleActionsPrevented; switch (aEvent.message) { case NS_TOUCH_START: { - mTouchEndIsClick = Unknown; if (mPendingTouchPreventedResponse) { // We can enter here if we get two TOUCH_STARTs in a row and didn't // respond to the first one. Respond to it now. SendContentReceivedTouch(mPendingTouchPreventedGuid, false); mPendingTouchPreventedResponse = false; } if (isTouchPrevented) { SendContentReceivedTouch(aGuid, isTouchPrevented); } else { mPendingTouchPreventedResponse = true; mPendingTouchPreventedGuid = aGuid; } break; } + case NS_TOUCH_MOVE: case NS_TOUCH_END: - if (isTouchPrevented && mTouchEndIsClick == IsClick) { - mTouchEndIsClick = IsNotClick; - } - // fall through - case NS_TOUCH_CANCEL: - if (mTouchEndIsClick != Unknown) { - mActiveElementManager->HandleTouchEnd(mTouchEndIsClick == IsClick); - } - // fall through - case NS_TOUCH_MOVE: { + case NS_TOUCH_CANCEL: { SendPendingTouchPreventedResponse(isTouchPrevented, aGuid); break; } default: NS_WARNING("Unknown touch event type"); }
--- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -589,23 +589,16 @@ private: bool mNotified; bool mTriedBrowserInit; ScreenOrientation mOrientation; bool mUpdateHitRegion; bool mPendingTouchPreventedResponse; ScrollableLayerGuid mPendingTouchPreventedGuid; void FireSingleTapEvent(LayoutDevicePoint aPoint); - enum ClickState { - Unknown, - IsClick, - IsNotClick - }; - ClickState mTouchEndIsClick; - bool mIgnoreKeyPressEvent; nsRefPtr<ActiveElementManager> mActiveElementManager; bool mHasValidInnerSize; uint64_t mUniqueId; DISALLOW_EVIL_CONSTRUCTORS(TabChild); };
--- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -59,18 +59,17 @@ skip-if = toolkit == 'gonk' # b2g(Bug 96 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_basicVideo.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_basicScreenshare.html] skip-if = toolkit == 'gonk' || toolkit == 'android' # no screenshare on b2g/android [test_peerConnection_basicWindowshare.html] skip-if = toolkit == 'gonk' || toolkit == 'android' # no windowshare on b2g/android [test_peerConnection_basicH264Video.html] -skip-if = true # disabled until we can resolve plugin installation issues -#skip-if = toolkit == 'gonk' || toolkit == 'android' # no openh264 on b2g/android +skip-if = buildapp == 'b2g' || os == 'android' # bug 1043403 [test_peerConnection_bug822674.html] [test_peerConnection_bug825703.html] [test_peerConnection_bug827843.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_bug834153.html] [test_peerConnection_bug835370.html] [test_peerConnection_bug1013809.html] skip-if = (toolkit == 'gonk' && debug) # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -1807,17 +1807,17 @@ nsEventStatus nsPluginInstanceOwner::Pro nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget); if (!pluginWidget || NS_FAILED(pluginWidget->StartDrawPlugin())) return nsEventStatus_eIgnore; NPEventModel eventModel = GetEventModel(); // If we have to synthesize an event we'll use one of these. NPCocoaEvent synthCocoaEvent; - void* event = anEvent.pluginEvent; + const NPCocoaEvent* event = static_cast<const NPCocoaEvent*>(anEvent.mPluginEvent); nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) - mObjectFrame->GetContentRectRelativeToSelf().TopLeft(); nsPresContext* presContext = mObjectFrame->PresContext(); // Plugin event coordinates need to be translated from device pixels // into "display pixels" in HiDPI modes. double scaleFactor = 1.0; GetContentsScaleFactor(&scaleFactor); @@ -1874,17 +1874,19 @@ nsEventStatus nsPluginInstanceOwner::Pro pluginWidget->EndDrawPlugin(); return nsEventStatus_eIgnore; } } int16_t response = kNPEventNotHandled; void* window = FixUpPluginWindow(ePluginPaintEnable); if (window || (eventModel == NPEventModelCocoa)) { - mInstance->HandleEvent(event, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + mInstance->HandleEvent(const_cast<NPCocoaEvent*>(event), + &response, + NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); } if (eventModel == NPEventModelCocoa && response == kNPEventStartIME) { pluginWidget->StartComplexTextInputForCurrentEvent(); } if ((response == kNPEventHandled || response == kNPEventStartIME) && !(anEvent.message == NS_MOUSE_BUTTON_DOWN && @@ -1893,17 +1895,17 @@ nsEventStatus nsPluginInstanceOwner::Pro rv = nsEventStatus_eConsumeNoDefault; } pluginWidget->EndDrawPlugin(); #endif #ifdef XP_WIN // this code supports windowless plugins - NPEvent *pPluginEvent = (NPEvent*)anEvent.pluginEvent; + const NPEvent *pPluginEvent = static_cast<const NPEvent*>(anEvent.mPluginEvent); // we can get synthetic events from the EventStateManager... these // have no pluginEvent NPEvent pluginEvent; if (anEvent.eventStructType == NS_MOUSE_EVENT) { if (!pPluginEvent) { // XXX Should extend this list to synthesize events for more event // types pluginEvent.event = 0; @@ -1962,17 +1964,17 @@ nsEventStatus nsPluginInstanceOwner::Pro "Incorrect event type for coordinate translation"); nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) - mObjectFrame->GetContentRectRelativeToSelf().TopLeft(); nsPresContext* presContext = mObjectFrame->PresContext(); nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x), presContext->AppUnitsToDevPixels(pt.y)); nsIntPoint widgetPtPx = ptPx + mObjectFrame->GetWindowOriginInPixels(true); - pPluginEvent->lParam = MAKELPARAM(widgetPtPx.x, widgetPtPx.y); + const_cast<NPEvent*>(pPluginEvent)->lParam = MAKELPARAM(widgetPtPx.x, widgetPtPx.y); } } else if (!pPluginEvent) { switch (anEvent.message) { case NS_FOCUS_CONTENT: pluginEvent.event = WM_SETFOCUS; pluginEvent.wParam = 0; pluginEvent.lParam = 0; @@ -1990,17 +1992,19 @@ nsEventStatus nsPluginInstanceOwner::Pro if (pPluginEvent && !pPluginEvent->event) { // Don't send null events to plugins. NS_WARNING("nsObjectFrame ProcessEvent: trying to send null event to plugin."); return rv; } if (pPluginEvent) { int16_t response = kNPEventNotHandled; - mInstance->HandleEvent(pPluginEvent, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + mInstance->HandleEvent(const_cast<NPEvent*>(pPluginEvent), + &response, + NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); if (response == kNPEventHandled) rv = nsEventStatus_eConsumeNoDefault; } #endif #ifdef MOZ_X11 // this code supports windowless plugins nsIWidget* widget = anEvent.widget; @@ -2112,24 +2116,24 @@ nsEventStatus nsPluginInstanceOwner::Pro break; } } break; //XXX case NS_MOUSE_SCROLL_EVENT: not received. case NS_KEY_EVENT: - if (anEvent.pluginEvent) + if (anEvent.mPluginEvent) { XKeyEvent &event = pluginEvent.xkey; #ifdef MOZ_WIDGET_GTK event.root = GDK_ROOT_WINDOW(); event.time = anEvent.time; const GdkEventKey* gdkEvent = - static_cast<const GdkEventKey*>(anEvent.pluginEvent); + static_cast<const GdkEventKey*>(anEvent.mPluginEvent); event.keycode = gdkEvent->hardware_keycode; event.state = gdkEvent->state; switch (anEvent.message) { case NS_KEY_DOWN: // Handle NS_KEY_DOWN for modifier key presses // For non-modifiers we get NS_KEY_PRESS if (gdkEvent->is_modifier) @@ -2264,21 +2268,23 @@ nsEventStatus nsPluginInstanceOwner::Pro } break; case NS_KEY_EVENT: { const WidgetKeyboardEvent& keyEvent = *anEvent.AsKeyboardEvent(); LOG("Firing NS_KEY_EVENT %d %d\n", keyEvent.keyCode, keyEvent.charCode); // pluginEvent is initialized by nsWindow::InitKeyEvent(). - ANPEvent* pluginEvent = reinterpret_cast<ANPEvent*>(keyEvent.pluginEvent); + const ANPEvent* pluginEvent = static_cast<const ANPEvent*>(keyEvent.mPluginEvent); if (pluginEvent) { MOZ_ASSERT(pluginEvent->inSize == sizeof(ANPEvent)); MOZ_ASSERT(pluginEvent->eventType == kKey_ANPEventType); - mInstance->HandleEvent(pluginEvent, nullptr, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + mInstance->HandleEvent(const_cast<ANPEvent*>(pluginEvent), + nullptr, + NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); } } break; default: break; } rv = nsEventStatus_eConsumeNoDefault; @@ -2982,17 +2988,17 @@ void* nsPluginInstanceOwner::FixUpPlugin // Set this before calling ProcessEvent to avoid endless recursion. mSentInitialTopLevelWindowEvent = true; WidgetPluginEvent pluginEvent(true, NS_PLUGIN_FOCUS_EVENT, nullptr); NPCocoaEvent cocoaEvent; InitializeNPCocoaEvent(&cocoaEvent); cocoaEvent.type = NPCocoaEventWindowFocusChanged; cocoaEvent.data.focus.hasFocus = cocoaTopLevelWindow ? NS_NPAPI_CocoaWindowIsMain(cocoaTopLevelWindow) : true; - pluginEvent.pluginEvent = &cocoaEvent; + pluginEvent.mPluginEvent.Copy(cocoaEvent); ProcessEvent(pluginEvent); } return nullptr; } void nsPluginInstanceOwner::HidePluginWindow()
--- a/dom/workers/ServiceWorkerContainer.cpp +++ b/dom/workers/ServiceWorkerContainer.cpp @@ -12,27 +12,45 @@ #include "nsCycleCollectionParticipant.h" #include "nsServiceManagerUtils.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ServiceWorkerContainerBinding.h" #include "mozilla/dom/workers/bindings/ServiceWorker.h" +#include "ServiceWorker.h" + namespace mozilla { namespace dom { namespace workers { NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_IMPL_ADDREF_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper) -NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper, mWindow) +NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper, + mInstallingWorker, + mWaitingWorker, + mActiveWorker, + mControllerWorker) + +ServiceWorkerContainer::ServiceWorkerContainer(nsPIDOMWindow* aWindow) + : mWindow(aWindow) +{ + SetIsDOMBinding(); + StartListeningForEvents(); +} + +ServiceWorkerContainer::~ServiceWorkerContainer() +{ + StopListeningForEvents(); +} JSObject* ServiceWorkerContainer::WrapObject(JSContext* aCx) { return ServiceWorkerContainerBinding::Wrap(aCx, this); } already_AddRefed<Promise> @@ -80,39 +98,67 @@ ServiceWorkerContainer::Unregister(const nsRefPtr<Promise> ret = static_cast<Promise*>(promise.get()); MOZ_ASSERT(ret); return ret.forget(); } already_AddRefed<workers::ServiceWorker> ServiceWorkerContainer::GetInstalling() { - // FIXME(nsm): Bug 1002570 - return nullptr; + if (!mInstallingWorker) { + mInstallingWorker = GetWorkerReference(WhichServiceWorker::INSTALLING_WORKER); + } + + nsRefPtr<ServiceWorker> ret = mInstallingWorker; + return ret.forget(); } already_AddRefed<workers::ServiceWorker> ServiceWorkerContainer::GetWaiting() { - // FIXME(nsm): Bug 1002570 - return nullptr; + if (!mWaitingWorker) { + mWaitingWorker = GetWorkerReference(WhichServiceWorker::WAITING_WORKER); + } + + nsRefPtr<ServiceWorker> ret = mWaitingWorker; + return ret.forget(); } already_AddRefed<workers::ServiceWorker> ServiceWorkerContainer::GetActive() { - // FIXME(nsm): Bug 1002570 - return nullptr; + if (!mActiveWorker) { + mActiveWorker = GetWorkerReference(WhichServiceWorker::ACTIVE_WORKER); + } + + nsRefPtr<ServiceWorker> ret = mActiveWorker; + return ret.forget(); } already_AddRefed<workers::ServiceWorker> ServiceWorkerContainer::GetController() { - // FIXME(nsm): Bug 1002570 - return nullptr; + if (!mControllerWorker) { + nsresult rv; + nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + nsCOMPtr<nsISupports> serviceWorker; + rv = swm->GetDocumentController(mWindow, getter_AddRefs(serviceWorker)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + mControllerWorker = static_cast<ServiceWorker*>(serviceWorker.get()); + } + + nsRefPtr<ServiceWorker> ref = mControllerWorker; + return ref.forget(); } already_AddRefed<Promise> ServiceWorkerContainer::GetAll(ErrorResult& aRv) { // FIXME(nsm): Bug 1002571 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; @@ -141,16 +187,64 @@ void ServiceWorkerContainer::StopListeningForEvents() { nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID); if (swm) { swm->RemoveContainerEventListener(mWindow->GetDocumentURI(), this); } } +void +ServiceWorkerContainer::InvalidateWorkerReference(WhichServiceWorker aWhichOnes) +{ + if (aWhichOnes & WhichServiceWorker::INSTALLING_WORKER) { + mInstallingWorker = nullptr; + } + + if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) { + mWaitingWorker = nullptr; + } + + if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) { + mActiveWorker = nullptr; + } +} + +already_AddRefed<workers::ServiceWorker> +ServiceWorkerContainer::GetWorkerReference(WhichServiceWorker aWhichOne) +{ + nsresult rv; + nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + nsCOMPtr<nsISupports> serviceWorker; + switch(aWhichOne) { + case WhichServiceWorker::INSTALLING_WORKER: + rv = swm->GetInstalling(mWindow, getter_AddRefs(serviceWorker)); + break; + case WhichServiceWorker::WAITING_WORKER: + rv = swm->GetWaiting(mWindow, getter_AddRefs(serviceWorker)); + break; + case WhichServiceWorker::ACTIVE_WORKER: + rv = swm->GetActive(mWindow, getter_AddRefs(serviceWorker)); + break; + default: + MOZ_CRASH("Invalid enum value"); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + nsRefPtr<ServiceWorker> ref = static_cast<ServiceWorker*>(serviceWorker.get()); + return ref.forget(); +} + // Testing only. already_AddRefed<Promise> ServiceWorkerContainer::ClearAllServiceWorkerData(ErrorResult& aRv) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; }
--- a/dom/workers/ServiceWorkerContainer.h +++ b/dom/workers/ServiceWorkerContainer.h @@ -4,16 +4,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_workers_serviceworkercontainer_h__ #define mozilla_dom_workers_serviceworkercontainer_h__ #include "mozilla/DOMEventTargetHelper.h" +#include "ServiceWorkerManager.h" + class nsPIDOMWindow; namespace mozilla { namespace dom { class Promise; struct RegistrationOptionList; @@ -28,22 +30,17 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper) IMPL_EVENT_HANDLER(updatefound) IMPL_EVENT_HANDLER(controllerchange) IMPL_EVENT_HANDLER(reloadpage) IMPL_EVENT_HANDLER(error) - explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow) - : mWindow(aWindow) - { - SetIsDOMBinding(); - StartListeningForEvents(); - } + explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow); nsPIDOMWindow* GetParentObject() const { return mWindow; } JSObject* @@ -76,41 +73,56 @@ public: GetReady(ErrorResult& aRv); nsIURI* GetDocumentURI() const { return mWindow->GetDocumentURI(); } + void + InvalidateWorkerReference(WhichServiceWorker aWhichOnes); + + already_AddRefed<workers::ServiceWorker> + GetWorkerReference(WhichServiceWorker aWhichOne); + // Testing only. already_AddRefed<Promise> ClearAllServiceWorkerData(ErrorResult& aRv); // Testing only. void GetScopeForUrl(const nsAString& aUrl, nsString& aScope, ErrorResult& aRv); // Testing only. void GetControllingWorkerScriptURLForPath(const nsAString& aPath, nsString& aScriptURL, ErrorResult& aRv); private: - ~ServiceWorkerContainer() - { - StopListeningForEvents(); - } + ~ServiceWorkerContainer(); void StartListeningForEvents(); void StopListeningForEvents(); nsCOMPtr<nsPIDOMWindow> mWindow; + + // The following properties are cached here to ensure JS equality is satisfied + // instead of acquiring a new worker instance from the ServiceWorkerManager + // for every access. A null value is considered a cache miss. + // These three may change to a new worker at any time. + nsRefPtr<ServiceWorker> mInstallingWorker; + nsRefPtr<ServiceWorker> mWaitingWorker; + nsRefPtr<ServiceWorker> mActiveWorker; + // This only changes when a worker hijacks everything in its scope by calling + // replace(). + // FIXME(nsm): Bug 982711. Provide API to let SWM invalidate this. + nsRefPtr<ServiceWorker> mControllerWorker; }; } // namespace workers } // namespace dom } // namespace mozilla #endif /* mozilla_dom_workers_serviceworkercontainer_h__ */
--- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -20,16 +20,17 @@ #include "nsContentUtils.h" #include "nsCxPusher.h" #include "nsNetUtil.h" #include "nsProxyRelease.h" #include "nsTArray.h" #include "RuntimeService.h" #include "ServiceWorker.h" +#include "ServiceWorkerContainer.h" #include "ServiceWorkerEvents.h" #include "WorkerInlines.h" #include "WorkerPrivate.h" #include "WorkerRunnable.h" #include "WorkerScope.h" using namespace mozilla; using namespace mozilla::dom; @@ -566,16 +567,18 @@ ServiceWorkerManager::Update(ServiceWork if (aRegistration->mInstallingWorker) { // FIXME(nsm): Terminate the worker. We still haven't figured out worker // instance ownership when not associated with a window, so let's wait on // this. // FIXME(nsm): We should be setting the state on the actual worker // instance. // FIXME(nsm): Fire "statechange" on installing worker instance. aRegistration->mInstallingWorker = nullptr; + InvalidateServiceWorkerContainerWorker(aRegistration, + WhichServiceWorker::INSTALLING_WORKER); } aRegistration->mUpdatePromise = new UpdatePromise(); // FIXME(nsm): Bug 931249. If we don't need to fetch & install, resolve // promise and skip this. // FIXME(nsm): Bug 931249. Force cache update if > 1 day. aRegistration->mUpdateInstance = @@ -773,16 +776,19 @@ public: NS_IMETHOD Run() MOZ_OVERRIDE { AssertIsOnMainThread(); // FIXME(nsm): Change installing worker state to redundant. // FIXME(nsm): Fire statechange. mRegistration->mInstallingWorker = nullptr; + nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + swm->InvalidateServiceWorkerContainerWorker(mRegistration, + WhichServiceWorker::INSTALLING_WORKER); return NS_OK; } }; /* * Used to handle InstallEvent::waitUntil() and proceed with installation. */ class FinishInstallHandler MOZ_FINAL : public PromiseNativeHandler @@ -995,28 +1001,31 @@ private: void ServiceWorkerManager::Install(ServiceWorkerRegistration* aRegistration, ServiceWorkerInfo* aServiceWorkerInfo) { AssertIsOnMainThread(); aRegistration->mInstallingWorker = aServiceWorkerInfo; MOZ_ASSERT(aRegistration->mInstallingWorker); + InvalidateServiceWorkerContainerWorker(aRegistration, + WhichServiceWorker::INSTALLING_WORKER); nsMainThreadPtrHandle<ServiceWorkerRegistration> handle = new nsMainThreadPtrHolder<ServiceWorkerRegistration>(aRegistration); nsRefPtr<ServiceWorker> serviceWorker; nsresult rv = CreateServiceWorker(aServiceWorkerInfo->GetScriptSpec(), aRegistration->mScope, getter_AddRefs(serviceWorker)); if (NS_WARN_IF(NS_FAILED(rv))) { aRegistration->mInstallingWorker = nullptr; + // We don't need to invalidate here since the upper one will have done it. return; } nsRefPtr<InstallEventRunnable> r = new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle); AutoSafeJSContext cx; r->Dispatch(cx); @@ -1052,19 +1061,21 @@ public: Run() MOZ_OVERRIDE { if (mRegistration->mCurrentWorker) { // FIXME(nsm). Steps 3.1-3.4 of the algorithm. } mRegistration->mCurrentWorker = mRegistration->mWaitingWorker.forget(); - // FIXME(nsm): Steps 7 of the algorithm. + nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + swm->InvalidateServiceWorkerContainerWorker(mRegistration, + WhichServiceWorker::ACTIVE_WORKER | WhichServiceWorker::WAITING_WORKER); - nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + // FIXME(nsm): Steps 7 of the algorithm. swm->FireEventOnServiceWorkerContainers(mRegistration, NS_LITERAL_STRING("controllerchange")); MOZ_ASSERT(mRegistration->mCurrentWorker); nsRefPtr<ServiceWorker> serviceWorker; nsresult rv = swm->CreateServiceWorker(mRegistration->mCurrentWorker->GetScriptSpec(), @@ -1104,16 +1115,18 @@ ServiceWorkerManager::FinishInstall(Serv // a different script leading to [[Update]] terminating the // installingWorker and setting it to null. The FinishInstallRunnable may // already have been dispatched, hence the check. return; } aRegistration->mWaitingWorker = aRegistration->mInstallingWorker.forget(); MOZ_ASSERT(aRegistration->mWaitingWorker); + InvalidateServiceWorkerContainerWorker(aRegistration, + WhichServiceWorker::WAITING_WORKER | WhichServiceWorker::INSTALLING_WORKER); // FIXME(nsm): Actually update state of active ServiceWorker instances to // installed. // FIXME(nsm): Fire statechange on the instances. // FIXME(nsm): Handle replace(). if (!aRegistration->IsControllingDocuments()) { @@ -1343,17 +1356,17 @@ ServiceWorkerManager::MaybeStartControll nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aDoc); if (!domainInfo) { return; } nsRefPtr<ServiceWorkerRegistration> registration = GetServiceWorkerRegistration(aDoc); - if (registration) { + if (registration && registration->mCurrentWorker) { MOZ_ASSERT(!domainInfo->mControlledDocuments.Contains(aDoc)); registration->StartControllingADocument(); // Use the already_AddRefed<> form of Put to avoid the addref-deref since // we don't need the registration pointer in this function anymore. domainInfo->mControlledDocuments.Put(aDoc, registration.forget()); } } @@ -1466,16 +1479,127 @@ ServiceWorkerManager::FireEventOnService continue; } target->DispatchTrustedEvent(aName); } } } +/* + * This is used for installing, waiting and active, and uses the registration + * most specifically matching the current scope. + */ +NS_IMETHODIMP +ServiceWorkerManager::GetServiceWorkerForWindow(nsIDOMWindow* aWindow, + WhichServiceWorker aWhichWorker, + nsISupports** aServiceWorker) +{ + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow); + MOZ_ASSERT(window); + + nsRefPtr<ServiceWorkerRegistration> registration = + GetServiceWorkerRegistration(window); + + if (!registration) { + return NS_ERROR_FAILURE; + } + + nsRefPtr<ServiceWorkerInfo> info; + if (aWhichWorker == WhichServiceWorker::INSTALLING_WORKER) { + info = registration->mInstallingWorker; + } else if (aWhichWorker == WhichServiceWorker::WAITING_WORKER) { + info = registration->mWaitingWorker; + } else if (aWhichWorker == WhichServiceWorker::ACTIVE_WORKER) { + info = registration->mCurrentWorker; + } else { + MOZ_CRASH("Invalid worker type"); + } + + if (!info) { + return NS_ERROR_DOM_NOT_FOUND_ERR; + } + + nsRefPtr<ServiceWorker> serviceWorker; + nsresult rv = CreateServiceWorkerForWindow(window, + info->GetScriptSpec(), + registration->mScope, + getter_AddRefs(serviceWorker)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + serviceWorker.forget(aServiceWorker); + return NS_OK; +} + +/* + * The .controller is for the registration associated with the document when + * the document was loaded. + */ +NS_IMETHODIMP +ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, nsISupports** aServiceWorker) +{ + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow); + MOZ_ASSERT(window); + if (!window || !window->GetExtantDoc()) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); + + nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(doc); + if (!domainInfo) { + return NS_ERROR_FAILURE; + } + + nsRefPtr<ServiceWorkerRegistration> registration; + if (!domainInfo->mControlledDocuments.Get(doc, getter_AddRefs(registration))) { + return NS_ERROR_FAILURE; + } + + // If the document is controlled, the current worker MUST be non-null. + MOZ_ASSERT(registration->mCurrentWorker); + + nsRefPtr<ServiceWorker> serviceWorker; + nsresult rv = CreateServiceWorkerForWindow(window, + registration->mCurrentWorker->GetScriptSpec(), + registration->mScope, + getter_AddRefs(serviceWorker)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + serviceWorker.forget(aServiceWorker); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerManager::GetInstalling(nsIDOMWindow* aWindow, + nsISupports** aServiceWorker) +{ + return GetServiceWorkerForWindow(aWindow, WhichServiceWorker::INSTALLING_WORKER, + aServiceWorker); +} + +NS_IMETHODIMP +ServiceWorkerManager::GetWaiting(nsIDOMWindow* aWindow, + nsISupports** aServiceWorker) +{ + return GetServiceWorkerForWindow(aWindow, WhichServiceWorker::WAITING_WORKER, + aServiceWorker); +} + +NS_IMETHODIMP +ServiceWorkerManager::GetActive(nsIDOMWindow* aWindow, nsISupports** aServiceWorker) +{ + return GetServiceWorkerForWindow(aWindow, WhichServiceWorker::ACTIVE_WORKER, + aServiceWorker); +} + NS_IMETHODIMP ServiceWorkerManager::CreateServiceWorker(const nsACString& aScriptSpec, const nsACString& aScope, ServiceWorker** aServiceWorker) { AssertIsOnMainThread(); WorkerPrivate::LoadInfo info; @@ -1514,9 +1638,40 @@ ServiceWorkerManager::CreateServiceWorke if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } serviceWorker.forget(aServiceWorker); return NS_OK; } +void +ServiceWorkerManager::InvalidateServiceWorkerContainerWorker(ServiceWorkerRegistration* aRegistration, + WhichServiceWorker aWhichOnes) +{ + AssertIsOnMainThread(); + nsRefPtr<ServiceWorkerDomainInfo> domainInfo = + GetDomainInfo(aRegistration->mScriptSpec); + + if (domainInfo) { + nsTObserverArray<ServiceWorkerContainer*>::ForwardIterator it(domainInfo->mServiceWorkerContainers); + while (it.HasMore()) { + nsRefPtr<ServiceWorkerContainer> target = it.GetNext(); + + nsIURI* targetURI = target->GetDocumentURI(); + nsCString path; + nsresult rv = targetURI->GetSpec(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + nsCString scope = FindScopeForPath(domainInfo->mOrderedScopes, path); + if (scope.IsEmpty() || + !scope.Equals(aRegistration->mScope)) { + continue; + } + + target->InvalidateWorkerReference(aWhichOnes); + } + } +} + END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -6,19 +6,20 @@ #define mozilla_dom_workers_serviceworkermanager_h #include "nsIServiceWorkerManager.h" #include "nsCOMPtr.h" #include "mozilla/Attributes.h" #include "mozilla/LinkedList.h" #include "mozilla/Preferences.h" +#include "mozilla/TypedEnum.h" +#include "mozilla/TypedEnumBits.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/ServiceWorkerContainer.h" #include "nsRefPtrHashtable.h" #include "nsTArrayForwardDeclare.h" #include "nsTObserverArray.h" #include "nsTWeakRef.h" class nsIScriptError; namespace mozilla { @@ -89,16 +90,25 @@ public: return mScriptSpec; } explicit ServiceWorkerInfo(const nsACString& aScriptSpec) : mScriptSpec(aScriptSpec) { } }; +// Use multiples of 2 since they can be bitwise ORed when calling +// InvalidateServiceWorkerContainerWorker. +MOZ_BEGIN_ENUM_CLASS(WhichServiceWorker) + INSTALLING_WORKER = 1, + WAITING_WORKER = 2, + ACTIVE_WORKER = 4, +MOZ_END_ENUM_CLASS(WhichServiceWorker) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(WhichServiceWorker) + // Needs to inherit from nsISupports because NS_ProxyRelease() does not support // non-ISupports classes. class ServiceWorkerRegistration MOZ_FINAL : public nsISupports { uint32_t mControlledDocumentsCounter; virtual ~ServiceWorkerRegistration(); @@ -184,16 +194,17 @@ public: * installation, querying and event dispatch of ServiceWorkers for all the * origins in the process. */ class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager { friend class ActivationRunnable; friend class RegisterRunnable; friend class CallInstallRunnable; + friend class CancelServiceWorkerInstallationRunnable; friend class ServiceWorkerUpdateInstance; public: NS_DECL_ISUPPORTS NS_DECL_NSISERVICEWORKERMANAGER NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID) static ServiceWorkerManager* FactoryCreate() @@ -334,16 +345,25 @@ private: GetDomainInfo(nsIDocument* aDoc); already_AddRefed<ServiceWorkerDomainInfo> GetDomainInfo(nsIURI* aURI); already_AddRefed<ServiceWorkerDomainInfo> GetDomainInfo(const nsCString& aURL); + NS_IMETHODIMP + GetServiceWorkerForWindow(nsIDOMWindow* aWindow, + WhichServiceWorker aWhichWorker, + nsISupports** aServiceWorker); + + void + InvalidateServiceWorkerContainerWorker(ServiceWorkerRegistration* aRegistration, + WhichServiceWorker aWhichOnes); + already_AddRefed<ServiceWorkerRegistration> GetServiceWorkerRegistration(nsPIDOMWindow* aWindow); already_AddRefed<ServiceWorkerRegistration> GetServiceWorkerRegistration(nsIDocument* aDoc); already_AddRefed<ServiceWorkerRegistration> GetServiceWorkerRegistration(nsIURI* aURI);
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/controller/index.html @@ -0,0 +1,51 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 94048 - test install event.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + // Make sure to use good, unique messages, since the actual expression will not show up in test results. + function my_ok(result, msg) { + window.opener.postMessage({status: "ok", result: result, message: msg}, "*"); + } + + function finish() { + window.opener.postMessage({status: "done"}, "*"); + } + + if (navigator.serviceWorker.controller) { + my_ok(navigator.serviceWorker.controller.scope.match(/serviceworkers\/control\*$/), + "This page should be controlled by upper level registration"); + my_ok(navigator.serviceWorker.installing == undefined, + "Upper level registration should not have a installing worker."); + // We are controlled. + // Register a new worker for this sub-scope. After that, controller should still be for upper level, but active should change to be this scope's. + navigator.serviceWorker.register("../worker2.js", { scope: "./*" }).then(function(e) { + my_ok(navigator.serviceWorker.installing && + navigator.serviceWorker.installing.scope.match(/controller\/\*$/), + "Installing is serviceworker/controller/*"); + my_ok(navigator.serviceWorker.controller.scope.match(/serviceworkers\/control\*$/), + "Controller is still serviceworker/*"); + finish(); + }); + } else { + my_ok(false, "Should've been controlled!"); + finish(); + } +</script> +</pre> +</body> +</html> + +
--- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -1,13 +1,16 @@ [DEFAULT] run-if = os == "win" # Bug 1040924 - Failure prone on !Windows support-files = worker.js worker2.js worker3.js parse_error_worker.js install_event_worker.js + simpleregister/index.html + controller/index.html [test_installation_simple.html] [test_install_event.html] [test_navigator.html] [test_scopes.html] +[test_controller.html]
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/simpleregister/index.html @@ -0,0 +1,11 @@ +<html> + <head></head> + <body> + <script type="text/javascript"> + navigator.serviceWorker.onupdatefound = function(e) { + window.parent.postMessage("updatefound", "*"); + } + window.parent.postMessage("ready", "*"); + </script> + </body> +</html>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_controller.html @@ -0,0 +1,61 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1002570 - test controller instance.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function simpleRegister() { + // We use the control* scope for the less specific registration. The window will register a worker on controller/* + return navigator.serviceWorker.register("worker.js", { scope: "./control*" }); + } + + function testController() { + var p = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "done") { + window.onmessage = null; + w.close(); + resolve(); + } + } + }); + + var w = window.open("controller/index.html"); + return p; + } + + // This document just flips the prefs and opens the window for the actual test. + function runTest() { + simpleRegister() + .then(testController) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> +
--- a/dom/workers/test/serviceworkers/test_installation_simple.html +++ b/dom/workers/test/serviceworkers/test_installation_simple.html @@ -11,17 +11,17 @@ </head> <body> <p id="display"></p> <div id="content" style="display: none"></div> <pre id="test"></pre> <script class="testbody" type="text/javascript"> function simpleRegister() { - var p = navigator.serviceWorker.register("worker.js"); + var p = navigator.serviceWorker.register("worker.js", { scope: "simpleregister/*" }); ok(p instanceof Promise, "register() should return a Promise"); return Promise.resolve(); } function sameOriginWorker() { p = navigator.serviceWorker.register("http://some-other-origin/worker.js"); return p.then(function(w) { ok(false, "Worker from different origin should fail"); @@ -103,37 +103,56 @@ info("NSM " + e.name); ok(e instanceof Error, "Registration should fail with parse error"); }); } // FIXME(nsm): test for parse error when Update step doesn't happen (directly from register). function updatefound() { - var p = navigator.serviceWorker.register("worker.js"); + var frame = document.createElement("iframe"); + frame.setAttribute("id", "simpleregister-frame"); + frame.setAttribute("src", new URL("simpleregister/index.html", document.baseURI).href); + document.body.appendChild(frame); + var resolve, reject; + var p = new Promise(function(res, rej) { + resolve = res; + reject = rej; + }); - return new Promise(function(resolve, reject) { - navigator.serviceWorker.onupdatefound = function(e) { - ok(true, "Got updatefound event"); - navigator.serviceWorker.onupdatefound = null; + function continueTest() { + navigator.serviceWorker.register("worker2.js", { scope: "simpleregister/*" }); + } + + window.onmessage = function(e) { + if (e.data == "ready") { + continueTest(); + } else if (e.data == "updatefound") { + window.onmessage = null; + // We have to make frame navigate away, otherwise it will call + // MaybeStopControlling() when this document is unloaded. At that point + // the pref has been disabled, and so MaybeStopControlling() will just + // return since it is currently gated. + frame.setAttribute("src", new URL("about:blank").href); resolve(); } - }); + } + return p; } function runTest() { simpleRegister() .then(sameOriginWorker) .then(sameOriginScope) .then(httpsOnly) .then(realWorker) .then(abortPrevious) .then(networkError404) .then(parseError) - //.then(updatefound) + .then(updatefound) // put more tests here. .then(function() { SimpleTest.finish(); }).catch(function(e) { ok(false, "Some test failed with error " + e); SimpleTest.finish(); }); }
--- a/dom/workers/test/serviceworkers/test_navigator.html +++ b/dom/workers/test/serviceworkers/test_navigator.html @@ -18,18 +18,18 @@ function checkEnabled() { ok(navigator.serviceWorker, "navigator.serviceWorker should exist when ServiceWorkers are enabled."); ok(typeof navigator.serviceWorker.register === "function", "navigator.serviceWorker.register() should be a function."); ok(typeof navigator.serviceWorker.unregister === "function", "navigator.serviceWorker.unregister() should be a function."); ok(typeof navigator.serviceWorker.getAll === "function", "navigator.serviceWorker.getAll() should be a function."); ok(navigator.serviceWorker.ready instanceof Promise, "navigator.serviceWorker.ready should be a Promise."); ok(navigator.serviceWorker.installing === null, "There should be no installing worker for an uncontrolled scope."); ok(navigator.serviceWorker.waiting === null, "There should be no waiting worker for an uncontrolled scope."); - ok(navigator.serviceWorker.active === null, "There should be no active worker for an uncontrolled scope."); - ok(navigator.serviceWorker.controller === null, "There should be no active worker for an uncontrolled document."); + // ok(navigator.serviceWorker.active === null, "There should be no active worker for an uncontrolled scope."); + ok(navigator.serviceWorker.controller === null, "There should be no controller worker for an uncontrolled document."); } SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.enabled", true] ]}, function() { checkEnabled();
--- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -135,29 +135,30 @@ InterpolationQualityFromFilter(Filter aF case Filter::POINT: return kCGInterpolationNone; case Filter::GOOD: return kCGInterpolationDefault; } } -DrawTargetCG::DrawTargetCG() : mCg(nullptr), mSnapshot(nullptr) +DrawTargetCG::DrawTargetCG() + : mColorSpace(nullptr) + , mCg(nullptr) { } DrawTargetCG::~DrawTargetCG() { MarkChanged(); - // We need to conditionally release these because Init can fail without initializing these. - if (mColorSpace) - CGColorSpaceRelease(mColorSpace); - if (mCg) - CGContextRelease(mCg); + // Both of these are OK with nullptr arguments, so we do not + // need to check (these could be nullptr if Init fails) + CGColorSpaceRelease(mColorSpace); + CGContextRelease(mCg); } DrawTargetType DrawTargetCG::GetType() const { return GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED ? DrawTargetType::HARDWARE_RASTER : DrawTargetType::SOFTWARE_RASTER; } @@ -377,71 +378,72 @@ static CGColorRef ColorToCGColor(CGColor CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a}; return CGColorCreate(aColorSpace, components); } class GradientStopsCG : public GradientStops { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCG) - //XXX: The skia backend uses a vector and passes in aNumStops. It should do better - GradientStopsCG(GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) + + GradientStopsCG(CGColorSpaceRef aColorSpace, + const std::vector<GradientStop>& aStops, + ExtendMode aExtendMode) + : mGradient(nullptr) { + // This all works fine with empty aStops vector + mExtend = aExtendMode; if (aExtendMode == ExtendMode::CLAMP) { - //XXX: do the stops need to be in any particular order? - // what should we do about the color space here? we certainly shouldn't be - // recreating it all the time + size_t numStops = aStops.size(); + std::vector<CGFloat> colors; std::vector<CGFloat> offsets; - colors.reserve(aNumStops*4); - offsets.reserve(aNumStops); + colors.reserve(numStops*4); + offsets.reserve(numStops); - for (uint32_t i = 0; i < aNumStops; i++) { + for (size_t i = 0; i < numStops; i++) { colors.push_back(aStops[i].color.r); colors.push_back(aStops[i].color.g); colors.push_back(aStops[i].color.b); colors.push_back(aStops[i].color.a); offsets.push_back(aStops[i].offset); } - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - mGradient = CGGradientCreateWithColorComponents(colorSpace, + mGradient = CGGradientCreateWithColorComponents(aColorSpace, &colors.front(), &offsets.front(), - aNumStops); - CGColorSpaceRelease(colorSpace); + offsets.size()); } else { - mGradient = nullptr; - mStops.reserve(aNumStops); - for (uint32_t i = 0; i < aNumStops; i++) { - mStops.push_back(aStops[i]); - } + mStops = aStops; } } + virtual ~GradientStopsCG() { - if (mGradient) - CGGradientRelease(mGradient); + // CGGradientRelease is OK with nullptr argument + CGGradientRelease(mGradient); } + // Will always report BackendType::COREGRAPHICS, but it is compatible // with BackendType::COREGRAPHICS_ACCELERATED BackendType GetBackendType() const { return BackendType::COREGRAPHICS; } // XXX this should be a union CGGradientRef mGradient; std::vector<GradientStop> mStops; ExtendMode mExtend; }; TemporaryRef<GradientStops> DrawTargetCG::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { - return new GradientStopsCG(aStops, aNumStops, aExtendMode); + std::vector<GradientStop> stops(aStops, aStops+aNumStops); + return new GradientStopsCG(mColorSpace, stops, aExtendMode); } static void UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start, double dx, double dy, double x, double y) { @@ -520,17 +522,18 @@ CalculateRepeatingGradientParams(CGPoint aStart->x = aStart->x + dx * t_min; aStart->y = aStart->y + dy * t_min; *aRepeatStartFactor = t_min; *aRepeatEndFactor = t_max; } static CGGradientRef -CreateRepeatingGradient(CGContextRef cg, GradientStopsCG* aStops, +CreateRepeatingGradient(CGColorSpaceRef aColorSpace, + CGContextRef cg, GradientStopsCG* aStops, int aRepeatStartFactor, int aRepeatEndFactor, bool aReflect) { int repeatCount = aRepeatEndFactor - aRepeatStartFactor; uint32_t stopCount = aStops->mStops.size(); double scale = 1./repeatCount; std::vector<CGFloat> colors; @@ -550,42 +553,41 @@ CreateRepeatingGradient(CGContextRef cg, CGFloat offset = aStops->mStops[stopIndex].offset; if (isReflected) { offset = 1 - offset; } offsets.push_back((offset + (j - aRepeatStartFactor)) * scale); } } - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, + CGGradientRef gradient = CGGradientCreateWithColorComponents(aColorSpace, &colors.front(), &offsets.front(), repeatCount*stopCount); - CGColorSpaceRelease(colorSpace); return gradient; } static void -DrawLinearRepeatingGradient(CGContextRef cg, const LinearGradientPattern &aPattern, +DrawLinearRepeatingGradient(CGColorSpaceRef aColorSpace, CGContextRef cg, + const LinearGradientPattern &aPattern, const CGRect &aExtents, bool aReflect) { GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get()); CGPoint startPoint = { aPattern.mBegin.x, aPattern.mBegin.y }; CGPoint endPoint = { aPattern.mEnd.x, aPattern.mEnd.y }; int repeatStartFactor = 0, repeatEndFactor = 1; // if we don't have a line then we can't extend it if (aPattern.mEnd.x != aPattern.mBegin.x || aPattern.mEnd.y != aPattern.mBegin.y) { CalculateRepeatingGradientParams(&startPoint, &endPoint, aExtents, &repeatStartFactor, &repeatEndFactor); } - CGGradientRef gradient = CreateRepeatingGradient(cg, stops, repeatStartFactor, repeatEndFactor, aReflect); + CGGradientRef gradient = CreateRepeatingGradient(aColorSpace, cg, stops, repeatStartFactor, repeatEndFactor, aReflect); CGContextDrawLinearGradient(cg, gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGGradientRelease(gradient); } static CGPoint CGRectTopLeft(CGRect a) { return a.origin; } @@ -598,17 +600,18 @@ static CGPoint CGRectBottomRight(CGRect static CGFloat CGPointDistance(CGPoint a, CGPoint b) { return hypot(a.x-b.x, a.y-b.y); } static void -DrawRadialRepeatingGradient(CGContextRef cg, const RadialGradientPattern &aPattern, +DrawRadialRepeatingGradient(CGColorSpaceRef aColorSpace, CGContextRef cg, + const RadialGradientPattern &aPattern, const CGRect &aExtents, bool aReflect) { GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get()); CGPoint startCenter = { aPattern.mCenter1.x, aPattern.mCenter1.y }; CGFloat startRadius = aPattern.mRadius1; CGPoint endCenter = { aPattern.mCenter2.x, aPattern.mCenter2.y }; CGFloat endRadius = aPattern.mRadius2; @@ -626,26 +629,27 @@ DrawRadialRepeatingGradient(CGContextRef repeatEndFactor++; } while (startRadius-length >= 0) { startRadius -= length; repeatStartFactor--; } - CGGradientRef gradient = CreateRepeatingGradient(cg, stops, repeatStartFactor, repeatEndFactor, aReflect); + CGGradientRef gradient = CreateRepeatingGradient(aColorSpace, cg, stops, repeatStartFactor, repeatEndFactor, aReflect); //XXX: are there degenerate radial gradients that we should avoid drawing? CGContextDrawRadialGradient(cg, gradient, startCenter, startRadius, endCenter, endRadius, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGGradientRelease(gradient); } static void -DrawGradient(CGContextRef cg, const Pattern &aPattern, const CGRect &aExtents) +DrawGradient(CGColorSpaceRef aColorSpace, + CGContextRef cg, const Pattern &aPattern, const CGRect &aExtents) { if (CGRectIsEmpty(aExtents)) { return; } if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern); GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get()); @@ -658,17 +662,17 @@ DrawGradient(CGContextRef cg, const Patt // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?) //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y) // return; CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) { - DrawLinearRepeatingGradient(cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT); + DrawLinearRepeatingGradient(aColorSpace, cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT); } } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern); CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix)); GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get()); if (stops->mExtend == ExtendMode::CLAMP) { // XXX: we should take the m out of the properties of RadialGradientPatterns @@ -676,17 +680,17 @@ DrawGradient(CGContextRef cg, const Patt CGFloat startRadius = pat.mRadius1; CGPoint endCenter = { pat.mCenter2.x, pat.mCenter2.y }; CGFloat endRadius = pat.mRadius2; //XXX: are there degenerate radial gradients that we should avoid drawing? CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) { - DrawRadialRepeatingGradient(cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT); + DrawRadialRepeatingGradient(aColorSpace, cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT); } } else { assert(0); } } static void @@ -713,16 +717,23 @@ CGPatternCallbacks patternCallbacks = { }; static bool isGradient(const Pattern &aPattern) { return aPattern.GetType() == PatternType::LINEAR_GRADIENT || aPattern.GetType() == PatternType::RADIAL_GRADIENT; } +static bool +isNonRepeatingSurface(const Pattern& aPattern) +{ + return aPattern.GetType() == PatternType::SURFACE && + static_cast<const SurfacePattern&>(aPattern).mExtendMode != ExtendMode::REPEAT; +} + /* CoreGraphics patterns ignore the userspace transform so * we need to multiply it in */ static CGPatternRef CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace) { const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); // XXX: is .get correct here? CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get()); @@ -844,74 +855,72 @@ DrawTargetCG::MaskSurface(const Pattern IntSize size = aMask->GetSize(); CGContextClipToMask(cg, CGRectMake(aOffset.x, -(aOffset.y + size.height), size.width, size.height), image); CGContextScaleCTM(cg, 1, -1); if (isGradient(aSource)) { // we shouldn't need to clip to an additional rectangle // as the cliping to the mask should be sufficient. - DrawGradient(cg, aSource, CGRectMake(aOffset.x, aOffset.y, size.width, size.height)); + DrawGradient(mColorSpace, cg, aSource, CGRectMake(aOffset.x, aOffset.y, size.width, size.height)); } else { SetFillFromPattern(cg, mColorSpace, aSource); CGContextFillRect(cg, CGRectMake(aOffset.x, aOffset.y, size.width, size.height)); } CGImageRelease(image); fixer.Fix(mCg); CGContextRestoreGState(mCg); } void DrawTargetCG::FillRect(const Rect &aRect, - const Pattern &aPattern, - const DrawOptions &aDrawOptions) + const Pattern &aPattern, + const DrawOptions &aDrawOptions) { MarkChanged(); CGContextSaveGState(mCg); UnboundnessFixer fixer; CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aRect); CGContextSetAlpha(mCg, aDrawOptions.mAlpha); CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE); CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); if (isGradient(aPattern)) { CGContextClipToRect(cg, RectToCGRect(aRect)); CGRect clipBounds = CGContextGetClipBoundingBox(cg); - DrawGradient(cg, aPattern, clipBounds); + DrawGradient(mColorSpace, cg, aPattern, clipBounds); + } else if (isNonRepeatingSurface(aPattern)) { + // SetFillFromPattern can handle this case but using CGContextDrawImage + // should give us better performance, better output, smaller PDF and + // matches what cairo does. + const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); + CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get()); + CGContextClipToRect(cg, RectToCGRect(aRect)); + CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix)); + CGContextTranslateCTM(cg, 0, CGImageGetHeight(image)); + CGContextScaleCTM(cg, 1, -1); + + CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)); + + CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter)); + + CGContextDrawImage(cg, imageRect, image); + CGImageRelease(image); } else { - if (aPattern.GetType() == PatternType::SURFACE && static_cast<const SurfacePattern&>(aPattern).mExtendMode != ExtendMode::REPEAT) { - // SetFillFromPattern can handle this case but using CGContextDrawImage - // should give us better performance, better output, smaller PDF and - // matches what cairo does. - const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); - CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get()); - CGContextClipToRect(cg, RectToCGRect(aRect)); - CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix)); - CGContextTranslateCTM(cg, 0, CGImageGetHeight(image)); - CGContextScaleCTM(cg, 1, -1); - - CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)); - - CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter)); - - CGContextDrawImage(cg, imageRect, image); - CGImageRelease(image); - } else { - SetFillFromPattern(cg, mColorSpace, aPattern); - CGContextFillRect(cg, RectToCGRect(aRect)); - } + SetFillFromPattern(cg, mColorSpace, aPattern); + CGContextFillRect(cg, RectToCGRect(aRect)); } fixer.Fix(mCg); CGContextRestoreGState(mCg); } void DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions) @@ -941,17 +950,17 @@ DrawTargetCG::StrokeLine(const Point &p1 SetStrokeOptions(cg, aStrokeOptions); if (isGradient(aPattern)) { CGContextReplacePathWithStrokedPath(cg); CGRect extents = CGContextGetPathBoundingBox(cg); //XXX: should we use EO clip here? CGContextClip(cg); - DrawGradient(cg, aPattern, extents); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { SetStrokeFromPattern(cg, mColorSpace, aPattern); CGContextStrokePath(cg); } fixer.Fix(mCg); CGContextRestoreGState(mCg); } @@ -983,17 +992,17 @@ DrawTargetCG::StrokeRect(const Rect &aRe if (isGradient(aPattern)) { // There's no CGContextClipStrokeRect so we do it by hand CGContextBeginPath(cg); CGContextAddRect(cg, RectToCGRect(aRect)); CGContextReplacePathWithStrokedPath(cg); CGRect extents = CGContextGetPathBoundingBox(cg); //XXX: should we use EO clip here? CGContextClip(cg); - DrawGradient(cg, aPattern, extents); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { SetStrokeFromPattern(cg, mColorSpace, aPattern); CGContextStrokeRect(cg, RectToCGRect(aRect)); } fixer.Fix(mCg); CGContextRestoreGState(mCg); } @@ -1040,17 +1049,17 @@ DrawTargetCG::Stroke(const Path *aPath, SetStrokeOptions(cg, aStrokeOptions); if (isGradient(aPattern)) { CGContextReplacePathWithStrokedPath(cg); CGRect extents = CGContextGetPathBoundingBox(cg); //XXX: should we use EO clip here? CGContextClip(cg); - DrawGradient(cg, aPattern, extents); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { // XXX: we could put fill mode into the path fill rule if we wanted SetStrokeFromPattern(cg, mColorSpace, aPattern); CGContextStrokePath(cg); } fixer.Fix(mCg); @@ -1090,17 +1099,17 @@ DrawTargetCG::Fill(const Path *aPath, co CGContextAddPath(cg, cgPath->GetPath()); extents = CGContextGetPathBoundingBox(cg); if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) CGContextEOClip(mCg); else CGContextClip(mCg); } - DrawGradient(cg, aPattern, extents); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { CGContextAddPath(cg, cgPath->GetPath()); SetFillFromPattern(cg, mColorSpace, aPattern); if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) CGContextEOFillPath(cg); else @@ -1196,17 +1205,17 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFo CGContextSetFont(cg, macFont->mFont); CGContextSetFontSize(cg, macFont->mSize); CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(), aBuffer.mNumGlyphs); delete bboxes; } CGContextScaleCTM(cg, 1, -1); - DrawGradient(cg, aPattern, extents); + DrawGradient(mColorSpace, cg, aPattern, extents); } else { //XXX: with CoreGraphics we can stroke text directly instead of going // through GetPath. It would be nice to add support for using that CGContextSetTextDrawingMode(cg, kCGTextFill); SetFillFromPattern(cg, mColorSpace, aPattern); if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) { ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(), &positions.front(),
--- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -594,16 +594,17 @@ GLContext::InitWithPrefix(const char *pr "Adreno (TM) 205", "Adreno (TM) 320", "PowerVR SGX 530", "PowerVR SGX 540", "NVIDIA Tegra", "Android Emulator", "Gallium 0.4 on llvmpipe", "Intel HD Graphics 3000 OpenGL Engine", + "Microsoft Basic Render Driver" }; mRenderer = GLRenderer::Other; for (size_t i = 0; i < size_t(GLRenderer::Other); ++i) { if (DoesStringMatch(glRendererString, rendererMatchStrings[i])) { mRenderer = GLRenderer(i); break; } @@ -662,16 +663,22 @@ GLContext::InitWithPrefix(const char *pr } if (Vendor() == GLVendor::Imagination && Renderer() == GLRenderer::SGX540) { // Bug 980048 MarkExtensionUnsupported(OES_EGL_sync); } + if (Renderer() == GLRenderer::MicrosoftBasicRenderDriver) { + // Bug 978966: on Microsoft's "Basic Render Driver" (software renderer) + // multisampling hardcodes blending with the default blendfunc, which breaks WebGL. + MarkUnsupported(GLFeature::framebuffer_multisample); + } + #ifdef XP_MACOSX // The Mac Nvidia driver, for versions up to and including 10.8, don't seem // to properly support this. See 814839 // this has been fixed in Mac OS X 10.9. See 907946 if (Vendor() == gl::GLVendor::NVIDIA && !nsCocoaFeatures::OnMavericksOrLater()) { MarkUnsupported(GLFeature::depth_texture);
--- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -142,16 +142,17 @@ MOZ_BEGIN_ENUM_CLASS(GLRenderer) AdrenoTM205, AdrenoTM320, SGX530, SGX540, Tegra, AndroidEmulator, GalliumLlvmpipe, IntelHD3000, + MicrosoftBasicRenderDriver, Other MOZ_END_ENUM_CLASS(GLRenderer) class GLContext : public GLLibraryLoader , public GenericAtomicRefCounted { // -----------------------------------------------------------------------------
--- a/gfx/gl/GLTextureImage.cpp +++ b/gfx/gl/GLTextureImage.cpp @@ -60,27 +60,16 @@ TileGenFunc(GLContext* gl, return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat); default: return nullptr; } } already_AddRefed<TextureImage> TextureImage::Create(GLContext* gl, - const nsIntSize& size, - TextureImage::ContentType contentType, - GLenum wrapMode, - TextureImage::Flags flags) -{ - return Create(gl, size.ToIntSize(), contentType, wrapMode, flags); -} - -// Moz2D equivalent... -already_AddRefed<TextureImage> -TextureImage::Create(GLContext* gl, const gfx::IntSize& size, TextureImage::ContentType contentType, GLenum wrapMode, TextureImage::Flags flags) { return CreateTextureImage(gl, size, contentType, wrapMode, flags); }
--- a/gfx/gl/GLTextureImage.h +++ b/gfx/gl/GLTextureImage.h @@ -62,23 +62,16 @@ public: DisallowBigImage = 0x4 }; typedef gfxContentType ContentType; typedef gfxImageFormat ImageFormat; static already_AddRefed<TextureImage> Create( GLContext* gl, - const nsIntSize& aSize, - TextureImage::ContentType aContentType, - GLenum aWrapMode, - TextureImage::Flags aFlags = TextureImage::NoFlags); - // Moz2D equivalent... - static already_AddRefed<TextureImage> Create( - GLContext* gl, const gfx::IntSize& aSize, TextureImage::ContentType aContentType, GLenum aWrapMode, TextureImage::Flags aFlags = TextureImage::NoFlags); /** * Returns a gfxASurface for updating |aRegion| of the client's * image if successul, nullptr if not. |aRegion|'s bounds must fit
--- a/gfx/gl/GfxTexturesReporter.cpp +++ b/gfx/gl/GfxTexturesReporter.cpp @@ -7,17 +7,18 @@ #include "GfxTexturesReporter.h" #include "GLDefs.h" using namespace mozilla; using namespace mozilla::gl; NS_IMPL_ISUPPORTS(GfxTexturesReporter, nsIMemoryReporter) -int64_t GfxTexturesReporter::sAmount = 0; +Atomic<int32_t> GfxTexturesReporter::sAmount(0); +Atomic<int32_t> GfxTexturesReporter::sTileWasteAmount(0); static uint32_t GetBitsPerTexel(GLenum format, GLenum type) { // If there is no defined format or type, we're not taking up any memory if (!format || !type) { return 0; }
--- a/gfx/gl/GfxTexturesReporter.h +++ b/gfx/gl/GfxTexturesReporter.h @@ -2,16 +2,17 @@ /* vim: set ts=8 sts=4 et sw=4 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GFXTEXTURESREPORTER_H_ #define GFXTEXTURESREPORTER_H_ +#include "mozilla/Atomics.h" #include "nsIMemoryReporter.h" #include "GLTypes.h" namespace mozilla { namespace gl { class GfxTexturesReporter MOZ_FINAL : public nsIMemoryReporter { @@ -38,24 +39,57 @@ public: MemoryFreed }; // When memory is used/freed for tile textures, call this method to update // the value reported by this memory reporter. static void UpdateAmount(MemoryUse action, GLenum format, GLenum type, int32_t tileWidth, int32_t tileHeight); + static void UpdateWasteAmount(int32_t delta) { + sTileWasteAmount += delta; + } + NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) { + MOZ_COLLECT_REPORT("gfx-tiles-waste", KIND_OTHER, UNITS_BYTES, + sTileWasteAmount, + "Memory lost due to tiles extending past content boundaries"); return MOZ_COLLECT_REPORT( "gfx-textures", KIND_OTHER, UNITS_BYTES, sAmount, "Memory used for storing GL textures."); } private: - static int64_t sAmount; + static Atomic<int32_t> sAmount; + // Count the amount of memory lost to tile waste + static Atomic<int32_t> sTileWasteAmount; +}; + +class GfxTextureWasteTracker { +public: + GfxTextureWasteTracker() + : mBytes(0) + { + MOZ_COUNT_CTOR(GfxTextureWasteTracker); + } + + void Update(int32_t aPixelArea, int32_t aBytesPerPixel) { + GfxTexturesReporter::UpdateWasteAmount(-mBytes); + mBytes = aPixelArea * aBytesPerPixel; + GfxTexturesReporter::UpdateWasteAmount(mBytes); + } + + ~GfxTextureWasteTracker() { + GfxTexturesReporter::UpdateWasteAmount(-mBytes); + MOZ_COUNT_DTOR(GfxTextureWasteTracker); + } +private: + GfxTextureWasteTracker(const GfxTextureWasteTracker& aRef); + + int32_t mBytes; }; } } #endif // GFXTEXTURESREPORTER_H_
--- a/gfx/layers/TextureDIB.cpp +++ b/gfx/layers/TextureDIB.cpp @@ -20,16 +20,29 @@ DIBTextureClient::DIBTextureClient(gfx:: MOZ_COUNT_CTOR(DIBTextureClient); } DIBTextureClient::~DIBTextureClient() { MOZ_COUNT_DTOR(DIBTextureClient); } +TemporaryRef<TextureClient> +DIBTextureClient::CreateSimilar(TextureFlags aFlags, + TextureAllocationFlags aAllocFlags) const +{ + RefPtr<TextureClient> tex = new DIBTextureClient(mFormat, mFlags | aFlags); + + if (!tex->AllocateForSurface(mSize, ALLOC_DEFAULT)) { + return nullptr; + } + + return tex; +} + bool DIBTextureClient::Lock(OpenMode) { MOZ_ASSERT(!mIsLocked); if (!IsValid()) { return false; } mIsLocked = true;
--- a/gfx/layers/TextureDIB.h +++ b/gfx/layers/TextureDIB.h @@ -47,16 +47,20 @@ public: virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE; virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE; virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; } + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT, + TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE; + protected: nsRefPtr<gfxWindowsSurface> mSurface; RefPtr<gfx::DrawTarget> mDrawTarget; gfx::IntSize mSize; gfx::SurfaceFormat mFormat; bool mIsLocked; };
--- a/gfx/layers/apz/src/Axis.cpp +++ b/gfx/layers/apz/src/Axis.cpp @@ -33,17 +33,21 @@ Axis::Axis(AsyncPanZoomController* aAsyn { } void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, uint32_t aTimestampMs) { // mVelocityQueue is controller-thread only AsyncPanZoomController::AssertOnControllerThread(); if (aTimestampMs == mPosTimeMs) { - // Duplicate event? + // This could be a duplicate event, or it could be a legitimate event + // on some platforms that generate events really fast. As a compromise + // update mPos so we don't run into problems like bug 1042734, even though + // that means the velocity will be stale. Better than doing a divide-by-zero. + mPos = aPos; return; } float newVelocity = mAxisLocked ? 0 : (float)(mPos - aPos) / (float)(aTimestampMs - mPosTimeMs); if (gfxPrefs::APZMaxVelocity() > 0.0f) { newVelocity = std::min(newVelocity, gfxPrefs::APZMaxVelocity() * APZCTreeManager::GetDPI()); }
--- a/gfx/layers/basic/TextureClientX11.cpp +++ b/gfx/layers/basic/TextureClientX11.cpp @@ -27,16 +27,29 @@ TextureClientX11::TextureClientX11(ISurf MOZ_COUNT_CTOR(TextureClientX11); } TextureClientX11::~TextureClientX11() { MOZ_COUNT_DTOR(TextureClientX11); } +TemporaryRef<TextureClient> +TextureClientX11::CreateSimilar(TextureFlags aFlags, + TextureAllocationFlags aAllocFlags) const +{ + RefPtr<TextureClient> tex = new TextureClientX11(mAllocator, mFormat, mFlags); + + if (!tex->AllocateForSurface(mSize, aAllocFlags)) { + return nullptr; + } + + return tex; +} + bool TextureClientX11::IsAllocated() const { return !!mSurface; } bool TextureClientX11::Lock(OpenMode aMode)
--- a/gfx/layers/basic/TextureClientX11.h +++ b/gfx/layers/basic/TextureClientX11.h @@ -42,16 +42,20 @@ class TextureClientX11 : public TextureC virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; } virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE; virtual gfx::SurfaceFormat GetFormat() const { return mFormat; } virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; } + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT, + TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE; + private: gfx::SurfaceFormat mFormat; gfx::IntSize mSize; RefPtr<gfxXlibSurface> mSurface; RefPtr<ISurfaceAllocator> mAllocator; RefPtr<gfx::DrawTarget> mDrawTarget; bool mLocked; };
--- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -23,16 +23,17 @@ #include "mozilla/layers/SimpleTextureClientPool.h" // for SimpleTextureClientPool #include "nsAString.h" #include "nsIWidget.h" // for nsIWidget #include "nsIWidgetListener.h" #include "nsTArray.h" // for AutoInfallibleTArray #include "nsXULAppAPI.h" // for XRE_GetProcessType, etc #include "TiledLayerBuffer.h" #include "mozilla/dom/WindowBinding.h" // for Overfill Callback +#include "gfxPrefs.h" #ifdef MOZ_WIDGET_ANDROID #include "AndroidBridge.h" #endif namespace mozilla { namespace layers { using namespace mozilla::gfx; @@ -160,17 +161,17 @@ ClientLayerManager::BeginTransactionWith // If we have a non-default target, we need to let our shadow manager draw // to it. This will happen at the end of the transaction. if (aTarget && XRE_GetProcessType() == GeckoProcessType_Default) { mShadowTarget = aTarget; } // If this is a new paint, increment the paint sequence number. - if (!mIsRepeatTransaction) { + if (!mIsRepeatTransaction && gfxPrefs::APZTestLoggingEnabled()) { ++mPaintSequenceNumber; mApzTestData.StartNewPaint(mPaintSequenceNumber); } } void ClientLayerManager::BeginTransaction() { @@ -319,16 +320,24 @@ ClientLayerManager::GetCompositorSideAPZ { if (mForwarder->HasShadowManager()) { if (!mForwarder->GetShadowManager()->SendGetAPZTestData(aData)) { NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed"); } } } +void +ClientLayerManager::StartNewRepaintRequest(SequenceNumber aSequenceNumber) +{ + if (gfxPrefs::APZTestLoggingEnabled()) { + mApzTestData.StartNewRepaintRequest(aSequenceNumber); + } +} + bool ClientLayerManager::RequestOverfill(mozilla::dom::OverfillCallback* aCallback) { MOZ_ASSERT(aCallback != nullptr); MOZ_ASSERT(HasShadowManager(), "Request Overfill only supported on b2g for now"); if (HasShadowManager()) { CompositorChild* child = GetRemoteRenderer();
--- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -198,20 +198,18 @@ public: const std::string& aValue) { mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue); } // Log APZ test data for a repaint request. The sequence number must be // passed in from outside, and APZTestData::StartNewRepaintRequest() needs // to be called from the outside as well when a new repaint request is started. - void StartNewRepaintRequest(SequenceNumber aSequenceNumber) - { - mApzTestData.StartNewRepaintRequest(aSequenceNumber); - } + void StartNewRepaintRequest(SequenceNumber aSequenceNumber); + // TODO(botond): When we start using this and write a wrapper similar to // nsLayoutUtils::LogTestDataForPaint(), make sure that wrapper checks // gfxPrefs::APZTestLoggingEnabled(). void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber, FrameMetrics::ViewID aScrollId, const std::string& aKey, const std::string& aValue) {
--- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -190,48 +190,16 @@ ContentClientRemoteBuffer::EndPaint() mTextureClient->Unlock(); } if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) { mTextureClientOnWhite->Unlock(); } ContentClientRemote::EndPaint(); } -bool -ContentClientRemoteBuffer::CreateAndAllocateTextureClient(RefPtr<TextureClient>& aClient, - TextureFlags aFlags) -{ - TextureAllocationFlags allocFlags = TextureAllocationFlags::ALLOC_CLEAR_BUFFER; - if (aFlags & TextureFlags::ON_WHITE) { - allocFlags = TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE; - } - - // gfx::BackendType::NONE means fallback to the content backend - aClient = CreateTextureClientForDrawing(mSurfaceFormat, mSize, - gfx::BackendType::NONE, - mTextureInfo.mTextureFlags | aFlags, - allocFlags); - if (!aClient) { - // try with ALLOC_FALLBACK - aClient = CreateTextureClientForDrawing(mSurfaceFormat, mSize, - gfx::BackendType::NONE, - mTextureInfo.mTextureFlags - | TextureFlags::ALLOC_FALLBACK - | aFlags, - allocFlags); - } - - if (!aClient) { - return false; - } - - NS_WARN_IF_FALSE(aClient->IsValid(), "Created an invalid texture client"); - return true; -} - void ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat, const nsIntRect& aRect, uint32_t aFlags) { // If we hit this assertion, then it might be due to an empty transaction // followed by a real transaction. Our buffers should be created (but not // painted in the empty transaction) and then painted (but not created) in the @@ -254,24 +222,42 @@ ContentClientRemoteBuffer::BuildTextureC } CreateBackBuffer(mBufferRect); } void ContentClientRemoteBuffer::CreateBackBuffer(const nsIntRect& aBufferRect) { - if (!CreateAndAllocateTextureClient(mTextureClient, TextureFlags::ON_BLACK) || - !AddTextureClient(mTextureClient)) { + // gfx::BackendType::NONE means fallback to the content backend + mTextureClient = CreateTextureClientForDrawing( + mSurfaceFormat, mSize, gfx::BackendType::NONE, + mTextureInfo.mTextureFlags, + TextureAllocationFlags::ALLOC_CLEAR_BUFFER + ); + if (!mTextureClient) { + // try with ALLOC_FALLBACK + mTextureClient = CreateTextureClientForDrawing( + mSurfaceFormat, mSize, gfx::BackendType::NONE, + mTextureInfo.mTextureFlags | TextureFlags::ALLOC_FALLBACK, + TextureAllocationFlags::ALLOC_CLEAR_BUFFER + ); + } + + if (!mTextureClient || !AddTextureClient(mTextureClient)) { AbortTextureClientCreation(); return; } + if (mTextureInfo.mTextureFlags & TextureFlags::COMPONENT_ALPHA) { - if (!CreateAndAllocateTextureClient(mTextureClientOnWhite, TextureFlags::ON_WHITE) || - !AddTextureClient(mTextureClientOnWhite)) { + mTextureClientOnWhite = mTextureClient->CreateSimilar( + mTextureInfo.mTextureFlags, + TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE + ); + if (!mTextureClientOnWhite || !AddTextureClient(mTextureClientOnWhite)) { AbortTextureClientCreation(); return; } } } void ContentClientRemoteBuffer::CreateBuffer(ContentType aType,
--- a/gfx/layers/client/ContentClient.h +++ b/gfx/layers/client/ContentClient.h @@ -274,19 +274,16 @@ protected: // Ensure we have a valid back buffer if we have a valid front buffer (i.e. // if a backbuffer has been created.) virtual void EnsureBackBufferIfFrontBuffer() {} // Create the front buffer for the ContentClient/Host pair if necessary // and notify the compositor that we have created the buffer(s). virtual void DestroyFrontBuffer() {} - bool CreateAndAllocateTextureClient(RefPtr<TextureClient>& aClient, - TextureFlags aFlags = TextureFlags::NO_FLAGS); - virtual void AbortTextureClientCreation() { mTextureClient = nullptr; mTextureClientOnWhite = nullptr; mIsNewBuffer = false; } RefPtr<TextureClient> mTextureClient;
--- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -645,16 +645,29 @@ BufferTextureClient::BufferTextureClient , mBackend(aMoz2DBackend) , mOpenMode(OpenMode::OPEN_NONE) , mLocked(false) {} BufferTextureClient::~BufferTextureClient() {} +TemporaryRef<TextureClient> +BufferTextureClient::CreateSimilar(TextureFlags aFlags, + TextureAllocationFlags aAllocFlags) const +{ + // This may return null + RefPtr<BufferTextureClient> newBufferTex = TextureClient::CreateForRawBufferAccess( + mAllocator, mFormat, mSize, mBackend, mFlags | aFlags, aAllocFlags + ); + + RefPtr<TextureClient> newTex = newBufferTex.get(); + return newTex; +} + ISurfaceAllocator* BufferTextureClient::GetAllocator() const { return mAllocator; } bool BufferTextureClient::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
--- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -21,16 +21,17 @@ #include "mozilla/ipc/Shmem.h" // for Shmem #include "mozilla/layers/AtomicRefCountedWithFinalize.h" #include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor #include "mozilla/mozalloc.h" // for operator delete #include "nsAutoPtr.h" // for nsRefPtr #include "nsCOMPtr.h" // for already_AddRefed #include "nsISupportsImpl.h" // for TextureImage::AddRef, etc +#include "GfxTexturesReporter.h" class gfxReusableSurfaceWrapper; class gfxImageSurface; namespace mozilla { namespace gl { class GLContext; class SurfaceStream; @@ -149,16 +150,36 @@ public: // pointers) with a certain buffer size. It's unfortunate that we need this. // providing format and sizes could let us do more optimization. static TemporaryRef<BufferTextureClient> CreateWithBufferSize(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat, size_t aSize, TextureFlags aTextureFlags); + // Creates and allocates a TextureClient of the same type. + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT, + TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const = 0; + + /** + * Allocates for a given surface size, taking into account the pixel format + * which is part of the state of the TextureClient. + * + * Does not clear the surface by default, clearing the surface can be done + * by passing the CLEAR_BUFFER flag. + * + * TextureClients that can expose a DrawTarget should override this method. + */ + virtual bool AllocateForSurface(gfx::IntSize aSize, + TextureAllocationFlags flags = ALLOC_DEFAULT) + { + return false; + } + virtual TextureClientYCbCr* AsTextureClientYCbCr() { return nullptr; } /** * Locks the shared data, allowing the caller to get access to it. * * Please always lock/unlock when accessing the shared data. * If Lock() returns false, you should not attempt to access the shared data. */ @@ -339,16 +360,24 @@ public: */ virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) {} /** * This function waits until the buffer is no longer being used. */ virtual void WaitForBufferOwnership() {} + /** + * Track how much of this texture is wasted. + * For example we might allocate a 256x256 tile but only use 10x10. + */ + void SetWaste(int aWasteArea) { + mWasteTracker.Update(aWasteArea, BytesPerPixel(GetFormat())); + } + private: /** * Called once, just before the destructor. * * Here goes the shut-down code that uses virtual methods. * Must only be called by Release(). */ void Finalize(); @@ -358,32 +387,16 @@ private: protected: /** * An invalid TextureClient cannot provide access to its shared data * anymore. This usually means it will soon be destroyed. */ void MarkInvalid() { mValid = false; } /** - * Allocates for a given surface size, taking into account the pixel format - * which is part of the state of the TextureClient. - * - * Does not clear the surface by default, clearing the surface can be done - * by passing the CLEAR_BUFFER flag. - * - * TextureClients that can expose a DrawTarget should override this method. - */ - virtual bool AllocateForSurface(gfx::IntSize aSize, - TextureAllocationFlags flags = ALLOC_DEFAULT) - { - return false; - } - - - /** * Should only be called *once* per texture, in TextureClient::InitIPDLActor. * Some texture implementations rely on the fact that the descriptor will be * deserialized. * Calling ToSurfaceDescriptor again after it has already returned true, * or never constructing a TextureHost with aDescriptor may result in a memory * leak (see CairoTextureClientD3D9 for example). */ virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor) = 0; @@ -397,20 +410,21 @@ protected: ISurfaceAllocator* GetAllocator() { return mAllocator; } RefPtr<TextureChild> mActor; RefPtr<ISurfaceAllocator> mAllocator; TextureFlags mFlags; + FenceHandle mReleaseFenceHandle; + FenceHandle mAcquireFenceHandle; + gl::GfxTextureWasteTracker mWasteTracker; bool mShared; bool mValid; - FenceHandle mReleaseFenceHandle; - FenceHandle mAcquireFenceHandle; friend class TextureChild; friend class RemoveTextureFromCompositableTracker; friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*); friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&); }; /** @@ -484,16 +498,20 @@ public: virtual bool Allocate(uint32_t aSize) = 0; virtual size_t GetBufferSize() const = 0; virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; } ISurfaceAllocator* GetAllocator() const; + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT, + TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE; + protected: RefPtr<gfx::DrawTarget> mDrawTarget; RefPtr<ISurfaceAllocator> mAllocator; gfx::SurfaceFormat mFormat; gfx::IntSize mSize; gfx::BackendType mBackend; OpenMode mOpenMode; bool mLocked; @@ -592,16 +610,22 @@ public: virtual gfx::IntSize GetSize() const { return gfx::IntSize(); } virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return gfx::SurfaceFormat::UNKNOWN; } + // This TextureClient should not be used in a context where we use CreateSimilar + // (ex. component alpha) because the underlying texture data is always created by + // an external producer. + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags, TextureAllocationFlags) const MOZ_OVERRIDE { return nullptr; } + virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags) MOZ_OVERRIDE { MOZ_CRASH("Should never hit this."); return false; } protected: bool mIsLocked;
--- a/gfx/layers/client/TiledContentClient.cpp +++ b/gfx/layers/client/TiledContentClient.cpp @@ -909,19 +909,20 @@ ClientTiledLayerBuffer::ValidateTile(Til offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution); bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget; RefPtr<TextureClient> backBuffer = aTile.GetBackBuffer(offsetScaledDirtyRegion, mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())), &createdTextureClient, !usingSinglePaintBuffer); - if (!backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) { + if (!backBuffer || !backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) { NS_WARNING("Failed to lock tile TextureClient for updating."); aTile.DiscardFrontBuffer(); + aTile.DiscardBackBuffer(); return aTile; } // We must not keep a reference to the DrawTarget after it has been unlocked, // make sure these are null'd before unlocking as destruction of the context // may cause the target to be flushed. RefPtr<DrawTarget> drawTarget = backBuffer->BorrowDrawTarget(); drawTarget->SetTransform(Matrix()); @@ -1030,16 +1031,24 @@ ClientTiledLayerBuffer::ValidateTile(Til #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution, aTileOrigin.y * mResolution, GetTileLength(), GetTileLength()); #endif ctxt = nullptr; drawTarget = nullptr; + nsIntRegion tileRegion = + nsIntRect(aTileOrigin.x, aTileOrigin.y, + GetScaledTileSize().width, GetScaledTileSize().height); + // Intersect this area with the portion that's invalid. + tileRegion = tileRegion.Sub(tileRegion, GetValidRegion()); + tileRegion = tileRegion.Sub(tileRegion, aDirtyRegion); // Has now been validated + + backBuffer->SetWaste(tileRegion.Area() * mResolution * mResolution); backBuffer->Unlock(); if (createdTextureClient) { if (!mCompositableClient->AddTextureClient(backBuffer)) { NS_WARNING("Failed to add tile TextureClient."); aTile.DiscardFrontBuffer(); aTile.DiscardBackBuffer(); return aTile;
--- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -180,16 +180,29 @@ TextureClientD3D11::~TextureClientD3D11( MOZ_ASSERT(mDrawTarget->refCount() == 1); LockD3DTexture(mTexture.get()); mDrawTarget = nullptr; UnlockD3DTexture(mTexture.get()); } #endif } +TemporaryRef<TextureClient> +TextureClientD3D11::CreateSimilar(TextureFlags aFlags, + TextureAllocationFlags aAllocFlags) const +{ + RefPtr<TextureClient> tex = new TextureClientD3D11(mFormat, mFlags | aFlags); + + if (!tex->AllocateForSurface(mSize, ALLOC_DEFAULT)) { + return nullptr; + } + + return tex; +} + bool TextureClientD3D11::Lock(OpenMode aMode) { if (!mTexture) { return false; } MOZ_ASSERT(!mIsLocked, "The Texture is already locked!");
--- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -54,16 +54,20 @@ public: virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; } virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE; virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE; + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT, + TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE; + protected: gfx::IntSize mSize; RefPtr<ID3D10Texture2D> mTexture; RefPtr<gfx::DrawTarget> mDrawTarget; gfx::SurfaceFormat mFormat; bool mIsLocked; bool mNeedsClear; bool mNeedsClearWhite;
--- a/gfx/layers/d3d9/TextureD3D9.cpp +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -561,16 +561,28 @@ CairoTextureClientD3D9::CairoTextureClie MOZ_COUNT_CTOR(CairoTextureClientD3D9); } CairoTextureClientD3D9::~CairoTextureClientD3D9() { MOZ_COUNT_DTOR(CairoTextureClientD3D9); } +TemporaryRef<TextureClient> +CairoTextureClientD3D9::CreateSimilar(TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const +{ + RefPtr<TextureClient> tex = new CairoTextureClientD3D9(mFormat, mFlags | aFlags); + + if (!tex->AllocateForSurface(mSize, aAllocFlags)) { + return nullptr; + } + + return tex; +} + bool CairoTextureClientD3D9::Lock(OpenMode aMode) { MOZ_ASSERT(!mIsLocked); if (!IsValid() || !IsAllocated()) { return false; }
--- a/gfx/layers/d3d9/TextureD3D9.h +++ b/gfx/layers/d3d9/TextureD3D9.h @@ -211,16 +211,20 @@ public: virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE; virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE; virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; } + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT, + TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE; + private: RefPtr<IDirect3DTexture9> mTexture; nsRefPtr<IDirect3DSurface9> mD3D9Surface; RefPtr<gfx::DrawTarget> mDrawTarget; nsRefPtr<gfxASurface> mSurface; gfx::IntSize mSize; gfx::SurfaceFormat mFormat; bool mIsLocked; @@ -263,16 +267,22 @@ public: virtual gfx::IntSize GetSize() const { return gfx::IntSize(mDesc.Width, mDesc.Height); } virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; } + // This TextureClient should not be used in a context where we use CreateSimilar + // (ex. component alpha) because the underlying texture data is always created by + // an external producer. + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags, TextureAllocationFlags) const MOZ_OVERRIDE { return nullptr; } + private: RefPtr<IDirect3DTexture9> mTexture; gfx::SurfaceFormat mFormat; HANDLE mHandle; D3DSURFACE_DESC mDesc; bool mIsLocked; };
--- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -244,16 +244,19 @@ static void ConnectImageBridge(ImageBrid child->Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide); } ImageBridgeChild::ImageBridgeChild() : mShuttingDown(false) { MOZ_ASSERT(NS_IsMainThread()); + // Always run destructor on the main thread + SetMessageLoopToPostDestructionTo(MessageLoop::current()); + mTxn = new CompositableTransaction(); } ImageBridgeChild::~ImageBridgeChild() { MOZ_ASSERT(NS_IsMainThread()); delete mTxn; }
--- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -590,17 +590,17 @@ ShadowLayerForwarder::EndTransaction(Inf } else { // If we don't require a swap we can call SendUpdateNoSwap which // assumes that aReplies is empty (DEBUG assertion) MOZ_LAYERS_LOG(("[LayersForwarder] sending no swap transaction...")); RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093"); if (!HasShadowManager() || !mShadowManager->IPCOpen() || !mShadowManager->SendUpdateNoSwap(cset, aId, targetConfig, mIsFirstPaint, - aPaintSequenceNumber, aScheduleComposite, + aScheduleComposite, aPaintSequenceNumber, aIsRepeatTransaction)) { MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!")); return false; } } *aSent = true; mIsFirstPaint = false;
--- a/gfx/layers/opengl/GrallocTextureClient.cpp +++ b/gfx/layers/opengl/GrallocTextureClient.cpp @@ -50,16 +50,31 @@ GrallocTextureClientOGL::~GrallocTexture ISurfaceAllocator* allocator = GetAllocator(); if (ShouldDeallocateInDestructor()) { allocator->DeallocGrallocBuffer(&mGrallocHandle); } else { allocator->DropGrallocBuffer(&mGrallocHandle); } } +TemporaryRef<TextureClient> +GrallocTextureClientOGL::CreateSimilar(TextureFlags aFlags, + TextureAllocationFlags aAllocFlags) const +{ + RefPtr<TextureClient> tex = new GrallocTextureClientOGL( + mAllocator, mFormat, mBackend, mFlags | aFlags + ); + + if (!tex->AllocateForSurface(mSize, aAllocFlags)) { + return nullptr; + } + + return tex; +} + void GrallocTextureClientOGL::InitWith(MaybeMagicGrallocBufferHandle aHandle, gfx::IntSize aSize) { MOZ_ASSERT(!IsAllocated()); MOZ_ASSERT(IsValid()); mGrallocHandle = aHandle; mGraphicBuffer = GetGraphicBufferFrom(aHandle); mSize = aSize;
--- a/gfx/layers/opengl/GrallocTextureClient.h +++ b/gfx/layers/opengl/GrallocTextureClient.h @@ -109,16 +109,20 @@ public: mMediaBuffer = aMediaBuffer; } android::MediaBuffer* GetMediaBuffer() { return mMediaBuffer; } + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT, + TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE; + protected: /** * Unfortunately, until bug 879681 is fixed we need to use a GrallocBufferActor. */ MaybeMagicGrallocBufferHandle mGrallocHandle; RefPtr<AsyncTransactionTracker> mRemoveFromCompositableTracker;
--- a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h +++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h @@ -31,16 +31,22 @@ public: virtual bool IsAllocated() const MOZ_OVERRIDE { return !!mSurface; } virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE; virtual gfx::IntSize GetSize() const; virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; } + // This TextureClient should not be used in a context where we use CreateSimilar + // (ex. component alpha) because the underlying texture data is always created by + // an external producer. + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags, TextureAllocationFlags) const MOZ_OVERRIDE { return nullptr; } + protected: RefPtr<MacIOSurface> mSurface; bool mIsLocked; }; } }
--- a/gfx/layers/opengl/TextureClientOGL.h +++ b/gfx/layers/opengl/TextureClientOGL.h @@ -55,16 +55,22 @@ public: virtual gfx::IntSize GetSize() const { return mSize; } virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return gfx::SurfaceFormat::UNKNOWN; } + // This TextureClient should not be used in a context where we use CreateSimilar + // (ex. component alpha) because the underlying texture data is always created by + // an external producer. + virtual TemporaryRef<TextureClient> + CreateSimilar(TextureFlags, TextureAllocationFlags) const MOZ_OVERRIDE { return nullptr; } + virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags) MOZ_OVERRIDE { return false; } protected: gl::SharedTextureHandle mHandle; gfx::IntSize mSize;
--- a/gfx/thebes/gfxContext.h +++ b/gfx/thebes/gfxContext.h @@ -14,17 +14,17 @@ #include "gfxMatrix.h" #include "gfxPattern.h" #include "nsTArray.h" #include "nsAutoPtr.h" #include "mozilla/gfx/2D.h" typedef struct _cairo cairo_t; -struct GlyphBufferAzure; +class GlyphBufferAzure; template <typename T> class FallibleTArray; /** * This is the main class for doing actual drawing. It is initialized using * a surface and can be drawn on. It manages various state information like * a current transformation matrix (CTM), a current path, current color, * etc. * @@ -711,17 +711,17 @@ public: #endif static mozilla::gfx::UserDataKey sDontUseAsSourceKey; private: ~gfxContext(); friend class GeneralPattern; - friend struct GlyphBufferAzure; + friend class GlyphBufferAzure; typedef mozilla::gfx::Matrix Matrix; typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::gfx::Color Color; typedef mozilla::gfx::StrokeOptions StrokeOptions; typedef mozilla::gfx::Float Float; typedef mozilla::gfx::Rect Rect; typedef mozilla::gfx::CompositionOp CompositionOp;
--- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -3039,236 +3039,222 @@ gfxFont::HasFeatureSet(uint32_t aFeature /** * A helper function in case we need to do any rounding or other * processing here. */ #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \ (double(aAppUnits)*double(aDevUnitsPerAppUnit)) -struct GlyphBuffer { -#define GLYPH_BUFFER_SIZE (2048/sizeof(cairo_glyph_t)) - cairo_glyph_t mGlyphBuffer[GLYPH_BUFFER_SIZE]; - unsigned int mNumGlyphs; - - GlyphBuffer() - : mNumGlyphs(0) { } - - cairo_glyph_t *AppendGlyph() { - return &mGlyphBuffer[mNumGlyphs++]; - } - - void Flush(cairo_t *aCR, DrawMode aDrawMode, bool aReverse, - gfxTextContextPaint *aContextPaint, - const gfxMatrix& aGlobalMatrix, bool aFinish = false) { - // Ensure there's enough room for a glyph to be added to the buffer - // and we actually have glyphs to draw - if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) { - return; - } - - if (aReverse) { - for (uint32_t i = 0; i < mNumGlyphs/2; ++i) { - cairo_glyph_t tmp = mGlyphBuffer[i]; - mGlyphBuffer[i] = mGlyphBuffer[mNumGlyphs - 1 - i]; - mGlyphBuffer[mNumGlyphs - 1 - i] = tmp; - } - } - - if (aDrawMode == DrawMode::GLYPH_PATH) { - cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs); - } else { - if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == - (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) { - FlushStroke(aCR, aContextPaint, aGlobalMatrix); - } - if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) { - PROFILER_LABEL("GlyphBuffer", "Flush::cairo_show_glyphs", - js::ProfileEntry::Category::GRAPHICS); - - nsRefPtr<gfxPattern> pattern; - if (aContextPaint && - !!(pattern = aContextPaint->GetFillPattern(aGlobalMatrix))) { - cairo_save(aCR); - cairo_set_source(aCR, pattern->CairoPattern()); - } - - cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs); - - if (pattern) { - cairo_restore(aCR); - } - } - if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == - int(DrawMode::GLYPH_STROKE)) { - FlushStroke(aCR, aContextPaint, aGlobalMatrix); - } - } - - mNumGlyphs = 0; - } - -private: - void FlushStroke(cairo_t *aCR, gfxTextContextPaint *aContextPaint, - const gfxMatrix& aGlobalMatrix) { - nsRefPtr<gfxPattern> pattern; - if (aContextPaint && - !!(pattern = aContextPaint->GetStrokePattern(aGlobalMatrix))) { - cairo_save(aCR); - cairo_set_source(aCR, pattern->CairoPattern()); - } - - cairo_new_path(aCR); - cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs); - cairo_stroke(aCR); - - if (pattern) { - cairo_restore(aCR); - } - } - -#undef GLYPH_BUFFER_SIZE -}; - static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) { switch (aAAOption) { case gfxFont::kAntialiasSubpixel: return AntialiasMode::SUBPIXEL; case gfxFont::kAntialiasGrayscale: return AntialiasMode::GRAY; case gfxFont::kAntialiasNone: return AntialiasMode::NONE; default: return AntialiasMode::DEFAULT; } } -struct GlyphBufferAzure { +// Parameters passed to gfxFont methods for drawing glyphs from a textrun. +// The TextRunDrawParams are set up once per textrun; the FontDrawParams +// are dependent on the specific font, so they are set per GlyphRun. + +struct TextRunDrawParams { + RefPtr<DrawTarget> dt; + gfxContext *context; + gfxFont::Spacing *spacing; + gfxTextRunDrawCallbacks *callbacks; + gfxTextContextPaint *contextPaint; + gfxFloat direction; + double devPerApp; + DrawMode drawMode; + bool isRTL; + bool paintSVGGlyphs; +}; + +struct FontDrawParams { + RefPtr<ScaledFont> scaledFont; + RefPtr<GlyphRenderingOptions> renderingOptions; + Matrix *passedInvMatrix; + Matrix matInv; + double synBoldOnePixelOffset; + int32_t extraStrikes; + DrawOptions drawOptions; + bool haveSVGGlyphs; + bool haveColorGlyphs; +}; + +class GlyphBufferAzure +{ +public: + GlyphBufferAzure(const TextRunDrawParams& aRunParams, + const FontDrawParams& aFontParams) + : mRunParams(aRunParams) + , mFontParams(aFontParams) + , mNumGlyphs(0) + { + } + + ~GlyphBufferAzure() + { + Flush(true); // flush any remaining buffered glyphs + } + + void OutputGlyph(uint32_t aGlyphID, const gfxPoint& aPt) + { + Glyph *glyph = AppendGlyph(); + glyph->mIndex = aGlyphID; + glyph->mPosition.x = aPt.x; + glyph->mPosition.y = aPt.y; + glyph->mPosition = mFontParams.matInv * glyph->mPosition; + Flush(false); // this will flush only if the buffer is full + } + + const TextRunDrawParams& mRunParams; + const FontDrawParams& mFontParams; + +private: #define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph)) - Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE]; - unsigned int mNumGlyphs; - - GlyphBufferAzure() - : mNumGlyphs(0) { } - - Glyph *AppendGlyph() { + + Glyph *AppendGlyph() + { return &mGlyphBuffer[mNumGlyphs++]; } - void Flush(DrawTarget *aDT, gfxTextContextPaint *aContextPaint, ScaledFont *aFont, - DrawMode aDrawMode, bool aReverse, const GlyphRenderingOptions *aOptions, - gfxContext *aThebesContext, const Matrix *aInvFontMatrix, const DrawOptions &aDrawOptions, - bool aFinish = false) + // Render the buffered glyphs to the draw target and clear the buffer. + // This actually flushes the glyphs only if the buffer is full, or if the + // aFinish parameter is true; otherwise it simply returns. + void Flush(bool aFinish) { // Ensure there's enough room for a glyph to be added to the buffer if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) { return; } - if (aReverse) { + if (mRunParams.isRTL) { Glyph *begin = &mGlyphBuffer[0]; Glyph *end = &mGlyphBuffer[mNumGlyphs]; std::reverse(begin, end); } - + gfx::GlyphBuffer buf; buf.mGlyphs = mGlyphBuffer; buf.mNumGlyphs = mNumGlyphs; - gfxContext::AzureState state = aThebesContext->CurrentState(); - if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == - (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) { - FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state); - } - if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) { - if (state.pattern || aContextPaint) { + gfxContext::AzureState state = mRunParams.context->CurrentState(); + if ((int(mRunParams.drawMode) & + (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == + (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) { + FlushStroke(buf, state); + } + if (int(mRunParams.drawMode) & int(DrawMode::GLYPH_FILL)) { + if (state.pattern || mRunParams.contextPaint) { Pattern *pat; nsRefPtr<gfxPattern> fillPattern; - if (!aContextPaint || - !(fillPattern = aContextPaint->GetFillPattern(aThebesContext->CurrentMatrix()))) { + if (!mRunParams.contextPaint || + !(fillPattern = mRunParams.contextPaint->GetFillPattern( + mRunParams.context->CurrentMatrix()))) { if (state.pattern) { - pat = state.pattern->GetPattern(aDT, state.patternTransformChanged ? &state.patternTransform : nullptr); + pat = state.pattern->GetPattern(mRunParams.dt, + state.patternTransformChanged ? + &state.patternTransform : nullptr); } else { pat = nullptr; } } else { - pat = fillPattern->GetPattern(aDT); + pat = fillPattern->GetPattern(mRunParams.dt); } if (pat) { Matrix saved; Matrix *mat = nullptr; - if (aInvFontMatrix) { - // The brush matrix needs to be multiplied with the inverted matrix - // as well, to move the brush into the space of the glyphs. Before - // the render target transformation - - // This relies on the returned Pattern not to be reused by - // others, but regenerated on GetPattern calls. This is true! + if (mFontParams.passedInvMatrix) { + // The brush matrix needs to be multiplied with the + // inverted matrix as well, to move the brush into the + // space of the glyphs. + + // This relies on the returned Pattern not to be reused + // by others, but regenerated on GetPattern calls. This + // is true! if (pat->GetType() == PatternType::LINEAR_GRADIENT) { mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix; } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) { mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix; } else if (pat->GetType() == PatternType::SURFACE) { mat = &static_cast<SurfacePattern*>(pat)->mMatrix; } if (mat) { saved = *mat; - *mat = (*mat) * (*aInvFontMatrix); + *mat = (*mat) * (*mFontParams.passedInvMatrix); } } - aDT->FillGlyphs(aFont, buf, *pat, - aDrawOptions, aOptions); + mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf, + *pat, mFontParams.drawOptions, + mFontParams.renderingOptions); if (mat) { *mat = saved; } } } else if (state.sourceSurface) { - aDT->FillGlyphs(aFont, buf, SurfacePattern(state.sourceSurface, - ExtendMode::CLAMP, - state.surfTransform), - aDrawOptions, aOptions); + mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf, + SurfacePattern(state.sourceSurface, + ExtendMode::CLAMP, + state.surfTransform), + mFontParams.drawOptions, + mFontParams.renderingOptions); } else { - aDT->FillGlyphs(aFont, buf, ColorPattern(state.color), - aDrawOptions, aOptions); + mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf, + ColorPattern(state.color), + mFontParams.drawOptions, + mFontParams.renderingOptions); } } - if (int(aDrawMode) & int(DrawMode::GLYPH_PATH)) { - aThebesContext->EnsurePathBuilder(); - Matrix mat = aDT->GetTransform(); - aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, - aDT->GetBackendType(), &mat); - } - if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == - int(DrawMode::GLYPH_STROKE)) { - FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state); + if (int(mRunParams.drawMode) & int(DrawMode::GLYPH_PATH)) { + mRunParams.context->EnsurePathBuilder(); + Matrix mat = mRunParams.dt->GetTransform(); + mFontParams.scaledFont->CopyGlyphsToBuilder( + buf, mRunParams.context->mPathBuilder, + mRunParams.dt->GetBackendType(), &mat); + } + if ((int(mRunParams.drawMode) & + (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == + int(DrawMode::GLYPH_STROKE)) { + FlushStroke(buf, state); } mNumGlyphs = 0; } -private: - void FlushStroke(DrawTarget *aDT, gfxTextContextPaint *aContextPaint, - ScaledFont *aFont, gfxContext *aThebesContext, - gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState) + void FlushStroke(gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState) { - RefPtr<Path> path = aFont->GetPathForGlyphs(aBuf, aDT); - if (aContextPaint) { + RefPtr<Path> path = + mFontParams.scaledFont->GetPathForGlyphs(aBuf, mRunParams.dt); + if (mRunParams.contextPaint) { nsRefPtr<gfxPattern> strokePattern = - aContextPaint->GetStrokePattern(aThebesContext->CurrentMatrix()); + mRunParams.contextPaint->GetStrokePattern( + mRunParams.context->CurrentMatrix()); if (strokePattern) { - aDT->Stroke(path, *strokePattern->GetPattern(aDT), aState.strokeOptions); + mRunParams.dt->Stroke(path, + *strokePattern->GetPattern(mRunParams.dt), + aState.strokeOptions); } } } + Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE]; + unsigned int mNumGlyphs; + #undef GLYPH_BUFFER_SIZE }; // Bug 674909. When synthetic bolding text by drawing twice, need to // render using a pixel offset in device pixels, otherwise text // doesn't appear bolded, it appears as if a bad text shadow exists // when a non-identity transform exists. Use an offset factor so that // the second draw occurs at a constant offset in device pixels. @@ -3297,332 +3283,258 @@ gfxFont::CalcXScale(gfxContext *aContext static DrawMode ForcePaintingDrawMode(DrawMode aDrawMode) { return aDrawMode == DrawMode::GLYPH_PATH ? DrawMode(int(DrawMode::GLYPH_FILL) | int(DrawMode::GLYPH_STROKE)) : aDrawMode; } +// Draw an individual glyph at a specific location. +// *aPt is the glyph position in appUnits; it is converted to device +// coordinates (devPt) here. +void +gfxFont::DrawOneGlyph(uint32_t aGlyphID, double aAdvance, gfxPoint *aPt, + GlyphBufferAzure& aBuffer, bool *aEmittedGlyphs) const +{ + const TextRunDrawParams& runParams(aBuffer.mRunParams); + const FontDrawParams& fontParams(aBuffer.mFontParams); + + double glyphX; + if (runParams.isRTL) { + aPt->x -= aAdvance; + glyphX = aPt->x; + } else { + glyphX = aPt->x; + aPt->x += aAdvance; + } + gfxPoint devPt(ToDeviceUnits(glyphX, runParams.devPerApp), + ToDeviceUnits(aPt->y, runParams.devPerApp)); + + if (fontParams.haveSVGGlyphs) { + if (!runParams.paintSVGGlyphs) { + return; + } + DrawMode mode = ForcePaintingDrawMode(runParams.drawMode); + if (RenderSVGGlyph(runParams.context, devPt, mode, + aGlyphID, runParams.contextPaint, + runParams.callbacks, *aEmittedGlyphs)) { + return; + } + } + + if (fontParams.haveColorGlyphs && + RenderColorGlyph(runParams.context, fontParams.scaledFont, + fontParams.renderingOptions, fontParams.drawOptions, + fontParams.matInv * gfx::Point(devPt.x, devPt.y), + aGlyphID)) { + return; + } + + aBuffer.OutputGlyph(aGlyphID, devPt); + + // Synthetic bolding (if required) by multi-striking. + for (int32_t i = 0; i < fontParams.extraStrikes; ++i) { + devPt.x += fontParams.synBoldOnePixelOffset; + aBuffer.OutputGlyph(aGlyphID, devPt); + } + + *aEmittedGlyphs = true; +} + +// Draw a run of CharacterGlyph records from the given offset in aShapedText. +// Returns true if glyph paths were actually emitted. +bool +gfxFont::DrawGlyphs(gfxShapedText *aShapedText, + uint32_t aOffset, // offset in the textrun + uint32_t aCount, // length of run to draw + gfxPoint *aPt, + const TextRunDrawParams& aRunParams, + const FontDrawParams& aFontParams) +{ + bool emittedGlyphs = false; + GlyphBufferAzure buffer(aRunParams, aFontParams); + + if (aRunParams.spacing) { + aPt->x += aRunParams.direction * aRunParams.spacing[0].mBefore; + } + + const gfxShapedText::CompressedGlyph *glyphData = + &aShapedText->GetCharacterGlyphs()[aOffset]; + + for (uint32_t i = 0; i < aCount; ++i, ++glyphData) { + if (glyphData->IsSimpleGlyph()) { + DrawOneGlyph(glyphData->GetSimpleGlyph(), + glyphData->GetSimpleAdvance(), + aPt, buffer, &emittedGlyphs); + } else { + uint32_t glyphCount = glyphData->GetGlyphCount(); + if (glyphCount > 0) { + const gfxShapedText::DetailedGlyph *details = + aShapedText->GetDetailedGlyphs(aOffset + i); + NS_ASSERTION(details, "detailedGlyph should not be missing!"); + for (uint32_t j = 0; j < glyphCount; ++j, ++details) { + double advance = details->mAdvance; + if (glyphData->IsMissing()) { + // Default-ignorable chars will have zero advance width; + // we don't have to draw the hexbox for them. + if (aRunParams.drawMode != DrawMode::GLYPH_PATH && + advance > 0) { + double glyphX = aPt->x; + if (aRunParams.isRTL) { + glyphX -= advance; + } + gfxPoint pt(ToDeviceUnits(glyphX, aRunParams.devPerApp), + ToDeviceUnits(aPt->y, aRunParams.devPerApp)); + gfxFloat advanceDevUnits = + ToDeviceUnits(advance, aRunParams.devPerApp); + gfxFloat height = GetMetrics().maxAscent; + gfxRect glyphRect(pt.x, pt.y - height, + advanceDevUnits, height); + gfxFontMissingGlyphs::DrawMissingGlyph( + aRunParams.context, glyphRect, details->mGlyphID, + aShapedText->GetAppUnitsPerDevUnit()); + } + } else { + gfxPoint glyphXY(*aPt); + glyphXY.x += details->mXOffset; + glyphXY.y += details->mYOffset; + DrawOneGlyph(details->mGlyphID, advance, &glyphXY, + buffer, &emittedGlyphs); + } + aPt->x += aRunParams.direction * advance; + } + } + } + + if (aRunParams.spacing) { + double space = aRunParams.spacing[i].mAfter; + if (i + 1 < aCount) { + space += aRunParams.spacing[i + 1].mBefore; + } + aPt->x += aRunParams.direction * space; + } + } + + return emittedGlyphs; +} + void gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, - gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt, - Spacing *aSpacing, gfxTextContextPaint *aContextPaint, - gfxTextRunDrawCallbacks *aCallbacks) -{ - NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)), + gfxPoint *aPt, TextRunDrawParams& aRunParams) +{ + NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH || + !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)), "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH"); - if (aStart >= aEnd) + if (aStart >= aEnd) { + return; + } + + FontDrawParams fontParams; + + fontParams.scaledFont = GetScaledFont(aRunParams.dt); + if (!fontParams.scaledFont) { return; - - const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs(); - const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit(); - const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit); - bool isRTL = aTextRun->IsRightToLeft(); - double direction = aTextRun->GetDirection(); - - bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this); - bool haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs(); + } + + fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this); + fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs(); + nsAutoPtr<gfxTextContextPaint> contextPaint; - if (haveSVGGlyphs && !aContextPaint) { + if (fontParams.haveSVGGlyphs && !aRunParams.contextPaint) { // If no pattern is specified for fill, use the current pattern - NS_ASSERTION((int(aDrawMode) & int(DrawMode::GLYPH_STROKE)) == 0, "no pattern supplied for stroking text"); - nsRefPtr<gfxPattern> fillPattern = aContext->GetPattern(); - contextPaint = new SimpleTextContextPaint(fillPattern, nullptr, - aContext->CurrentMatrix()); - aContextPaint = contextPaint; - } - - // synthetic-bold strikes are each offset one device pixel in run direction + NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0, + "no pattern supplied for stroking text"); + nsRefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern(); + contextPaint = + new SimpleTextContextPaint(fillPattern, nullptr, + aRunParams.context->CurrentMatrix()); + aRunParams.contextPaint = contextPaint; + } + + // Synthetic-bold strikes are each offset one device pixel in run direction. // (these values are only needed if IsSyntheticBold() is true) - double synBoldOnePixelOffset = 0; - int32_t strikes = 1; if (IsSyntheticBold()) { - double xscale = CalcXScale(aContext); - synBoldOnePixelOffset = direction * xscale; + double xscale = CalcXScale(aRunParams.context); + fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale; if (xscale != 0.0) { // use as many strikes as needed for the the increased advance - strikes = NS_lroundf(GetSyntheticBoldOffset() / xscale); - } - } - - uint32_t i; - // Current position in appunits - double x = aPt->x; - double y = aPt->y; - double origY = aPt->y; - if (mStyle.baselineOffset != 0.0) { - y += mStyle.baselineOffset * appUnitsPerDevUnit; - } - - RefPtr<DrawTarget> dt = aContext->GetDrawTarget(); - - bool paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs; - bool emittedGlyphs = false; - - { - RefPtr<ScaledFont> scaledFont = GetScaledFont(dt); - - if (!scaledFont) { - return; - } - - bool oldSubpixelAA = dt->GetPermitSubpixelAA(); - - if (!AllowSubpixelAA()) { - dt->SetPermitSubpixelAA(false); - } - - GlyphBufferAzure glyphs; - Glyph *glyph; - - Matrix mat, matInv; - Matrix oldMat = dt->GetTransform(); - - // This is nullptr when we have inverse-transformed glyphs and we need - // to transform the Brush inside flush. - Matrix *passedInvMatrix = nullptr; - - RefPtr<GlyphRenderingOptions> renderingOptions = - GetGlyphRenderingOptions(); - - DrawOptions drawOptions; - drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption); - - // The cairo DrawTarget backend uses the cairo_scaled_font directly - // and so has the font skew matrix applied already. - if (mScaledFont && - dt->GetBackendType() != BackendType::CAIRO) { + fontParams.extraStrikes = + std::max(1, NS_lroundf(GetSyntheticBoldOffset() / xscale)); + } + } else { + fontParams.synBoldOnePixelOffset = 0; + fontParams.extraStrikes = 0; + } + + bool oldSubpixelAA = aRunParams.dt->GetPermitSubpixelAA(); + if (!AllowSubpixelAA()) { + aRunParams.dt->SetPermitSubpixelAA(false); + } + + Matrix mat; + Matrix oldMat = aRunParams.dt->GetTransform(); + + // This is nullptr when we have inverse-transformed glyphs and we need + // to transform the Brush inside flush. + fontParams.passedInvMatrix = nullptr; + + fontParams.renderingOptions = GetGlyphRenderingOptions(); + fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption); + + // The cairo DrawTarget backend uses the cairo_scaled_font directly + // and so has the font skew matrix applied already. + if (mScaledFont && + aRunParams.dt->GetBackendType() != BackendType::CAIRO) { cairo_matrix_t matrix; cairo_scaled_font_get_font_matrix(mScaledFont, &matrix); if (matrix.xy != 0) { - // If this matrix applies a skew, which can happen when drawing - // oblique fonts, we will set the DrawTarget matrix to apply the - // skew. We'll need to move the glyphs by the inverse of the skew to - // get the glyphs positioned correctly in the new device space - // though, since the font matrix should only be applied to drawing - // the glyphs, and not to their position. - mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix)); - - mat._11 = mat._22 = 1.0; - float adjustedSize = mAdjustedSize > 0 ? mAdjustedSize : GetStyle()->size; - mat._21 /= adjustedSize; - - dt->SetTransform(mat * oldMat); - - matInv = mat; - matInv.Invert(); - - passedInvMatrix = &matInv; - } - } - - if (aSpacing) { - x += direction*aSpacing[0].mBefore; - } - for (i = aStart; i < aEnd; ++i) { - const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i]; - if (glyphData->IsSimpleGlyph()) { - double advance = glyphData->GetSimpleAdvance(); - double glyphX; - if (isRTL) { - x -= advance; - glyphX = x; - } else { - glyphX = x; - x += advance; - } - - if (haveSVGGlyphs) { - if (!paintSVGGlyphs) { - continue; - } - gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), - ToDeviceUnits(y, devUnitsPerAppUnit)); - DrawMode mode = ForcePaintingDrawMode(aDrawMode); - if (RenderSVGGlyph(aContext, point, mode, - glyphData->GetSimpleGlyph(), aContextPaint, - aCallbacks, emittedGlyphs)) { - continue; - } - } - - if (haveColorGlyphs) { - mozilla::gfx::Point point(ToDeviceUnits(glyphX, - devUnitsPerAppUnit), - ToDeviceUnits(y, - devUnitsPerAppUnit)); - if (RenderColorGlyph(aContext, scaledFont, renderingOptions, - drawOptions, matInv * point, - glyphData->GetSimpleGlyph())) { - continue; - } - } - - // Perhaps we should put a scale in the cairo context instead of - // doing this scaling here... - // Multiplying by the reciprocal may introduce tiny error here, - // but we assume cairo is going to round coordinates at some stage - // and this is faster - glyph = glyphs.AppendGlyph(); - glyph->mIndex = glyphData->GetSimpleGlyph(); - glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); - glyph->mPosition.y = ToDeviceUnits(y, devUnitsPerAppUnit); - glyph->mPosition = matInv * glyph->mPosition; - glyphs.Flush(dt, aContextPaint, scaledFont, - aDrawMode, isRTL, renderingOptions, - aContext, passedInvMatrix, - drawOptions); - - // synthetic bolding by multi-striking with 1-pixel offsets - // at least once, more if there's room (large font sizes) - if (IsSyntheticBold()) { - double strikeOffset = synBoldOnePixelOffset; - int32_t strikeCount = strikes; - do { - Glyph *doubleglyph; - doubleglyph = glyphs.AppendGlyph(); - doubleglyph->mIndex = glyph->mIndex; - doubleglyph->mPosition.x = - ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit, - devUnitsPerAppUnit); - doubleglyph->mPosition.y = glyph->mPosition.y; - doubleglyph->mPosition = matInv * doubleglyph->mPosition; - strikeOffset += synBoldOnePixelOffset; - glyphs.Flush(dt, aContextPaint, scaledFont, - aDrawMode, isRTL, renderingOptions, - aContext, passedInvMatrix, - drawOptions); - } while (--strikeCount > 0); - } - emittedGlyphs = true; - } else { - uint32_t glyphCount = glyphData->GetGlyphCount(); - if (glyphCount > 0) { - const gfxTextRun::DetailedGlyph *details = - aTextRun->GetDetailedGlyphs(i); - NS_ASSERTION(details, "detailedGlyph should not be missing!"); - double advance; - for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) { - advance = details->mAdvance; - if (glyphData->IsMissing()) { - // default ignorable characters will have zero advance width. - // we don't have to draw the hexbox for them - if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) { - double glyphX = x; - if (isRTL) { - glyphX -= advance; - } - gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit), - ToDeviceUnits(y, devUnitsPerAppUnit)); - gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit); - gfxFloat height = GetMetrics().maxAscent; - gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height); - gfxFontMissingGlyphs::DrawMissingGlyph(aContext, - glyphRect, - details->mGlyphID, - appUnitsPerDevUnit); - } - } else { - double glyphX = x + details->mXOffset; - if (isRTL) { - glyphX -= advance; - } - - gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), - ToDeviceUnits(y, devUnitsPerAppUnit)); - - if (haveSVGGlyphs) { - if (!paintSVGGlyphs) { - continue; - } - DrawMode mode = ForcePaintingDrawMode(aDrawMode); - if (RenderSVGGlyph(aContext, point, mode, - details->mGlyphID, - aContextPaint, aCallbacks, - emittedGlyphs)) { - continue; - } - } - - if (haveColorGlyphs) { - mozilla::gfx::Point point(ToDeviceUnits(glyphX, - devUnitsPerAppUnit), - ToDeviceUnits(y + details->mYOffset, - devUnitsPerAppUnit)); - if (RenderColorGlyph(aContext, scaledFont, - renderingOptions, - drawOptions, matInv * point, - details->mGlyphID)) { - continue; - } - } - - glyph = glyphs.AppendGlyph(); - glyph->mIndex = details->mGlyphID; - glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); - glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); - glyph->mPosition = matInv * glyph->mPosition; - glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode, - isRTL, renderingOptions, aContext, passedInvMatrix, - drawOptions); - - if (IsSyntheticBold()) { - double strikeOffset = synBoldOnePixelOffset; - int32_t strikeCount = strikes; - do { - Glyph *doubleglyph; - doubleglyph = glyphs.AppendGlyph(); - doubleglyph->mIndex = glyph->mIndex; - doubleglyph->mPosition.x = - ToDeviceUnits(glyphX + strikeOffset * - appUnitsPerDevUnit, - devUnitsPerAppUnit); - doubleglyph->mPosition.y = glyph->mPosition.y; - strikeOffset += synBoldOnePixelOffset; - doubleglyph->mPosition = matInv * doubleglyph->mPosition; - glyphs.Flush(dt, aContextPaint, scaledFont, - aDrawMode, isRTL, renderingOptions, - aContext, passedInvMatrix, drawOptions); - } while (--strikeCount > 0); - } - emittedGlyphs = true; - } - } - } - } - - if (aSpacing) { - double space = aSpacing[i - aStart].mAfter; - if (i + 1 < aEnd) { - space += aSpacing[i + 1 - aStart].mBefore; - } - x += direction*space; - } - } - - glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode, isRTL, - renderingOptions, aContext, passedInvMatrix, - drawOptions, true); - if (aCallbacks && emittedGlyphs) { - aCallbacks->NotifyGlyphPathEmitted(); - } - - dt->SetTransform(oldMat); - - dt->SetPermitSubpixelAA(oldSubpixelAA); - } - - *aPt = gfxPoint(x, origY); + // If this matrix applies a skew, which can happen when drawing + // oblique fonts, we will set the DrawTarget matrix to apply the + // skew. We'll need to move the glyphs by the inverse of the skew to + // get the glyphs positioned correctly in the new device space + // though, since the font matrix should only be applied to drawing + // the glyphs, and not to their position. + mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix)); + + mat._11 = mat._22 = 1.0; + mat._21 /= GetAdjustedSize(); + + aRunParams.dt->SetTransform(mat * oldMat); + + fontParams.matInv = mat; + fontParams.matInv.Invert(); + + fontParams.passedInvMatrix = &fontParams.matInv; + } + } + + double origY = aPt->y; + if (mStyle.baselineOffset != 0.0) { + aPt->y += mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit(); + } + + bool emittedGlyphs = + DrawGlyphs(aTextRun, aStart, aEnd - aStart, aPt, + aRunParams, fontParams); + + aPt->y = origY; + + if (aRunParams.callbacks && emittedGlyphs) { + aRunParams.callbacks->NotifyGlyphPathEmitted(); + } + + aRunParams.dt->SetTransform(oldMat); + aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA); } bool gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, - uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) + uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const { if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) { return false; } const gfxFloat devUnitsPerSVGUnit = GetAdjustedSize() / GetFontEntry()->UnitsPerEm(); gfxContextMatrixAutoSaveRestore matrixRestore(aContext); @@ -3635,17 +3547,17 @@ gfxFont::RenderSVGGlyph(gfxContext *aCon return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, int(aDrawMode), aContextPaint); } bool gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, uint32_t aGlyphId, gfxTextContextPaint *aContextPaint, gfxTextRunDrawCallbacks *aCallbacks, - bool& aEmittedGlyphs) + bool& aEmittedGlyphs) const { if (aCallbacks) { if (aEmittedGlyphs) { aCallbacks->NotifyGlyphPathEmitted(); aEmittedGlyphs = false; } aCallbacks->NotifyBeforeSVGGlyphPainted(); } @@ -3653,56 +3565,22 @@ gfxFont::RenderSVGGlyph(gfxContext *aCon aContextPaint); if (aCallbacks) { aCallbacks->NotifyAfterSVGGlyphPainted(); } return rendered; } bool -gfxFont::RenderColorGlyph(gfxContext* aContext, gfxPoint& point, - uint32_t aGlyphId) -{ - nsAutoTArray<uint16_t, 8> layerGlyphs; - nsAutoTArray<mozilla::gfx::Color, 8> layerColors; - - if (!GetFontEntry()->GetColorLayersInfo(aGlyphId, layerGlyphs, layerColors)) { - return false; - } - - cairo_t* cr = aContext->GetCairo(); - cairo_save(cr); - for (uint32_t layerIndex = 0; layerIndex < layerGlyphs.Length(); - layerIndex++) { - - cairo_glyph_t glyph; - glyph.index = layerGlyphs[layerIndex]; - glyph.x = point.x; - glyph.y = point.y; - - mozilla::gfx::Color &color = layerColors[layerIndex]; - cairo_pattern_t* pattern = - cairo_pattern_create_rgba(color.r, color.g, color.b, color.a); - - cairo_set_source(cr, pattern); - cairo_show_glyphs(cr, &glyph, 1); - cairo_pattern_destroy(pattern); - } - cairo_restore(cr); - - return true; -} - -bool gfxFont::RenderColorGlyph(gfxContext* aContext, mozilla::gfx::ScaledFont* scaledFont, GlyphRenderingOptions* aRenderingOptions, mozilla::gfx::DrawOptions aDrawOptions, const mozilla::gfx::Point& aPoint, - uint32_t aGlyphId) + uint32_t aGlyphId) const { nsAutoTArray<uint16_t, 8> layerGlyphs; nsAutoTArray<mozilla::gfx::Color, 8> layerColors; if (!GetFontEntry()->GetColorLayersInfo(aGlyphId, layerGlyphs, layerColors)) { return false; } @@ -7034,30 +6912,26 @@ gfxTextRun::ShrinkToLigatureBoundaries(u if (*aEnd < GetLength()) { while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) { --(*aEnd); } } } void -gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext, - DrawMode aDrawMode, gfxPoint *aPt, - gfxTextContextPaint *aContextPaint, - uint32_t aStart, uint32_t aEnd, - PropertyProvider *aProvider, +gfxTextRun::DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, + gfxPoint *aPt, PropertyProvider *aProvider, uint32_t aSpacingStart, uint32_t aSpacingEnd, - gfxTextRunDrawCallbacks *aCallbacks) + TextRunDrawParams& aParams) { nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer; bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider, aSpacingStart, aSpacingEnd, &spacingBuffer); - aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt, - haveSpacing ? spacingBuffer.Elements() : nullptr, aContextPaint, - aCallbacks); + aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr; + aFont->Draw(this, aStart, aEnd, aPt, aParams); } static void ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight, gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature) { if (aLigature->mClipBeforePart) { if (aTextRun->IsRightToLeft()) { @@ -7072,59 +6946,54 @@ ClipPartialLigature(gfxTextRun *aTextRun *aLeft = std::max(*aLeft, endEdge); } else { *aRight = std::min(*aRight, endEdge); } } } void -gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, - uint32_t aStart, uint32_t aEnd, - gfxPoint *aPt, - PropertyProvider *aProvider, - gfxTextRunDrawCallbacks *aCallbacks) +gfxTextRun::DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, + gfxPoint *aPt, PropertyProvider *aProvider, + TextRunDrawParams& aParams) { if (aStart >= aEnd) return; // Draw partial ligature. We hack this by clipping the ligature. LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider); - gfxRect clipExtents = aCtx->GetClipExtents(); - gfxFloat left = clipExtents.X()*mAppUnitsPerDevUnit; - gfxFloat right = clipExtents.XMost()*mAppUnitsPerDevUnit; + gfxRect clipExtents = aParams.context->GetClipExtents(); + gfxFloat left = clipExtents.X() * mAppUnitsPerDevUnit; + gfxFloat right = clipExtents.XMost() * mAppUnitsPerDevUnit; ClipPartialLigature(this, &left, &right, aPt->x, &data); { // Need to preserve the path, otherwise this can break canvas text-on-path; // in general it seems like a good thing, as naive callers probably won't // expect gfxTextRun::Draw to implicitly destroy the current path. - gfxContextPathAutoSaveRestore savePath(aCtx); + gfxContextPathAutoSaveRestore savePath(aParams.context); // use division here to ensure that when the rect is aligned on multiples // of mAppUnitsPerDevUnit, we clip to true device unit boundaries. // Also, make sure we snap the rectangle to device pixels. - aCtx->Save(); - aCtx->NewPath(); - aCtx->Rectangle(gfxRect(left / mAppUnitsPerDevUnit, - clipExtents.Y(), - (right - left) / mAppUnitsPerDevUnit, - clipExtents.Height()), true); - aCtx->Clip(); - } - - gfxFloat direction = GetDirection(); - gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y); - DrawGlyphs(aFont, aCtx, - aCallbacks ? DrawMode::GLYPH_PATH : DrawMode::GLYPH_FILL, &pt, - nullptr, data.mLigatureStart, data.mLigatureEnd, aProvider, - aStart, aEnd, aCallbacks); - aCtx->Restore(); - - aPt->x += direction*data.mPartWidth; + aParams.context->Save(); + aParams.context->NewPath(); + aParams.context->Rectangle(gfxRect(left / mAppUnitsPerDevUnit, + clipExtents.Y(), + (right - left) / mAppUnitsPerDevUnit, + clipExtents.Height()), true); + aParams.context->Clip(); + } + + gfxPoint pt(aPt->x - aParams.direction * data.mPartAdvance, aPt->y); + DrawGlyphs(aFont, data.mLigatureStart, data.mLigatureEnd, &pt, + aProvider, aStart, aEnd, aParams); + aParams.context->Restore(); + + aPt->x += aParams.direction * data.mPartWidth; } // returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise static bool HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength) { gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength); while (iter.NextRun()) { @@ -7191,19 +7060,21 @@ struct BufferAlphaColor { void gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode, uint32_t aStart, uint32_t aLength, PropertyProvider *aProvider, gfxFloat *aAdvanceWidth, gfxTextContextPaint *aContextPaint, gfxTextRunDrawCallbacks *aCallbacks) { NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range"); - NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)), + NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || + !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)), "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH"); - NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH"); + NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks, + "callback must not be specified unless using GLYPH_PATH"); bool skipDrawing = mSkipDrawing; if (aDrawMode == DrawMode::GLYPH_FILL) { gfxRGBA currentColor; if (aContext->GetDeviceColor(currentColor) && currentColor.a == 0) { skipDrawing = true; } } @@ -7219,57 +7090,73 @@ gfxTextRun::Draw(gfxContext *aContext, g aContext, aProvider); *aAdvanceWidth = metrics.mAdvanceWidth * direction; } // return without drawing return; } + // Set up parameters that will be constant across all glyph runs we need + // to draw, regardless of the font used. + TextRunDrawParams params; + params.context = aContext; + params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit()); + params.isRTL = IsRightToLeft(); + params.direction = direction; + params.drawMode = aDrawMode; + params.callbacks = aCallbacks; + params.contextPaint = aContextPaint; + params.paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs; + params.dt = aContext->GetDrawTarget(); + gfxPoint pt = aPt; - // synthetic bolding draws glyphs twice ==> colors with opacity won't draw correctly unless first drawn without alpha + // synthetic bolding draws glyphs twice ==> colors with opacity won't draw + // correctly unless first drawn without alpha BufferAlphaColor syntheticBoldBuffer(aContext); gfxRGBA currentColor; bool needToRestore = false; - if (aDrawMode == DrawMode::GLYPH_FILL && HasNonOpaqueColor(aContext, currentColor) - && HasSyntheticBold(this, aStart, aLength)) { + if (aDrawMode == DrawMode::GLYPH_FILL && + HasNonOpaqueColor(aContext, currentColor) && + HasSyntheticBold(this, aStart, aLength)) { needToRestore = true; // measure text, use the bounding box - gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS, + gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, + gfxFont::LOOSE_INK_EXTENTS, aContext, aProvider); metrics.mBoundingBox.MoveBy(aPt); - syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit()); + syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, + GetAppUnitsPerDevUnit()); } GlyphRunIterator iter(this, aStart, aLength); while (iter.NextRun()) { gfxFont *font = iter.GetGlyphRun()->mFont; uint32_t start = iter.GetStringStart(); uint32_t end = iter.GetStringEnd(); uint32_t ligatureRunStart = start; uint32_t ligatureRunEnd = end; ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd); - + bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL || (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks); if (drawPartial) { - DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt, - aProvider, aCallbacks); - } - - DrawGlyphs(font, aContext, aDrawMode, &pt, aContextPaint, ligatureRunStart, - ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd, - aCallbacks); + DrawPartialLigature(font, start, ligatureRunStart, &pt, + aProvider, params); + } + + DrawGlyphs(font, ligatureRunStart, ligatureRunEnd, &pt, + aProvider, ligatureRunStart, ligatureRunEnd, params); if (drawPartial) { - DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt, - aProvider, aCallbacks); + DrawPartialLigature(font, ligatureRunEnd, end, &pt, + aProvider, params); } } // composite result when synthetic bolding used if (needToRestore) { syntheticBoldBuffer.PopAlpha(); }
--- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1522,17 +1522,21 @@ public: bool aAddSmallCaps, nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures); protected: // the font this shaper is working with gfxFont * mFont; }; -/* a SPECIFIC single font family */ + +class GlyphBufferAzure; +struct TextRunDrawParams; +struct FontDrawParams; + class gfxFont { friend class gfxHarfBuzzShaper; friend class gfxGraphiteShaper; public: nsrefcnt AddRef(void) { NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt"); @@ -1624,17 +1628,17 @@ public: virtual cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; } virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) { // platforms where this actually matters should override return nullptr; } - virtual gfxFloat GetAdjustedSize() { + virtual gfxFloat GetAdjustedSize() const { return mAdjustedSize > 0.0 ? mAdjustedSize : mStyle.size; } float FUnitsToDevUnitsFactor() const { // check this was set up during font initialization NS_ASSERTION(mFUnitsConvFactor > 0.0f, "mFUnitsConvFactor not valid"); return mFUnitsConvFactor; } @@ -1759,41 +1763,40 @@ public: gfxRect mBoundingBox; }; /** * Draw a series of glyphs to aContext. The direction of aTextRun must * be honoured. * @param aStart the first character to draw * @param aEnd draw characters up to here - * @param aBaselineOrigin the baseline origin; the left end of the baseline - * for LTR textruns, the right end of the baseline for RTL textruns. On return, - * this should be updated to the other end of the baseline. In application - * units, really! - * @param aSpacing spacing to insert before and after characters (for RTL - * glyphs, before-spacing is inserted to the right of characters). There - * are aEnd - aStart elements in this array, unless it's null to indicate - * that there is no spacing. - * @param aDrawMode specifies whether the fill or stroke of the glyph should be - * drawn, or if it should be drawn into the current path - * @param aContextPaint information about how to construct the fill and - * stroke pattern. Can be nullptr if we are not stroking the text, which - * indicates that the current source from aContext should be used for filling - * + * @param aPt the baseline origin; the left end of the baseline + * for LTR textruns, the right end for RTL textruns. + * On return, this will be updated to the other end of the baseline. + * In application units, really! + * @param aParams record with drawing parameters, see TextRunDrawParams. + * Particular fields of interest include + * .spacing spacing to insert before and after characters (for RTL + * glyphs, before-spacing is inserted to the right of characters). There + * are aEnd - aStart elements in this array, unless it's null to indicate + * that there is no spacing. + * .drawMode specifies whether the fill or stroke of the glyph should be + * drawn, or if it should be drawn into the current path + * .contextPaint information about how to construct the fill and + * stroke pattern. Can be nullptr if we are not stroking the text, which + * indicates that the current source from context should be used for fill + * .context the Thebes graphics context to which we're drawing + * .dt Moz2D DrawTarget to which we're drawing + * * Callers guarantee: * -- aStart and aEnd are aligned to cluster and ligature boundaries * -- all glyphs use this font - * - * The default implementation builds a cairo glyph array and - * calls cairo_show_glyphs or cairo_glyph_path. */ - virtual void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, - gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aBaselineOrigin, - Spacing *aSpacing, gfxTextContextPaint *aContextPaint, - gfxTextRunDrawCallbacks *aCallbacks); + void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, + gfxPoint *aPt, TextRunDrawParams& aParams); /** * Measure a run of characters. See gfxTextRun::Metrics. * @param aTight if false, then return the union of the glyph extents * with the font-box for the characters (the rectangle with x=0,width= * the advance width for the character run,y=-(font ascent), and height= * font ascent + font descent). Otherwise, we must return as tight as possible * an approximation to the area actually painted by glyphs. @@ -1850,17 +1853,17 @@ public: // simply use (S / T). gfxFloat GetSyntheticBoldOffset() { gfxFloat size = GetAdjustedSize(); const gfxFloat threshold = 48.0; return size < threshold ? (0.25 + 0.75 * size / threshold) : (size / threshold); } - gfxFontEntry *GetFontEntry() { return mFontEntry.get(); } + gfxFontEntry *GetFontEntry() const { return mFontEntry.get(); } bool HasCharacter(uint32_t ch) { if (!mIsValid) return false; return mFontEntry->HasCharacter(ch); } uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS) { if (!mIsValid) { @@ -2014,16 +2017,35 @@ public: return mFontEntry->GetMathConstant(aConstant); } // return a cloned font resized and offset to simulate sub/superscript glyphs virtual already_AddRefed<gfxFont> GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel); protected: + // Output a single glyph at *aPt, which is updated by the glyph's advance. + // Normal glyphs are simply accumulated in aBuffer until it is full and + // gets flushed, but SVG or color-font glyphs will instead be rendered + // directly to the destination (found from the buffer's parameters). + void DrawOneGlyph(uint32_t aGlyphID, + double aAdvance, + gfxPoint *aPt, + GlyphBufferAzure& aBuffer, + bool *aEmittedGlyphs) const; + + // Output a run of glyphs at *aPt, which is updated to follow the last glyph + // in the run. This method also takes account of any letter-spacing provided + // in aRunParams. + bool DrawGlyphs(gfxShapedText *aShapedText, + uint32_t aOffset, // offset in the textrun + uint32_t aCount, // length of run to draw + gfxPoint *aPt, + const TextRunDrawParams& aRunParams, + const FontDrawParams& aFontParams); // set the font size and offset used for // synthetic subscript/superscript glyphs void CalculateSubSuperSizeAndOffset(int32_t aAppUnitsPerDevPixel, gfxFloat& aSubSuperSizeRatio, float& aBaselineOffset); // Return a font that is a "clone" of this one, but reduced to 80% size @@ -2251,29 +2273,28 @@ protected: // InitMetricsFromSfntTables or equivalent platform code void CalculateDerivedMetrics(Metrics& aMetrics); // some fonts have bad metrics, this method sanitize them. // if this font has bad underline offset, aIsBadUnderlineFont should be true. void SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont); bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, - uint32_t aGlyphId, gfxTextContextPaint *aContextPaint); + uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const; bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, uint32_t aGlyphId, gfxTextContextPaint *aContextPaint, gfxTextRunDrawCallbacks *aCallbacks, - bool& aEmittedGlyphs); - - bool RenderColorGlyph(gfxContext* aContext, gfxPoint& point, uint32_t aGlyphId); + bool& aEmittedGlyphs) const; + bool RenderColorGlyph(gfxContext* aContext, mozilla::gfx::ScaledFont* scaledFont, mozilla::gfx::GlyphRenderingOptions* renderingOptions, mozilla::gfx::DrawOptions drawOptions, const mozilla::gfx::Point& aPoint, - uint32_t aGlyphId); + uint32_t aGlyphId) const; // Bug 674909. When synthetic bolding text by drawing twice, need to // render using a pixel offset in device pixels, otherwise text // doesn't appear bolded, it appears as if a bad text shadow exists // when a non-identity transform exists. Use an offset factor so that // the second draw occurs at a constant offset in device pixels. // This helper calculates the scale factor we need to apply to the // synthetic-bold offset. @@ -3533,20 +3554,19 @@ private: // (Platforms do the actual ligaturization, but we need to do a bunch of stuff // to handle requests that begin or end inside a ligature) // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero LigatureData ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd, PropertyProvider *aProvider); gfxFloat ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd, PropertyProvider *aProvider); - void DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, - uint32_t aStart, uint32_t aEnd, gfxPoint *aPt, - PropertyProvider *aProvider, - gfxTextRunDrawCallbacks *aCallbacks); + void DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, + gfxPoint *aPt, PropertyProvider *aProvider, + TextRunDrawParams& aParams); // Advance aStart to the start of the nearest ligature; back up aEnd // to the nearest ligature end; may result in *aStart == *aEnd void ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd); // result in appunits gfxFloat GetPartialLigatureWidth(uint32_t aStart, uint32_t aEnd, PropertyProvider *aProvider); void AccumulatePartialLigatureMetrics(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, gfxFont::BoundingBoxType aBoundingBoxType, @@ -3558,22 +3578,20 @@ private: void AccumulateMetricsForRun(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext, PropertyProvider *aProvider, uint32_t aSpacingStart, uint32_t aSpacingEnd, Metrics *aMetrics); // **** drawing helper **** - void DrawGlyphs(gfxFont *aFont, gfxContext *aContext, - DrawMode aDrawMode, gfxPoint *aPt, - gfxTextContextPaint *aContextPaint, uint32_t aStart, - uint32_t aEnd, PropertyProvider *aProvider, + void DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, + gfxPoint *aPt, PropertyProvider *aProvider, uint32_t aSpacingStart, uint32_t aSpacingEnd, - gfxTextRunDrawCallbacks *aCallbacks); + TextRunDrawParams& aParams); // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*, // for smaller size especially in the super-common one-glyphrun case nsAutoTArray<GlyphRun,1> mGlyphRuns; void *mUserData; gfxFontGroup *mFontGroup; // addrefed on creation, but our reference // may be released by ReleaseFontGroup()
--- a/intl/build/moz.build +++ b/intl/build/moz.build @@ -5,15 +5,15 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. SOURCES += [ 'nsI18nModule.cpp', ] FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ - '../locale/src', - '../lwbrk/src', - '../strres/src', - '../uconv/src', - '../unicharutil/src', + '../locale', + '../lwbrk', + '../strres', + '../uconv', + '../unicharutil', ]
--- a/intl/chardet/moz.build +++ b/intl/chardet/moz.build @@ -1,7 +1,19 @@ # -*- Mode: python; c-basic-offset: 4; 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/. -DIRS += ['public', 'src'] +EXPORTS += [ + 'nsDetectionConfident.h', + 'nsICharsetDetectionObserver.h', + 'nsICharsetDetector.h', + 'nsIStringCharsetDetector.h', +] + +UNIFIED_SOURCES += [ + 'nsChardetModule.cpp', + 'nsCyrillicDetector.cpp', +] + +FINAL_LIBRARY = 'xul'
rename from intl/chardet/public/nsDetectionConfident.h rename to intl/chardet/nsDetectionConfident.h
rename from intl/chardet/public/nsICharsetDetectionObserver.h rename to intl/chardet/nsICharsetDetectionObserver.h
rename from intl/chardet/public/nsIStringCharsetDetector.h rename to intl/chardet/nsIStringCharsetDetector.h
deleted file mode 100644 --- a/intl/chardet/public/moz.build +++ /dev/null @@ -1,13 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -EXPORTS += [ - 'nsDetectionConfident.h', - 'nsICharsetDetectionObserver.h', - 'nsICharsetDetector.h', - 'nsIStringCharsetDetector.h', -] -
deleted file mode 100644 --- a/intl/chardet/src/moz.build +++ /dev/null @@ -1,12 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -UNIFIED_SOURCES += [ - 'nsChardetModule.cpp', - 'nsCyrillicDetector.cpp', -] - -FINAL_LIBRARY = 'xul'
--- a/intl/hyphenation/moz.build +++ b/intl/hyphenation/moz.build @@ -1,8 +1,32 @@ # -*- Mode: python; c-basic-offset: 4; 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/. -DIRS += ['public', 'src'] +EXPORTS += [ + 'nsHyphenationManager.h', + 'nsHyphenator.h', +] + +UNIFIED_SOURCES += [ + 'nsHyphenationManager.cpp', + 'nsHyphenator.cpp', +] +# These files cannot be built in unified mode because they include hnjalloc.h. +SOURCES += [ + 'hnjstdio.cpp', + 'hyphen.c', +] + +MSVC_ENABLE_PGO = True + +FINAL_LIBRARY = 'xul' + +# Suppress warnings in third-party code. +if CONFIG['GNU_CC']: + CFLAGS += [ + '-Wno-sign-compare', + '-Wno-type-limits', + ]
rename from intl/hyphenation/src/nsHyphenationManager.cpp rename to intl/hyphenation/nsHyphenationManager.cpp
rename from intl/hyphenation/public/nsHyphenationManager.h rename to intl/hyphenation/nsHyphenationManager.h
deleted file mode 100644 --- a/intl/hyphenation/public/moz.build +++ /dev/null @@ -1,11 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -EXPORTS += [ - 'nsHyphenationManager.h', - 'nsHyphenator.h', -] -
deleted file mode 100644 --- a/intl/hyphenation/src/moz.build +++ /dev/null @@ -1,27 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -UNIFIED_SOURCES += [ - 'nsHyphenationManager.cpp', - 'nsHyphenator.cpp', -] - -# These files cannot be built in unified mode because they include hnjalloc.h. -SOURCES += [ - 'hnjstdio.cpp', - 'hyphen.c', -] - -MSVC_ENABLE_PGO = True - -FINAL_LIBRARY = 'xul' - -# Suppress warnings in third-party code. -if CONFIG['GNU_CC']: - CFLAGS += [ - '-Wno-sign-compare', - '-Wno-type-limits', - ]
deleted file mode 100644 --- a/intl/locale/idl/moz.build +++ /dev/null @@ -1,15 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -XPIDL_SOURCES += [ - 'nsICollation.idl', - 'nsILocale.idl', - 'nsILocaleService.idl', - 'nsIScriptableDateFormat.idl', -] - -XPIDL_MODULE = 'locale' -
rename from intl/locale/src/mac/nsDateTimeFormatMac.cpp rename to intl/locale/mac/nsDateTimeFormatMac.cpp
rename from intl/locale/src/mac/nsDateTimeFormatMac.h rename to intl/locale/mac/nsDateTimeFormatMac.h
--- a/intl/locale/moz.build +++ b/intl/locale/moz.build @@ -1,9 +1,62 @@ # -*- Mode: python; c-basic-offset: 4; 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/. -DIRS += ['public', 'idl', 'src'] TEST_DIRS += ['tests'] +toolkit = CONFIG['MOZ_WIDGET_TOOLKIT'] + +if toolkit == 'windows': + DIRS += ['windows'] +elif toolkit == 'cocoa': + DIRS += ['mac'] +else: + DIRS += ['unix'] + +XPIDL_SOURCES += [ + 'nsICollation.idl', + 'nsILocale.idl', + 'nsILocaleService.idl', + 'nsIScriptableDateFormat.idl', +] + +XPIDL_MODULE = 'locale' + +EXPORTS += [ + 'nsCollation.h', + 'nsCollationCID.h', + 'nsDateTimeFormatCID.h', + 'nsIDateTimeFormat.h', + 'nsILanguageAtomService.h', + 'nsIPlatformCharset.h', + 'nsPosixLocale.h', + 'nsWin32Locale.h', +] + +UNIFIED_SOURCES += [ + 'nsCollation.cpp', + 'nsLanguageAtomService.cpp', + 'nsLocale.cpp', + 'nsLocaleService.cpp', + 'nsScriptableDateFormat.cpp', + 'nsUConvPropertySearch.cpp', +] + +EXTRA_JS_MODULES += [ + 'PluralForm.jsm', +] + +MSVC_ENABLE_PGO = True + +FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '/intl/uconv', +] + +RESOURCE_FILES += [ + 'langGroups.properties', + 'language.properties', +]
rename from intl/locale/public/nsILanguageAtomService.h rename to intl/locale/nsILanguageAtomService.h
rename from intl/locale/idl/nsIScriptableDateFormat.idl rename to intl/locale/nsIScriptableDateFormat.idl
rename from intl/locale/src/nsLanguageAtomService.cpp rename to intl/locale/nsLanguageAtomService.cpp
rename from intl/locale/src/nsScriptableDateFormat.cpp rename to intl/locale/nsScriptableDateFormat.cpp
rename from intl/locale/src/nsUConvPropertySearch.cpp rename to intl/locale/nsUConvPropertySearch.cpp
deleted file mode 100644 --- a/intl/locale/public/moz.build +++ /dev/null @@ -1,16 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -EXPORTS += [ - 'nsCollationCID.h', - 'nsDateTimeFormatCID.h', - 'nsIDateTimeFormat.h', - 'nsILanguageAtomService.h', - 'nsIPlatformCharset.h', - 'nsPosixLocale.h', - 'nsWin32Locale.h', -] -
deleted file mode 100644 --- a/intl/locale/src/moz.build +++ /dev/null @@ -1,44 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -toolkit = CONFIG['MOZ_WIDGET_TOOLKIT'] - -if toolkit == 'windows': - DIRS += ['windows'] -elif toolkit == 'cocoa': - DIRS += ['mac'] -else: - DIRS += ['unix'] - -EXPORTS += [ - 'nsCollation.h', -] - -UNIFIED_SOURCES += [ - 'nsCollation.cpp', - 'nsLanguageAtomService.cpp', - 'nsLocale.cpp', - 'nsLocaleService.cpp', - 'nsScriptableDateFormat.cpp', - 'nsUConvPropertySearch.cpp', -] - -EXTRA_JS_MODULES += [ - 'PluralForm.jsm', -] - -MSVC_ENABLE_PGO = True - -FINAL_LIBRARY = 'xul' - -LOCAL_INCLUDES += [ - '/intl/uconv/src', -] - -RESOURCE_FILES += [ - 'langGroups.properties', - 'language.properties', -]
rename from intl/locale/src/unix/nsAndroidCharset.cpp rename to intl/locale/unix/nsAndroidCharset.cpp
rename from intl/locale/src/unix/nsDateTimeFormatUnix.cpp rename to intl/locale/unix/nsDateTimeFormatUnix.cpp
rename from intl/locale/src/unix/nsDateTimeFormatUnix.h rename to intl/locale/unix/nsDateTimeFormatUnix.h
rename from intl/locale/src/unix/unixcharset.properties rename to intl/locale/unix/unixcharset.properties
rename from intl/locale/src/windows/nsCollationWin.cpp rename to intl/locale/windows/nsCollationWin.cpp
rename from intl/locale/src/windows/nsDateTimeFormatWin.cpp rename to intl/locale/windows/nsDateTimeFormatWin.cpp
rename from intl/locale/src/windows/nsDateTimeFormatWin.h rename to intl/locale/windows/nsDateTimeFormatWin.h
rename from intl/locale/src/windows/nsWin32Locale.cpp rename to intl/locale/windows/nsWin32Locale.cpp
rename from intl/locale/src/windows/wincharset.properties rename to intl/locale/windows/wincharset.properties
rename from intl/lwbrk/src/crashtests/crashtests.list rename to intl/lwbrk/crashtests/crashtests.list
deleted file mode 100644 --- a/intl/lwbrk/idl/moz.build +++ /dev/null @@ -1,12 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -XPIDL_SOURCES += [ - 'nsISemanticUnitScanner.idl', -] - -XPIDL_MODULE = 'lwbrk' -
--- a/intl/lwbrk/moz.build +++ b/intl/lwbrk/moz.build @@ -1,9 +1,49 @@ # -*- Mode: python; c-basic-offset: 4; 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/. -DIRS += ['idl', 'public', 'src'] TEST_TOOL_DIRS += ['tests'] +XPIDL_SOURCES += [ + 'nsISemanticUnitScanner.idl', +] + +XPIDL_MODULE = 'lwbrk' + +EXPORTS += [ + 'nsILineBreaker.h', + 'nsIWordBreaker.h', + 'nsLWBrkCIID.h', +] + +UNIFIED_SOURCES += [ + 'nsJISx4051LineBreaker.cpp', + 'nsSampleWordBreaker.cpp', + 'nsSemanticUnitScanner.cpp', +] + +if CONFIG['MOZ_WIDGET_GTK']: + SOURCES += [ + 'nsPangoBreaker.cpp', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': + SOURCES += [ + 'nsUniscribeBreaker.cpp', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + UNIFIED_SOURCES += [ + 'nsCarbonBreaker.cpp', + ] +else: + SOURCES += [ + 'nsRuleBreaker.cpp', + ] + SOURCES += [ + 'rulebrk.c', + ] + +MSVC_ENABLE_PGO = True + +FINAL_LIBRARY = 'xul'
rename from intl/lwbrk/idl/nsISemanticUnitScanner.idl rename to intl/lwbrk/nsISemanticUnitScanner.idl
deleted file mode 100644 --- a/intl/lwbrk/public/moz.build +++ /dev/null @@ -1,12 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -EXPORTS += [ - 'nsILineBreaker.h', - 'nsIWordBreaker.h', - 'nsLWBrkCIID.h', -] -
deleted file mode 100644 --- a/intl/lwbrk/src/moz.build +++ /dev/null @@ -1,37 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -UNIFIED_SOURCES += [ - 'nsJISx4051LineBreaker.cpp', - 'nsSampleWordBreaker.cpp', - 'nsSemanticUnitScanner.cpp', -] - -if CONFIG['MOZ_WIDGET_GTK']: - SOURCES += [ - 'nsPangoBreaker.cpp', - ] -elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': - SOURCES += [ - 'nsUniscribeBreaker.cpp', - ] -elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': - UNIFIED_SOURCES += [ - 'nsCarbonBreaker.cpp', - ] -else: - SOURCES += [ - 'nsRuleBreaker.cpp', - ] - SOURCES += [ - 'rulebrk.c', - ] - - - -MSVC_ENABLE_PGO = True - -FINAL_LIBRARY = 'xul'
--- a/intl/strres/moz.build +++ b/intl/strres/moz.build @@ -1,9 +1,23 @@ # -*- Mode: python; c-basic-offset: 4; 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/. -DIRS += ['public', 'src'] TEST_TOOL_DIRS += ['tests'] +XPIDL_SOURCES += [ + 'nsIStringBundle.idl', + 'nsIStringBundleOverride.idl', +] + +XPIDL_MODULE = 'intl' + +UNIFIED_SOURCES += [ + 'nsStringBundle.cpp', + 'nsStringBundleTextOverride.cpp', +] + +MSVC_ENABLE_PGO = True + +FINAL_LIBRARY = 'xul'
rename from intl/strres/public/nsIStringBundleOverride.idl rename to intl/strres/nsIStringBundleOverride.idl
rename from intl/strres/src/nsStringBundleTextOverride.cpp rename to intl/strres/nsStringBundleTextOverride.cpp
rename from intl/strres/src/nsStringBundleTextOverride.h rename to intl/strres/nsStringBundleTextOverride.h
deleted file mode 100644 --- a/intl/strres/public/moz.build +++ /dev/null @@ -1,13 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -XPIDL_SOURCES += [ - 'nsIStringBundle.idl', - 'nsIStringBundleOverride.idl', -] - -XPIDL_MODULE = 'intl' -
deleted file mode 100644 --- a/intl/strres/src/moz.build +++ /dev/null @@ -1,14 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -UNIFIED_SOURCES += [ - 'nsStringBundle.cpp', - 'nsStringBundleTextOverride.cpp', -] - -MSVC_ENABLE_PGO = True - -FINAL_LIBRARY = 'xul'
deleted file mode 100644 --- a/intl/uconv/idl/moz.build +++ /dev/null @@ -1,15 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; 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/. - -XPIDL_SOURCES += [ - 'nsICurrentCharsetListener.idl', - 'nsIScriptableUConv.idl', - 'nsITextToSubURI.idl', - 'nsIUTF8ConverterService.idl', -] - -XPIDL_MODULE = 'uconv' -
--- a/intl/uconv/moz.build +++ b/intl/uconv/moz.build @@ -1,20 +1,226 @@ # -*- Mode: python; c-basic-offset: 4; 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/. DIRS += [ - 'idl', - 'public', 'ucvja', 'ucvcn', 'ucvlatin', 'ucvtw', 'ucvko', 'ucvibm', - 'src', ] TEST_TOOL_DIRS += ['tests'] +XPIDL_SOURCES += [ + 'nsICurrentCharsetListener.idl', + 'nsIScriptableUConv.idl', + 'nsITextToSubURI.idl', + 'nsIUTF8ConverterService.idl', +] + +XPIDL_MODULE = 'uconv' + +EXPORTS += [ + 'nsEncoderDecoderUtils.h', + 'nsIUnicodeDecoder.h', + 'nsIUnicodeEncoder.h', + 'nsUConvCID.h', + 'nsUCSupport.h', + 'uconvutil.h', +] + +UNIFIED_SOURCES += [ + 'nsConverterInputStream.cpp', + 'nsConverterOutputStream.cpp', + 'nsCP1252ToUnicode.cpp', + 'nsISO88591ToUnicode.cpp', + 'nsMacRomanToUnicode.cpp', + 'nsReplacementToUnicode.cpp', + 'nsScriptableUConv.cpp', + 'nsTextToSubURI.cpp', + 'nsUConvModule.cpp', + 'nsUnicodeToCP1252.cpp', + 'nsUnicodeToISO88591.cpp', + 'nsUnicodeToMacRoman.cpp', + 'nsUnicodeToUTF8.cpp', + 'nsUTF8ConverterService.cpp', + 'nsUTF8ToUnicode.cpp', +] + +UNIFIED_SOURCES += [ + 'ucvcn/nsGBKConvUtil.cpp', + 'ucvcn/nsGBKToUnicode.cpp', + 'ucvcn/nsHZToUnicode.cpp', + 'ucvcn/nsUnicodeToGB2312V2.cpp', + 'ucvcn/nsUnicodeToGBK.cpp', + 'ucvcn/nsUnicodeToHZ.cpp', +] + +UNIFIED_SOURCES += [ + 'ucvibm/nsCP850ToUnicode.cpp', + 'ucvibm/nsCP852ToUnicode.cpp', + 'ucvibm/nsCP855ToUnicode.cpp', + 'ucvibm/nsCP857ToUnicode.cpp', + 'ucvibm/nsCP862ToUnicode.cpp', + 'ucvibm/nsCP864ToUnicode.cpp', + 'ucvibm/nsUnicodeToCP850.cpp', + 'ucvibm/nsUnicodeToCP852.cpp', + 'ucvibm/nsUnicodeToCP855.cpp', + 'ucvibm/nsUnicodeToCP857.cpp', + 'ucvibm/nsUnicodeToCP862.cpp', + 'ucvibm/nsUnicodeToCP864.cpp', +] + +UNIFIED_SOURCES += [ + 'ucvja/nsJapaneseToUnicode.cpp', + 'ucvja/nsUnicodeToEUCJP.cpp', + 'ucvja/nsUnicodeToISO2022JP.cpp', + 'ucvja/nsUnicodeToJISx0201.cpp', + 'ucvja/nsUnicodeToSJIS.cpp', +] + +UNIFIED_SOURCES += [ + 'ucvko/nsCP949ToUnicode.cpp', + 'ucvko/nsUnicodeToCP949.cpp', +] + +UNIFIED_SOURCES += [ + 'ucvlatin/nsARMSCII8ToUnicode.cpp', + 'ucvlatin/nsAsciiToUnicode.cpp', + 'ucvlatin/nsCP1250ToUnicode.cpp', + 'ucvlatin/nsCP1251ToUnicode.cpp', + 'ucvlatin/nsCP1253ToUnicode.cpp', + 'ucvlatin/nsCP1254ToUnicode.cpp', + 'ucvlatin/nsCP1255ToUnicode.cpp', + 'ucvlatin/nsCP1256ToUnicode.cpp', + 'ucvlatin/nsCP1257ToUnicode.cpp', + 'ucvlatin/nsCP1258ToUnicode.cpp', + 'ucvlatin/nsCP866ToUnicode.cpp', + 'ucvlatin/nsCP874ToUnicode.cpp', + 'ucvlatin/nsISO885910ToUnicode.cpp', + 'ucvlatin/nsISO885911ToUnicode.cpp', + 'ucvlatin/nsISO885913ToUnicode.cpp', + 'ucvlatin/nsISO885914ToUnicode.cpp', + 'ucvlatin/nsISO885915ToUnicode.cpp', + 'ucvlatin/nsISO885916ToUnicode.cpp', + 'ucvlatin/nsISO88592ToUnicode.cpp', + 'ucvlatin/nsISO88593ToUnicode.cpp', + 'ucvlatin/nsISO88594ToUnicode.cpp', + 'ucvlatin/nsISO88595ToUnicode.cpp', + 'ucvlatin/nsISO88596EToUnicode.cpp', + 'ucvlatin/nsISO88596IToUnicode.cpp', + 'ucvlatin/nsISO88596ToUnicode.cpp', + 'ucvlatin/nsISO88597ToUnicode.cpp', + 'ucvlatin/nsISO88598EToUnicode.cpp', + 'ucvlatin/nsISO88598IToUnicode.cpp', + 'ucvlatin/nsISO88598ToUnicode.cpp', + 'ucvlatin/nsISO88599ToUnicode.cpp', + 'ucvlatin/nsISOIR111ToUnicode.cpp', + 'ucvlatin/nsKOI8RToUnicode.cpp', + 'ucvlatin/nsKOI8UToUnicode.cpp', + 'ucvlatin/nsMacArabicToUnicode.cpp', + 'ucvlatin/nsMacCEToUnicode.cpp', + 'ucvlatin/nsMacCroatianToUnicode.cpp', + 'ucvlatin/nsMacCyrillicToUnicode.cpp', + 'ucvlatin/nsMacDevanagariToUnicode.cpp', + 'ucvlatin/nsMacFarsiToUnicode.cpp', + 'ucvlatin/nsMacGreekToUnicode.cpp', + 'ucvlatin/nsMacGujaratiToUnicode.cpp', + 'ucvlatin/nsMacGurmukhiToUnicode.cpp', + 'ucvlatin/nsMacHebrewToUnicode.cpp', + 'ucvlatin/nsMacIcelandicToUnicode.cpp', + 'ucvlatin/nsMacRomanianToUnicode.cpp', + 'ucvlatin/nsMacTurkishToUnicode.cpp', + 'ucvlatin/nsTIS620ToUnicode.cpp', + 'ucvlatin/nsUnicodeToARMSCII8.cpp', + 'ucvlatin/nsUnicodeToAscii.cpp', + 'ucvlatin/nsUnicodeToCP1250.cpp', + 'ucvlatin/nsUnicodeToCP1251.cpp', + 'ucvlatin/nsUnicodeToCP1253.cpp', + 'ucvlatin/nsUnicodeToCP1254.cpp', + 'ucvlatin/nsUnicodeToCP1255.cpp', + 'ucvlatin/nsUnicodeToCP1256.cpp', + 'ucvlatin/nsUnicodeToCP1257.cpp', + 'ucvlatin/nsUnicodeToCP1258.cpp', + 'ucvlatin/nsUnicodeToCP866.cpp', + 'ucvlatin/nsUnicodeToCP874.cpp', + 'ucvlatin/nsUnicodeToISO885910.cpp', + 'ucvlatin/nsUnicodeToISO885911.cpp', + 'ucvlatin/nsUnicodeToISO885913.cpp', + 'ucvlatin/nsUnicodeToISO885914.cpp', + 'ucvlatin/nsUnicodeToISO885915.cpp', + 'ucvlatin/nsUnicodeToISO885916.cpp', + 'ucvlatin/nsUnicodeToISO88592.cpp', + 'ucvlatin/nsUnicodeToISO88593.cpp', + 'ucvlatin/nsUnicodeToISO88594.cpp', + 'ucvlatin/nsUnicodeToISO88595.cpp', + 'ucvlatin/nsUnicodeToISO88596.cpp', + 'ucvlatin/nsUnicodeToISO88596E.cpp', + 'ucvlatin/nsUnicodeToISO88596I.cpp', + 'ucvlatin/nsUnicodeToISO88597.cpp', + 'ucvlatin/nsUnicodeToISO88598.cpp', + 'ucvlatin/nsUnicodeToISO88598E.cpp', + 'ucvlatin/nsUnicodeToISO88598I.cpp', + 'ucvlatin/nsUnicodeToISO88599.cpp', + 'ucvlatin/nsUnicodeToISOIR111.cpp', + 'ucvlatin/nsUnicodeToKOI8R.cpp', + 'ucvlatin/nsUnicodeToKOI8U.cpp', + 'ucvlatin/nsUnicodeToMacArabic.cpp', + 'ucvlatin/nsUnicodeToMacCE.cpp', + 'ucvlatin/nsUnicodeToMacCroatian.cpp', + 'ucvlatin/nsUnicodeToMacCyrillic.cpp', + 'ucvlatin/nsUnicodeToMacDevanagari.cpp', + 'ucvlatin/nsUnicodeToMacFarsi.cpp', + 'ucvlatin/nsUnicodeToMacGreek.cpp', + 'ucvlatin/nsUnicodeToMacGujarati.cpp', + 'ucvlatin/nsUnicodeToMacGurmukhi.cpp', + 'ucvlatin/nsUnicodeToMacHebrew.cpp', + 'ucvlatin/nsUnicodeToMacIcelandic.cpp', + 'ucvlatin/nsUnicodeToMacRomanian.cpp', + 'ucvlatin/nsUnicodeToMacTurkish.cpp', + 'ucvlatin/nsUnicodeToTIS620.cpp', + 'ucvlatin/nsUnicodeToUserDefined.cpp', + 'ucvlatin/nsUnicodeToUTF16.cpp', + 'ucvlatin/nsUserDefinedToUnicode.cpp', + 'ucvlatin/nsUTF16ToUnicode.cpp', +] + +UNIFIED_SOURCES += [ + 'ucvtw/nsBIG5HKSCSToUnicode.cpp', + 'ucvtw/nsBIG5ToUnicode.cpp', + 'ucvtw/nsUnicodeToBIG5.cpp', + 'ucvtw/nsUnicodeToBIG5HKSCS.cpp', + 'ucvtw/nsUnicodeToHKSCS.cpp', +] + +UNIFIED_SOURCES += [ + 'util/nsUCConstructors.cpp', + 'util/nsUCSupport.cpp', + 'util/nsUnicodeDecodeHelper.cpp', + 'util/nsUnicodeEncodeHelper.cpp', + 'util/ugen.c', + 'util/umap.c', + 'util/uscan.c', +] + +if CONFIG['INTEL_ARCHITECTURE']: + SOURCES += ['nsUTF8ToUnicodeSSE2.cpp'] + SOURCES['nsUTF8ToUnicodeSSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] + +MSVC_ENABLE_PGO = True + +LOCAL_INCLUDES += [ + 'ucvcn', + 'ucvibm', + 'ucvja', + 'ucvko', + 'ucvlatin', + 'ucvtw', + 'util', +] + +FINAL_LIBRARY = 'xul'
rename from intl/uconv/src/nsConverterInputStream.cpp rename to intl/uconv/nsConverterInputStream.cpp
rename from intl/uconv/src/nsConverterOutputStream.cpp rename to intl/uconv/nsConverterOutputStream.cpp
rename from intl/uconv/idl/nsICurrentCharsetListener.idl rename to intl/uconv/nsICurrentCharsetListener.idl
rename from intl/uconv/idl/nsIUTF8ConverterService.idl rename to intl/uconv/nsIUTF8ConverterService.idl
rename from intl/uconv/src/nsReplacementToUnicode.cpp rename to intl/uconv/nsReplacementToUnicode.cpp