author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Thu, 30 Oct 2014 14:45:37 +0100 | |
changeset 213076 | 1aa1b23d799e5cf08b21bd8b08f78716d35d89f6 |
parent 212983 | 46cab998bd3114b42e03e07488dfb10b2f956db6 (current diff) |
parent 213075 | b991ca238a3538e152dbcd7317b2c5f178b61555 (diff) |
child 213090 | 675913ddbb5571ffbd6a2dd44e82ad3579917d0b |
push id | 27738 |
push user | cbook@mozilla.com |
push date | Thu, 30 Oct 2014 13:46:07 +0000 |
treeherder | mozilla-central@1aa1b23d799e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 36.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/CLOBBER +++ b/CLOBBER @@ -17,9 +17,9 @@ # # Modifying this file will now automatically clobber the buildbot machines \o/ # # Are you updating CLOBBER because you think it's needed for your WebIDL # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Backing out bug 1087560 needs a CLOBBER +For some reason, Clobber had problems applying the changes for Android in Bug 1091118
--- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -215,37 +215,29 @@ NotificationController::WillRefresh(mozi continue; nsIContent* ownerContent = mDocument->DocumentNode()-> FindContentForSubDocument(childDoc->DocumentNode()); if (ownerContent) { Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent); if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) { if (mDocument->AppendChildDocument(childDoc)) { - if (IPCAccessibilityActive()) { - DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc); - childDoc->SetIPCDoc(ipcDoc); - auto contentChild = dom::ContentChild::GetSingleton(); - DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc(); - uint64_t id = reinterpret_cast<uintptr_t>(outerDocAcc->UniqueID()); - contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, - id); - } continue; } outerDocAcc->RemoveChild(childDoc); } // Failed to bind the child document, destroy it. childDoc->Shutdown(); } } - mHangingChildDocuments.Clear(); + + nsTArray<nsRefPtr<DocAccessible>> newChildDocs = Move(mHangingChildDocuments); // If the document is ready and all its subdocuments are completely loaded // then process the document load. if (mDocument->HasLoadState(DocAccessible::eReady) && !mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) && hangingDocCnt == 0) { uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0; for (; childDocIdx < childDocCnt; childDocIdx++) { @@ -277,16 +269,30 @@ NotificationController::WillRefresh(mozi mDocument->ProcessInvalidationList(); // If a generic notification occurs after this point then we may be allowed to // process it synchronously. However we do not want to reenter if fireing // events causes script to run. mObservingState = eRefreshProcessing; ProcessEventQueue(); + + if (IPCAccessibilityActive()) { + size_t newDocCount = newChildDocs.Length(); + for (size_t i = 0; i < newDocCount; i++) { + DocAccessible* childDoc = newChildDocs[i]; + DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc); + childDoc->SetIPCDoc(ipcDoc); + auto contentChild = dom::ContentChild::GetSingleton(); + DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc(); + uint64_t id = reinterpret_cast<uintptr_t>(childDoc->Parent()->UniqueID()); + contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id); + } + } + mObservingState = eRefreshObserving; if (!mDocument) return; // Stop further processing if there are no new notifications of any kind or // events and document load is processed. if (mContentInsertions.IsEmpty() && mNotifications.IsEmpty() && mEvents.IsEmpty() && mTextHash.Count() == 0 &&
--- a/accessible/base/moz.build +++ b/accessible/base/moz.build @@ -58,16 +58,17 @@ if CONFIG['A11Y_LOG']: ] LOCAL_INCLUDES += [ '/accessible/generic', '/accessible/html', '/accessible/ipc', '/accessible/xpcom', '/accessible/xul', + '/dom/base', '/dom/xbl', '/ipc/chromium/src', '/layout/generic', '/layout/style', '/layout/svg', '/layout/xul', '/layout/xul/tree/', ]
--- a/accessible/interfaces/ia2/moz.build +++ b/accessible/interfaces/ia2/moz.build @@ -1,15 +1,15 @@ # -*- 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/. -SharedLibrary('IA2Marshal') +GeckoSharedLibrary('IA2Marshal', linkage=None) DEFINES['REGISTER_PROXY_DLL'] = True DEFFILE = SRCDIR + '/IA2Marshal.def' OS_LIBS += [ 'uuid', 'kernel32',
--- a/accessible/interfaces/msaa/moz.build +++ b/accessible/interfaces/msaa/moz.build @@ -1,15 +1,15 @@ # -*- 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/. -SharedLibrary('AccessibleMarshal') +GeckoSharedLibrary('AccessibleMarshal', linkage=None) GENERATED_SOURCES += [ 'dlldata.c', 'ISimpleDOMDocument_i.c', 'ISimpleDOMDocument_p.c', 'ISimpleDOMNode_i.c', 'ISimpleDOMNode_p.c', 'ISimpleDOMText_i.c',
--- a/accessible/ipc/DocAccessibleChild.cpp +++ b/accessible/ipc/DocAccessibleChild.cpp @@ -13,16 +13,22 @@ namespace a11y { void SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree) { uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID()); uint32_t role = aRoot->Role(); uint32_t childCount = aRoot->ChildCount(); + // OuterDocAccessibles are special because we don't want to serialize the + // child doc here, we'll call PDocAccessibleConstructor in + // NotificationController. + if (childCount == 1 && aRoot->GetChildAt(0)->IsDoc()) + childCount = 0; + aTree.AppendElement(AccessibleData(id, role, childCount)); for (uint32_t i = 0; i < childCount; i++) SerializeTree(aRoot->GetChildAt(i), aTree); } void DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent) {
--- a/b2g/app/moz.build +++ b/b2g/app/moz.build @@ -1,19 +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/. if not CONFIG['LIBXUL_SDK']: if CONFIG['GAIADIR']: - Program(CONFIG['MOZ_APP_NAME'] + "-bin") + GeckoProgram(CONFIG['MOZ_APP_NAME'] + "-bin") else: - Program(CONFIG['MOZ_APP_NAME']) + GeckoProgram(CONFIG['MOZ_APP_NAME']) if CONFIG['MOZ_B2G_LOADER']: SOURCES += [ 'B2GLoader.cpp', ] SOURCES += [ 'nsBrowserApp.cpp', ] @@ -21,18 +21,16 @@ if not CONFIG['LIBXUL_SDK']: # Always enter a Windows program through wmain, whether or not we're # a console application. WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup'] USE_LIBS += [ 'zlib', ] -DEFINES['XPCOM_GLUE'] = True - for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION', 'MOZ_UPDATER'): DEFINES[var] = CONFIG[var] GENERATED_INCLUDES += [ '/build', ] LOCAL_INCLUDES += [ @@ -65,18 +63,14 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk 'gui', 'suspend', ] OS_LIBS += [ 'binder', 'utils', ] -USE_LIBS += [ - 'xpcomglue', -] - DISABLE_STL_WRAPPING = True if CONFIG['OS_ARCH'] == 'WINNT': OS_LIBS += [ 'version', ]
--- a/b2g/gaia/Makefile.in +++ b/b2g/gaia/Makefile.in @@ -1,17 +1,14 @@ # 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/. GAIA_PATH := gaia/profile -# This is needed to avoid making run-b2g depend on mozglue -WRAP_LDFLAGS := - GENERATED_DIRS += $(DIST)/bin/$(GAIA_PATH) include $(topsrcdir)/config/rules.mk libs:: +$(MAKE) -j1 -C $(GAIADIR) clean +$(MAKE) -j1 -C $(GAIADIR) profile (cd $(GAIADIR)/profile && tar $(TAR_CREATE_FLAGS) - .) | (cd $(abspath $(DIST))/bin/$(GAIA_PATH) && tar -xf -)
--- a/browser/app/moz.build +++ b/browser/app/moz.build @@ -1,45 +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 += ['profile/extensions'] -Program(CONFIG['MOZ_APP_NAME']) +if CONFIG['OS_ARCH'] == 'WINNT' and CONFIG['MOZ_METRO']: + GeckoProgram(CONFIG['MOZ_APP_NAME']) +else: + GeckoProgram(CONFIG['MOZ_APP_NAME'], msvcrt='static') SOURCES += [ 'nsBrowserApp.cpp', ] DEFINES['APP_VERSION'] = CONFIG['MOZ_APP_VERSION'] for var in ('MOZILLA_OFFICIAL', 'LIBXUL_SDK'): if CONFIG[var]: DEFINES[var] = True -DEFINES['XPCOM_GLUE'] = True - GENERATED_INCLUDES += [ '/build', ] LOCAL_INCLUDES += [ '/toolkit/xre', '/xpcom/base', '/xpcom/build', ] if not CONFIG['MOZ_METRO']: DELAYLOAD_DLLS += [ 'mozglue.dll', ] - USE_STATIC_LIBS = True + +USE_LIBS += [ + 'mozglue', +] if CONFIG['_MSC_VER']: # Always enter a Windows program through wmain, whether or not we're # a console application. WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup'] if CONFIG['OS_ARCH'] == 'WINNT': RCINCLUDE = 'splash.rc' @@ -51,26 +55,16 @@ if CONFIG['OS_ARCH'] == 'WINNT': # # The default heap size is 1MB on Win32. # The heap will grow if need be. # # Set it to 256k. See bug 127069. if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']: LDFLAGS += ['/HEAP:0x40000'] -if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['MOZ_METRO']: - USE_LIBS += [ - 'mozglue', - 'xpcomglue_staticruntime', - ] -else: - USE_LIBS += [ - 'xpcomglue', - ] - DISABLE_STL_WRAPPING = True if CONFIG['MOZ_LINKER']: OS_LIBS += CONFIG['MOZ_ZLIB_LIBS'] if CONFIG['HAVE_CLOCK_MONOTONIC']: OS_LIBS += CONFIG['REALTIME_LIBS']
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -2339,17 +2339,22 @@ let E10SUINotification = { // e10s testing period to Nightly users. CURRENT_NOTICE_COUNT: 1, CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1", PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt", checkStatus: function() { let skipE10sChecks = false; try { + // This order matters, because + // browser.tabs.remote.autostart.disabled-because-using-a11y is not + // always defined and will throw when not present. + // privacy.trackingprotection.enabled is always defined. skipE10sChecks = (UpdateChannel.get() != "nightly") || + Services.prefs.getBoolPref("privacy.trackingprotection.enabled") || Services.prefs.getBoolPref("browser.tabs.remote.autostart.disabled-because-using-a11y"); } catch(e) {} if (skipE10sChecks) { return; } if (Services.appinfo.browserTabsRemoteAutostart) {
--- a/browser/metro/shell/commandexecutehandler/Makefile.in +++ b/browser/metro/shell/commandexecutehandler/Makefile.in @@ -1,16 +1,11 @@ # 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 MOZ_WINCONSOLE MOZ_WINCONSOLE = 0 endif -include $(topsrcdir)/config/config.mk include $(topsrcdir)/config/rules.mk DIST_PROGRAM = CommandExecuteHandler$(BIN_SUFFIX) - -# Don't link against mozglue.dll -MOZ_GLUE_LDFLAGS = -MOZ_GLUE_PROGRAM_LDFLAGS =
deleted file mode 100644 --- a/browser/metro/shell/testing/Makefile.in +++ /dev/null @@ -1,7 +0,0 @@ -# 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/. - -# don't use moz glue libs -MOZ_GLUE_LDFLAGS = -MOZ_GLUE_PROGRAM_LDFLAGS =
--- a/build/autoconf/toolchain.m4 +++ b/build/autoconf/toolchain.m4 @@ -68,16 +68,24 @@ CLANG_CL= if test "$compiler" = "clang"; then GNU_CC=1 GNU_CXX=1 CLANG_CC=1 CLANG_CXX=1 fi if test "$compiler" = "clang-cl"; then CLANG_CL=1 + # We force clang-cl to emulate Visual C++ 2013 in configure.in, but that + # is based on the CLANG_CL variable defined here, so make sure that we're + # getting the right version here manually. + CC_VERSION=1800 + CXX_VERSION=1800 + # Build on clang-cl with MSVC 2013 with fallback emulation. + CFLAGS="$CFLAGS -fmsc-version=1800 -fallback" + CXXFLAGS="$CXXFLAGS -fmsc-version=1800 -fallback" fi if test "$GNU_CC"; then if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then GCC_USE_GNU_LD=1 fi fi
--- a/build/clang-plugin/Makefile.in +++ b/build/clang-plugin/Makefile.in @@ -1,17 +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/. # LLVM_CXXFLAGS comes with its own optimization flags. MOZ_OPTIMIZE = -MOZ_GLUE_LDFLAGS = - include $(topsrcdir)/config/config.mk # In the current moz.build world, we need to override essentially every # variable to limit ourselves to what we need to build the clang plugin. OS_CXXFLAGS := $(LLVM_CXXFLAGS) -fno-rtti -fno-exceptions OS_COMPILE_CXXFLAGS := OS_LDFLAGS := $(LLVM_LDFLAGS) $(CLANG_LDFLAGS) DSO_LDOPTS := -shared
--- a/build/docs/defining-binaries.rst +++ b/build/docs/defining-binaries.rst @@ -114,21 +114,16 @@ On OSX, one may want to create a special This is done with the ``Framework`` template. Framework('foo') With a ``Framework`` name of ``foo``, the framework file name will be ``foo``. This template however affects the behavior on all platforms, so it needs to be set only on OSX. -Another special kind of library, XPCOM-specific, are XPCOM components. One can -build such a component with the ``XPCOMBinaryComponent`` template. - - XPCOMBinaryComponent('foo') - Executables =========== Executables, a.k.a. programs, are, in the simplest form, defined with the ``Program`` template. Program('foobar') @@ -288,8 +283,33 @@ instead of the ``Library`` name. This on 'mylib', 'otherlib', ] On e.g. Linux, the above ``myprog`` will have DT_NEEDED markers for ``libmylib.so`` and ``libfoo.so`` instead of ``libmylib.so`` and ``libotherlib.so`` if there weren't a ``SONAME``. This means the runtime requirement for ``myprog`` is ``libfoo.so`` instead of ``libotherlib.so``. + + +Gecko-related binaries +====================== + +Some programs or libraries are totally independent of Gecko, and can use the +above mentioned templates. Others are Gecko-related in some way, and may +need XPCOM linkage, mozglue. These things are tedious. A set of additional +templates exists to ease defining such programs and libraries. They are +essentially the same as the above mentioned templates, prefixed with "Gecko": + + - ``GeckoProgram`` + - ``GeckoSimplePrograms`` + - ``GeckoCppUnitTests`` + - ``GeckoSharedLibrary`` + - ``GeckoFramework`` + +There is also ``XPCOMBinaryComponent`` for XPCOM components, which is a +special kind of library. + +All the Gecko-prefixed templates take the same arguments as their +non-Gecko-prefixed counterparts, and can take a few more arguments +for non-standard cases. See the definition of ``GeckoBinary`` in +build/gecko_templates.mozbuild for more details, but most usecases +should not require these additional arguments.
new file mode 100644 --- /dev/null +++ b/build/gecko_templates.mozbuild @@ -0,0 +1,155 @@ +# -*- 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/. + +@template +def GeckoBinary(linkage='dependent', msvcrt='dynamic', mozglue=None): + '''Template for Gecko-related binaries. + + This template is meant to be used in other templates. + + `linkage` indicates the wanted xpcom linkage type. Valid values are + 'dependent', 'standalone' or None. 'dependent' is the default. It is + used for e.g. XPCOM components and executables with direct dependencies + on libxul. Most executables should use the 'standalone' linkage, which + uses the standalone XPCOM glue to load libxul. None means no XPCOM glue + or libxul linkage at all. + + `msvcrt` indicates which Microsoft Visual Studio CRT, for Windows build, + ought to be linked: 'static' or 'dynamic'. + + `mozglue` indicates whether to link against the mozglue library, and if + so, what linkage to apply. Valid values are None (mozglue not linked), + 'program' (mozglue linked to an executable program), or 'library' (mozglue + linked to a shared library). + ''' + if msvcrt == 'dynamic' or CONFIG['OS_ARCH'] != 'WINNT': + xpcomglue = 'xpcomglue' + elif msvcrt == 'static': + USE_STATIC_LIBS = True + xpcomglue = 'xpcomglue_staticruntime' + if not CONFIG['GNU_CC']: + mozglue = None + else: + error('msvcrt must be "dynamic" or "static"') + + if linkage == 'dependent': + USE_LIBS += [ + 'mozalloc', + 'nspr', + '%s_s' % xpcomglue, + 'xul', + ] + elif linkage == 'standalone': + DEFINES['XPCOM_GLUE'] = True + + USE_LIBS += [ + xpcomglue, + ] + elif linkage != None: + error('`linkage` must be "dependent", "standalone" or None') + + if mozglue: + if CONFIG['JS_STANDALONE']: + pass + elif CONFIG['MOZ_CRT']: + if msvcrt == 'dynamic': + USE_LIBS += ['mozcrt'] + elif msvcrt == 'static': + USE_LIBS += ['mozglue'] + else: + error('`msvcrt` must be "dynamic" or "static"') + else: + LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS'] + if mozglue == 'program': + USE_LIBS += ['mozglue'] + if CONFIG['MOZ_GLUE_IN_PROGRAM']: + if CONFIG['GNU_CC']: + LDFLAGS += ['-rdynamic'] + if CONFIG['MOZ_MEMORY']: + USE_LIBS += ['memory'] + if CONFIG['MOZ_LINKER']: + OS_LIBS += CONFIG['MOZ_ZLIB_LIBS'] + elif mozglue == 'library': + if not CONFIG['MOZ_GLUE_IN_PROGRAM']: + USE_LIBS += ['mozglue'] + else: + error('`mozglue` must be "program" or "library"') + + +@template +def GeckoProgram(name, linkage='standalone', **kwargs): + '''Template for program executables related to Gecko. + + `name` identifies the executable base name. + + See the documentation for `GeckoBinary` for other possible arguments, + with the notable difference that the default for `linkage` is 'standalone'. + ''' + Program(name) + + GeckoBinary(linkage=linkage, mozglue='program', **kwargs) + + +@template +def GeckoSimplePrograms(names, **kwargs): + '''Template for simple program executables related to Gecko. + + `names` identifies the executable base names for each executable. + + See the documentation for `GeckoBinary` for other possible arguments. + ''' + SimplePrograms(names) + + GeckoBinary(mozglue='program', **kwargs) + + +@template +def GeckoCppUnitTests(names, **kwargs): + '''Template for C++ unit tests related to Gecko. + + `names` identifies the executable base names for each executable. + + See the documentation for `GeckoBinary` for other possible arguments. + ''' + CppUnitTests(names) + + GeckoBinary(mozglue='program', **kwargs) + + +@template +def GeckoSharedLibrary(name, **kwargs): + '''Template for shared libraries related to Gecko. + + `name` identifies the library base name. + See the documentation for `GeckoBinary` for other possible arguments. + ''' + SharedLibrary(name) + + GeckoBinary(mozglue='library', **kwargs) + + +@template +def GeckoFramework(name, **kwargs): + '''Template for OSX frameworks related to Gecko. + + `name` identifies the library base name. + See the documentation for `GeckoBinary` for other possible arguments. + ''' + Framework(name) + + GeckoBinary(mozglue='library', **kwargs) + + +@template +def XPCOMBinaryComponent(name): + '''Template defining an XPCOM binary component for Gecko. + + `name` is the name of the component. + ''' + GeckoSharedLibrary(name) + + IS_COMPONENT = True +
--- a/build/templates.mozbuild +++ b/build/templates.mozbuild @@ -110,33 +110,9 @@ def HostSimplePrograms(names, ext='.cpp' @template def HostLibrary(name): '''Template for build tools libraries.''' HOST_LIBRARY_NAME = name -@template -def GeckoBinary(): - '''Template for binaries using Gecko. - - This template is meant to be used in other templates. - ''' - USE_LIBS += [ - 'mozalloc', - 'nspr', - 'xpcomglue_s', - 'xul', - ] - - -@template -def XPCOMBinaryComponent(name): - '''Template defining an XPCOM binary component for Gecko. - - name is the name of the component. - ''' - SharedLibrary(name) - - GeckoBinary() - - IS_COMPONENT = True +include('gecko_templates.mozbuild')
--- a/build/unix/elfhack/Makefile.in +++ b/build/unix/elfhack/Makefile.in @@ -2,18 +2,16 @@ # 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/. INTERNAL_TOOLS = 1 OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions -WRAP_LDFLAGS= - include $(topsrcdir)/config/rules.mk test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack $(MKSHLIB) $(LDFLAGS) $< -nostartfiles @echo === @echo === If you get failures below, please file a bug describing the error @echo === and your environment \(compiler and linker versions\), and use @echo === --disable-elf-hack until this is fixed.
--- a/build/unix/moz.build +++ b/build/unix/moz.build @@ -5,10 +5,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION'] or CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']: DIRS += ['stdc++compat'] if CONFIG['USE_ELF_HACK']: DIRS += ['elfhack'] -TEST_DIRS += ['test'] -
deleted file mode 100644 --- a/build/unix/test/Makefile.in +++ /dev/null @@ -1,38 +0,0 @@ -# -*- 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/. - -include $(topsrcdir)/config/rules.mk - -################################################## -## Gather a list of tests, generate timestamp deps -################################################## -TS=.ts -ifneq (,$(findstring check,$(MAKECMDGOALS))) - allsrc = $(wildcard $(srcdir)/*) - tests2run = $(notdir $(filter %.tpl,$(allsrc))) - check_targets += $(addprefix $(TS)/,$(tests2run)) -endif - -check:: $(TS) $(check_targets) - -############################################# -# Only invoke tests when sources have changed -############################################# -$(TS)/%: $(srcdir)/% - $(PERL) $(srcdir)/runtest $< - @touch $@ - -##################################################### -## Extra dep needed to synchronize parallel execution -##################################################### -$(TS): $(TS)/.done -$(TS)/.done: - $(MKDIR) -p $(dir $@) - touch $@ - -GARBAGE_DIRS += $(TS) - -# EOF
deleted file mode 100644 --- a/build/unix/test/moz.build +++ /dev/null @@ -1,6 +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/. -
deleted file mode 100644 --- a/build/unix/test/runtest +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env perl -########################################################################### -## Intent: -## Test::Harness is a testing wrapper that will process output -## from Test.pm module tests. Sumarize results, report stats -## and exit with overall status for the testing suites. -## -## Run testing suite: -## % make clean test -## % perl runtest -## -## Run Individual tests -## % perl tUtils0 -########################################################################### - -##----------------------------## -##---] CORE/CPAN INCLUDES [---## -##----------------------------## -use strict; -use warnings; -use Getopt::Long; - -use Test::Harness; - -##-------------------## -##---] EXPORTS [---## -##-------------------## -our $VERSION = qw(1.0); -use FindBin; - -##-------------------## -##---] GLOBALS [---## -##-------------------## -my %argv; - -##----------------## -##---] MAIN [---## -##----------------## -unless(GetOptions(\%argv, - qw(debug|d) - )) -{ - print "Usage: $0\n"; - print " --debug Enable debug mode\n"; - exit 1; -} - -if (2 > $Test::Harness::VERSION) -{ - print "Unit tests will not be run, Test::Harness is too old\n" - if ($argv{debug}); - exit 0; -} - - -my @tests; - -######################################## -## Gather a list of tests if none passed -######################################## -unless (@tests = @ARGV) -{ - local *D; - opendir(D, '.'); - while($_ = readdir(D)) { - next unless /.t\S+$/; - next if (/\.ts$/); - push(@tests, $_); - } - closedir(D); -} - -############################################### -## Glob a list of tests when directories passed -############################################### -my @tmp; -foreach (@tests) -{ - local *D; - if (-d $_ && (my $dir = $_)) - { - opendir(D, $_) || die "opendir(D) failed: $!"; - my @tests = grep(/\.t[^\.\s]+/o, readdir(D)); - closedir(D); - push(@tmp, map{ join('/', $dir, $_); } @tests); - } else { - push(@tmp, $_); - } -} -@tests = @tmp; - -print "$0: @ARGV\n" if ($argv{debug}); -runtests(@tests); - -# EOF
deleted file mode 100644 --- a/build/unix/test/uniq.tpl +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env perl -########################################################################### -## Intent: Unit test to verify uniq.pl -########################################################################### - -##----------------------------## -##---] CORE/CPAN INCLUDES [---## -##----------------------------## -use strict; -use warnings; -use Cwd; -use Getopt::Long; # GetOptions - -use Test; -sub BEGIN { plan tests => 12 } - -##-------------------## -##---] EXPORTS [---## -##-------------------## -our $VERSION = qw(1.0); - -##------------------## -##---] INCLUDES [---## -##------------------## -use FindBin; - -##-------------------## -##---] GLOBALS [---## -##-------------------## -my %argv; - - -########################################################################### -## Intent: Run the arch command for output -## -## Returns: -## 0 on success -## $? command shell exit status -########################################################################### -sub uniq_pl -{ - my $cmd = "perl $FindBin::RealBin/../uniq.pl @_"; - print "Running: $cmd\n" if ($argv{debug}); - my @tmp = `$cmd 2>&1`; - my @output = map{ split(/\s+/o); } @tmp; - wantarray ? @output : "@output"; -} # uniq_pl - -########################################################################### -## Intent: -## -## Returns: -## 0 on success -########################################################################### -sub check_uniq -{ - print STDERR "Running test: check_uniq\n" if ($argv{debug}); - - # TODO: improve test, uniq.pl regexpr handling not quite right - - my @todo = - ( - [ '', qw(a a/b a/b/c) ] => [ qw(a a/b a/b/c) ], - [ '', qw(a/b a a/b/c) ] => [ qw(a/b a a/b/c) ], - [ '', qw(a/b/c a/b a) ] => [ qw(a/b/c a/b a) ], - - [ '', qw(a a/b a/b/c a/b a) ] => [ qw(a a/b a/b/c) ], # dup removal - - [ '-s', qw(a a/b a/b/c) ] => [ qw(a a/b a/b/c) ], - [ '-s', qw(a/b a a/b/c) ] => [ qw(a a/b a/b/c) ], - [ '-s', qw(a/b/c a/b a) ] => [ qw(a a/b a/b/c) ], - - [ '-r', qw(a a/b a/b/c) ] => [ qw(a) ], - [ '-r', qw(a/b a a/b/c) ] => [ qw(a/b a) ], - [ '-r', qw(a/b/c a/b a) ] => [ qw(a/b/c a/b a) ], - - [ '-r', qw(. .. a/b ../a aa/bb) ] => [ qw(. .. a/b aa/bb) ], - [ '-r', qw(.. a/b ../a . aa/bb) ] => [ qw(.. a/b . aa/bb) ], - ); - - my $ct=1; - while (@todo) - { - my ($a, $b) = splice(@todo, 0, 2); - my @args = @{ $a }; - my @exp = @{ $b }; - - my @out = uniq_pl(@args); -# compareExp(\@out, \@exp, 'Failed on line ' . __LINE__ . ", dataset $ct"); - if (0 && 7 == $ct) - { - print STDERR "\n"; - print STDERR map{ "args> $_\n" }@args; - print STDERR "\n"; - print STDERR map{ "exp> $_\n" }@exp; - print STDERR "\n"; - print STDERR map{ "out> $_\n" }@out; - } - - ok("@out", "@exp", 'Failed on line ' . __LINE__ . ", dataset $ct"); - $ct++; - } - -} # check_uniq - -########################################################################### -## Intent: Smoke tests for the unittests module -########################################################################### -sub smoke -{ - print STDERR "Running test: smoke()\n" if ($argv{debug}); -} # smoke() - -########################################################################### -## Intent: Intitialize global test objects and consts -########################################################################### -sub init -{ - print "Running: init()\n" if ($argv{debug}); -# testplan(24, 0); -} # init() - -##----------------## -##---] MAIN [---## -##----------------## -unless(GetOptions(\%argv, - qw( - debug|d - manual - test=s@ - verbose - ))) -{ - print "USAGE: $0\n"; - print " --debug Enable script debug mode\n"; - print " --fail Force a testing failure condition\n"; - print " --manual Also run disabled tests\n"; - print " --smoke Run smoke tests then exit\n"; - print " --test Run a list of tests by function name\n"; - print " --verbose Enable script verbose mode\n"; - exit 1; -} - -init(); -testbyname(@{ $argv{test} }) if ($argv{test}); -smoke(); - -check_uniq(); -ok(1, 0, 'Forced failure by command line arg --fail') if ($argv{fail}); - -# EOF
deleted file mode 100755 --- a/build/unix/uniq.pl +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env perl - -# 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/. - -##----------------------------## -##---] CORE/CPAN INCLUDES [---## -##----------------------------## -use strict; -use warnings; -use Getopt::Long; - -##-------------------## -##---] EXPORTS [---## -##-------------------## -our $VERSION = qw(1.1); - -##-------------------## -##---] GLOBALS [---## -##-------------------## -my %argv; -my $modver = $Getopt::Long::VERSION || 0; -my $isOldGetopt = ($modver eq '2.25') ? 1 : 0; - -########################################################################### -## Intent: Script init function -########################################################################### -sub init -{ - if ($isOldGetopt) - { - # mozilla.build/mingw perl in need of an upgrade - # emulate Getopt::Long switch|short:init - foreach (qw(debug regex sort)) - { - if (defined($argv{$_})) - { - $argv{$_} ||= 1; - } - } - } -} # init - -##----------------## -##---] MAIN [---## -##----------------## -my @args = ($isOldGetopt) - ? qw(debug|d regex|r sort|s) - : qw(debug|d:1 regex|r:1 sort|s:1) - ; - -unless(GetOptions(\%argv, @args)) -{ - print "Usage: $0\n"; - print " --sort Sort list elements early\n"; - print " --regex Exclude subdirs by pattern\n"; -} - -init(); -my $debug = $argv{debug} || 0; - -my %seen; -my @out; -my @in = ($argv{sort}) ? sort @ARGV : @ARGV; - -foreach my $d (@in) -{ - next if ($seen{$d}++); - - print " arg is $d\n" if ($debug); - - if ($argv{regex}) - { - my $found = 0; - foreach my $dir (@out) - { - my $dirM = quotemeta($dir); - $found++, last if ($d eq $dir || $d =~ m!^${dirM}\/!); - } - print "Adding $d\n" if ($debug && !$found); - push @out, $d if (!$found); - } else { - print "Adding: $d\n" if ($debug); - push(@out, $d); - } -} - -print "@out\n" - -# EOF
new file mode 100755 --- /dev/null +++ b/build/unix/uniq.py @@ -0,0 +1,10 @@ +#! /usr/bin/env 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/. + +'''Prints the given arguments in sorted order with duplicates removed.''' + +import sys + +print(' '.join(sorted(set(sys.argv[1:]))))
--- a/build/win32/Makefile.in +++ b/build/win32/Makefile.in @@ -1,14 +1,12 @@ # 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/. -MOZ_GLUE_LDFLAGS = - include $(topsrcdir)/config/rules.mk ifdef WIN32_REDIST_DIR REDIST_FILES = \ $(MSVC_C_RUNTIME_DLL) \ $(MSVC_CXX_RUNTIME_DLL) \ $(NULL)
deleted file mode 100644 --- a/build/win32/crashinjectdll/Makefile.in +++ /dev/null @@ -1,5 +0,0 @@ -# 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/. - -MOZ_GLUE_LDFLAGS =
deleted file mode 100644 --- a/build/win32/vmwarerecordinghelper/Makefile.in +++ /dev/null @@ -1,5 +0,0 @@ -# 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/. - -MOZ_GLUE_LDFLAGS =
--- a/config/config.mk +++ b/config/config.mk @@ -229,30 +229,20 @@ ifdef HAVE_64BIT_BUILD OS_LDFLAGS = -DEBUG -OPT:REF,ICF else OS_LDFLAGS = -DEBUG -OPT:REF endif endif # NS_TRACE_MALLOC || MOZ_DMD endif # MOZ_DEBUG -# We don't build a static CRT when building a custom CRT, -# it appears to be broken. So don't link to jemalloc if -# the Makefile wants static CRT linking. -ifeq ($(MOZ_MEMORY)_$(USE_STATIC_LIBS),1_1) -# Disable default CRT libs and add the right lib path for the linker -MOZ_GLUE_LDFLAGS= -endif - endif # WINNT && !GNU_CC -ifdef MOZ_GLUE_PROGRAM_LDFLAGS +ifdef MOZ_GLUE_IN_PROGRAM DEFINES += -DMOZ_GLUE_IN_PROGRAM -else -MOZ_GLUE_PROGRAM_LDFLAGS=$(MOZ_GLUE_LDFLAGS) endif # # Build using PIC by default # _ENABLE_PIC=1 # PGO on MSVC is opt-in
--- a/config/external/nss/Makefile.in +++ b/config/external/nss/Makefile.in @@ -103,21 +103,16 @@ else NSPR_INCLUDE_DIR = $(ABS_DIST)/include/nspr endif NSPR_LIB_DIR = $(firstword $(filter -L%,$(NSPR_LIBS))) ifneq (,$(strip $(NSPR_LIB_DIR))) NSPR_LIB_DIR := $(subst -L,,$(subst -L$(DIST),-L$(ABS_DIST),$(NSPR_LIB_DIR))) else NSPR_LIB_DIR = $(ABS_DIST)/lib endif -# Can't pass this in DEFAULT_GMAKE_FLAGS because that overrides -# definitions in NSS, so just export it into the sub-make's environment. -ifeq (WINNT_1,$(OS_TARGET)_$(MOZ_MEMORY)) -export DLLFLAGS -endif # To get debug symbols from NSS export MOZ_DEBUG_SYMBOLS DEFAULT_GMAKE_FLAGS = DEFAULT_GMAKE_FLAGS += CC='$(CC)' DEFAULT_GMAKE_FLAGS += SOURCE_MD_DIR=$(ABS_DIST) DEFAULT_GMAKE_FLAGS += SOURCE_MDHEADERS_DIR=$(NSPR_INCLUDE_DIR) @@ -217,19 +212,27 @@ DEFAULT_GMAKE_FLAGS += \ OS_PTHREAD= \ $(NULL) DEFAULT_GMAKE_FLAGS += ARCHFLAG='$(CFLAGS) -DCHECK_FORK_GETPID -DRTLD_NOLOAD=0 -include $(topsrcdir)/security/manager/android_stub.h' endif endif ifdef WRAP_LDFLAGS +NSS_EXTRA_LDFLAGS += $(WRAP_LDFLAGS) +endif + +ifdef MOZ_GLUE_WRAP_LDFLAGS +NSS_EXTRA_LDFLAGS += $(SHARED_LIBS:$(DEPTH)%=$(MOZ_BUILD_ROOT)%) $(MOZ_GLUE_WRAP_LDFLAGS) +endif + +ifneq (,$(WRAP_LDFLAGS)$(MOZ_GLUE_WRAP_LDFLAGS)) DEFAULT_GMAKE_FLAGS += \ - LDFLAGS='$(LDFLAGS) $(WRAP_LDFLAGS)' \ - DSO_LDOPTS='$(DSO_LDOPTS) $(LDFLAGS) $(WRAP_LDFLAGS)' \ + LDFLAGS='$(LDFLAGS) $(NSS_EXTRA_LDFLAGS)' \ + DSO_LDOPTS='$(DSO_LDOPTS) $(LDFLAGS) $(NSS_EXTRA_LDFLAGS)' \ $(NULL) endif DEFAULT_GMAKE_FLAGS += FREEBL_NO_DEPEND=0 ifeq ($(OS_TARGET),Linux) DEFAULT_GMAKE_FLAGS += FREEBL_LOWHASH=1 endif @@ -352,16 +355,28 @@ GARBAGE += \ $(NULL) endif # GCC_USE_GNU_LD endif # WINNT endif # MOZ_FOLD_LIBS include $(topsrcdir)/config/rules.mk +# Can't pass this in DEFAULT_GMAKE_FLAGS because that overrides +# definitions in NSS, so just export it into the sub-make's environment. +ifeq (WINNT_1,$(OS_TARGET)_$(MOZ_MEMORY)) +ifdef MOZ_CRT +# OS_LIBS comes from having mozcrt as a dependency in moz.build. +DLLFLAGS := $(OS_LIBS) +else +DLLFLAGS := -LIBPATH:$(ABS_DIST)/lib -DEFAULTLIB:mozglue +endif +export DLLFLAGS +endif + ifdef MOZ_FOLD_LIBS # Force the linker to include everything from the static libraries. EXPAND_LIBS_EXEC += --extract $(SHARED_LIBRARY): $(addprefix $(DEPTH)/security/,$(NSS_STATIC_LIBS)) ifdef IMPORT_LIB_SUFFIX IMPORT_PREFIX = $(LIB_PREFIX)
--- a/config/external/nss/moz.build +++ b/config/external/nss/moz.build @@ -5,17 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DIRS += ['crmf'] if CONFIG['MOZ_NATIVE_NSS']: Library('nss') OS_LIBS += CONFIG['NSS_LIBS'] elif CONFIG['MOZ_FOLD_LIBS']: - SharedLibrary('nss') + GeckoSharedLibrary('nss', linkage=None) # TODO: The library name can be changed when bug 845217 is fixed. SHARED_LIBRARY_NAME = 'nss3' SDK_LIBRARY = True # Normally, there should be /something/ to ensure nspr is built # before this directory, but since nspr is built during "export", # it actually doesn't matter.
--- a/config/external/sqlite/Makefile.in +++ b/config/external/sqlite/Makefile.in @@ -21,15 +21,8 @@ GARBAGE += \ # Convert to the format we need for ld. $(LD_VERSION_SCRIPT): $(topsrcdir)/db/sqlite3/src/sqlite.def @$(call py_action,convert_def_file, \ $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) -o $@ $^) endif endif endif - - -ifeq (Darwin,$(OS_TARGET)) -# On OSX, with jemalloc enabled, having sqlite linked against mozglue -# causes crashes in NSS standalone tools. -MOZ_GLUE_LDFLAGS = -endif
deleted file mode 100644 --- a/config/make-atom-strings.pl +++ /dev/null @@ -1,57 +0,0 @@ -#! perl -# 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/. - - -# Converts a list of atoms in the form: -# // OUTPUT_CLASS=<classname> -# // MACRO_NAME=<macro> -# <macroname>(atomName, "String") -# <macroname>(atomName2, "String2") -# -# into a file suitable for gperf using static atoms -# -# usage: -# make-atom-strings < file.h > file.gperf -# -# the lines in the C++ comments define two variables: -# OUTPUT_CLASS is the class who has all the atoms as members -# MACRO_NAME is the macro to look for in the rest of the file -# -# for example -# // OUTPUT_CLASS=nsHTMLAtoms -# // MACRO_NAME=HTML_ATOM -# HTML_ATOM(a, "a") -# HTML_ATOM(body, "body") -# -# etc... -# -# this will generate a file that looks like: -# struct nsStaticAtom ( const char* mValue; nsIAtom** aAtom; } -# %% -# "a", &nsHTMLAtoms::a -# "body", &nsHTMLAtoms::body -# -# etc... -# -# the output can be plugged into gperf to generate a perfect hash - -print "struct nsStaticAtom {const char* mValue; nsIAtom** aAtom; };\n"; -print "%%\n"; - -my $classname, $macroname; - -while (<>) { - chop; - if (/OUTPUT_CLASS=(\S+)/) { - $classname=$1; - } elsif (/MACRO_NAME=(\S+)/) { - $macroname=$1; - } - elsif ($classname && $macroname && - /$macroname\((\S+),\s*\"(.*?)\"\s*\)/) { - my ($str, $atom) = ($2, $1); - print "\"$str\", (nsIAtom**)&${classname}::$atom\n"; - } -}
--- a/config/recurse.mk +++ b/config/recurse.mk @@ -153,10 +153,13 @@ toolkit/library/target: widget/gtk/mozgt endif ifdef MOZ_LDAP_XPCOM ldap/target: config/external/nss/target mozglue/build/target toolkit/library/target: ldap/target endif ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library) mozglue/build/target memory/replace/logalloc/replay/target: memory/replace/dummy/target endif +ifdef MOZ_CRT +mozglue/crt/target: mozglue/build/target +endif endif
--- a/config/rules.mk +++ b/config/rules.mk @@ -636,18 +636,16 @@ ifdef GNU_CC # Force rebuilding libraries and programs in both passes because each # pass uses different object files. $(PROGRAM) $(SHARED_LIBRARY) $(LIBRARY): FORCE endif endif endif # NO_PROFILE_GUIDED_OPTIMIZE -MOZ_PROGRAM_LDFLAGS += $(MOZ_GLUE_PROGRAM_LDFLAGS) - ############################################## checkout: $(MAKE) -C $(topsrcdir) -f client.mk checkout clean clobber realclean clobber_all:: -$(RM) $(ALL_TRASH) -$(RM) -r $(ALL_TRASH_DIRS)
--- a/configure.in +++ b/configure.in @@ -509,20 +509,17 @@ case "$target" in AC_SUBST(MSVS_VERSION) AC_SUBST(MSVC_C_RUNTIME_DLL) AC_SUBST(MSVC_CXX_RUNTIME_DLL) # Disable SEH on clang-cl because it doesn't implement them yet. if test -z "$CLANG_CL"; then AC_DEFINE(HAVE_SEH_EXCEPTIONS) else - # Build on clang-cl with MSVC 2013 with fallback emulation. - CFLAGS="$CFLAGS -fmsc-version=1800 -fallback" - CXXFLAGS="$CXXFLAGS -fmsc-version=1800 -fallback" - # Send our CFLAGS to NSS too + # Send our CFLAGS to NSS MOZ_CFLAGS_NSS=1 AC_DEFINE_UNQUOTED(GTEST_HAS_SEH, 0) fi if test -n "$WIN32_REDIST_DIR"; then if test ! -d "$WIN32_REDIST_DIR"; then AC_MSG_ERROR([Invalid Win32 Redist directory: ${WIN32_REDIST_DIR}]) fi @@ -6268,17 +6265,17 @@ fi if test `echo "$MOZ_EXTENSIONS" | grep -c gio` -ne 0; then MOZ_GIO_COMPONENT=1 MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|gio||'` fi AC_SUBST(MOZ_GIO_COMPONENT) dnl Remove dupes -MOZ_EXTENSIONS=`${PERL} ${srcdir}/build/unix/uniq.pl ${MOZ_EXTENSIONS}` +MOZ_EXTENSIONS=`$PYTHON ${srcdir}/build/unix/uniq.py ${MOZ_EXTENSIONS}` dnl Ensure every extension exists, to avoid mostly-inscrutable error messages dnl when trying to build a nonexistent extension. for extension in $MOZ_EXTENSIONS; do if test ! -d "${srcdir}/extensions/${extension}"; then AC_MSG_ERROR([Unrecognized extension provided to --enable-extensions: ${extension}.]) fi done @@ -7070,38 +7067,25 @@ MOZ_ARG_ENABLE_BOOL(jemalloc, [ --enable-jemalloc Replace memory allocator with jemalloc], MOZ_MEMORY=1, MOZ_MEMORY=) if test "$NS_TRACE_MALLOC"; then MOZ_MEMORY= fi -if test "${OS_TARGET}" = "Android"; then - dnl On Android, we use WRAP_LDFLAGS to link everything to mozglue - : -elif test "${OS_TARGET}" = "WINNT" -o "${OS_TARGET}" = "Darwin"; then - dnl On Windows and OSX, we want to link all our binaries against mozglue - MOZ_GLUE_LDFLAGS='$(call EXPAND_LIBNAME_PATH,mozglue,$(LIBXUL_DIST)/lib)' -else - dnl On other Unix systems, we only want to link executables against mozglue - MOZ_GLUE_PROGRAM_LDFLAGS='$(call EXPAND_LIBNAME_PATH,mozglue,$(LIBXUL_DIST)/lib)' - dnl On other Unix systems, where mozglue is a static library, jemalloc is - dnl separated for the SDK, so we need to add it here. - if test "$MOZ_MEMORY" = 1 -o \( "$LIBXUL_SDK" -a -f "$LIBXUL_SDK/lib/${LIB_PREFIX}memory.${LIB_SUFFIX}" \); then - MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS "'$(call EXPAND_LIBNAME_PATH,memory,$(LIBXUL_DIST)/lib)' - fi - if test -n "$GNU_CC"; then - dnl And we need mozglue symbols to be exported. - MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS -rdynamic" - fi - if test "$MOZ_LINKER" = 1; then - MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS $MOZ_ZLIB_LIBS" - fi -fi +case "${OS_TARGET}" in +Android|WINNT|Darwin) + MOZ_GLUE_IN_PROGRAM= + ;; +*) + dnl On !Android !Windows !OSX, we only want to link executables against mozglue + MOZ_GLUE_IN_PROGRAM=1 + ;; +esac dnl ======================================================== dnl = Enable dynamic replacement of malloc implementation dnl ======================================================== if test -n "$NIGHTLY_BUILD" -a -n "$MOZ_MEMORY"; then # Enable on central for the debugging opportunities it adds. MOZ_REPLACE_MALLOC=1 fi @@ -7219,17 +7203,16 @@ else AC_DEFINE(MOZ_MEMORY_BSD) ;; *-android*|*-linuxandroid*) AC_DEFINE(MOZ_MEMORY_LINUX) AC_DEFINE(MOZ_MEMORY_ANDROID) if test -n "$gonkdir"; then AC_DEFINE(MOZ_MEMORY_GONK) fi - MOZ_GLUE_LDFLAGS= ;; *-*linux*) AC_DEFINE(MOZ_MEMORY_LINUX) ;; *-netbsd*) AC_DEFINE(MOZ_MEMORY_BSD) ;; *-solaris*) @@ -7238,47 +7221,44 @@ else *-mingw*) AC_DEFINE(MOZ_MEMORY_WINDOWS) export MOZ_NO_DEBUG_RTL=1 WIN32_CRT_LIBS="msvcrt.lib msvcprt.lib" dnl Look for a broken crtdll.obj WIN32_CRTDLL_FULLPATH=`lib -nologo -list $WIN32_CRT_LIBS | grep crtdll\\.obj` lib -NOLOGO -OUT:crtdll.obj $WIN32_CRT_LIBS -EXTRACT:$WIN32_CRTDLL_FULLPATH if grep -q '__imp__\{0,1\}free' crtdll.obj; then - MOZ_GLUE_LDFLAGS='-LIBPATH:$(DIST)/lib -NODEFAULTLIB:msvcrt -NODEFAULTLIB:msvcprt -DEFAULTLIB:mozcrt' - dnl Also pass this to NSPR/NSS - DLLFLAGS="$DLLFLAGS $MOZ_GLUE_LDFLAGS" - else - DLLFLAGS="$DLLFLAGS -LIBPATH:\$(DIST)/lib -DEFAULTLIB:mozglue" + MOZ_CRT=1 fi rm crtdll.obj ;; *) AC_MSG_ERROR([--enable-jemalloc not supported on ${target}]) ;; esac fi # MOZ_MEMORY AC_SUBST(MOZ_MEMORY) AC_SUBST(MOZ_JEMALLOC3) AC_SUBST(MOZ_NATIVE_JEMALLOC) -AC_SUBST(MOZ_GLUE_LDFLAGS) -AC_SUBST(MOZ_GLUE_PROGRAM_LDFLAGS) +AC_SUBST(MOZ_CRT) +export MOZ_CRT +AC_SUBST(MOZ_GLUE_IN_PROGRAM) AC_SUBST_LIST(WIN32_CRT_LIBS) -dnl Need to set this for make because NSS doesn't have configure -AC_SUBST(DLLFLAGS) dnl We need to wrap dlopen and related functions on Android because we use dnl our own linker. if test "$OS_TARGET" = Android; then - WRAP_LDFLAGS="${WRAP_LDFLAGS} -L$_objdir/dist/lib -lmozglue" - WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv" + MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv" if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then - WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2,--wrap=tgkill" - fi -fi + MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2,--wrap=tgkill" + fi +fi + +AC_SUBST_LIST(MOZ_GLUE_WRAP_LDFLAGS) +export MOZ_GLUE_WRAP_LDFLAGS dnl ======================================================== dnl = Use JS Call tracing dnl ======================================================== MOZ_ARG_ENABLE_BOOL(trace-jscalls, [ --enable-trace-jscalls Enable JS call enter/exit callback (default=no)], MOZ_TRACE_JSCALLS=1, MOZ_TRACE_JSCALLS= ) @@ -8350,17 +8330,17 @@ MOZ_ARG_ENABLE_STRING(necko-protocols, option=`echo $option | sed 's/^-//'` NECKO_PROTOCOLS=`echo "$NECKO_PROTOCOLS" | sed "s/ ${option}//"` else NECKO_PROTOCOLS="$NECKO_PROTOCOLS $option" fi done], NECKO_PROTOCOLS="$NECKO_PROTOCOLS_DEFAULT") dnl Remove dupes -NECKO_PROTOCOLS=`${PERL} ${srcdir}/build/unix/uniq.pl ${NECKO_PROTOCOLS}` +NECKO_PROTOCOLS=`$PYTHON ${srcdir}/build/unix/uniq.py ${NECKO_PROTOCOLS}` AC_SUBST_SET(NECKO_PROTOCOLS) for p in $NECKO_PROTOCOLS; do AC_DEFINE_UNQUOTED(NECKO_PROTOCOL_$p) _NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES NECKO_PROTOCOL_$p" done dnl dnl option to disable necko's wifi scanner @@ -9198,18 +9178,21 @@ if test "$MOZ_TREE_FREETYPE"; then export ZLIB_CFLAGS="$MOZ_ZLIB_CFLAGS " export ZLIB_LIBS="$MOZ_ZLIB_LIBS " export CONFIG_FILES="unix-cc.mk:unix-cc.in unix-def.mk:unix-def.in freetype-config freetype2.pc:freetype2.in" ac_configure_args="$ac_configure_args --host=$target --disable-shared --with-pic=yes --with-zlib=yes --without-bzip2 --with-png=yes --without-harfbuzz" if ! test -e modules; then mkdir modules fi - - AC_OUTPUT_SUBDIRS(modules/freetype2,$cache_file) + # Only export CC and CXX for the subconfigure, and avoid spilling that + # further down the road. + (export CC CXX; + AC_OUTPUT_SUBDIRS(modules/freetype2) + ) || exit 1 fi if test -z "$direct_nspr_config"; then dnl ======================================================== dnl = Setup a nice relatively clean build environment for dnl = sub-configures. dnl ======================================================== CC="$_SUBDIR_CC" @@ -9298,21 +9281,18 @@ fi if test -n "$NSPR_CFLAGS" -o -n "$NSPR_LIBS"; then ac_configure_args="$ac_configure_args --with-nspr-cflags='$NSPR_CFLAGS'" ac_configure_args="$ac_configure_args --with-nspr-libs='$NSPR_LIBS'" fi ac_configure_args="$ac_configure_args --prefix=$dist" if test "$MOZ_MEMORY"; then ac_configure_args="$ac_configure_args --enable-jemalloc" fi -if test -n "$MOZ_GLUE_LDFLAGS"; then - export MOZ_GLUE_LDFLAGS -fi -if test -n "$MOZ_GLUE_PROGRAM_LDFLAGS"; then - export MOZ_GLUE_PROGRAM_LDFLAGS +if test -n "$MOZ_GLUE_IN_PROGRAM"; then + export MOZ_GLUE_IN_PROGRAM fi if test -n "$ZLIB_IN_MOZGLUE"; then MOZ_ZLIB_LIBS= fi export MOZ_NATIVE_ZLIB export MOZ_ZLIB_CFLAGS export MOZ_ZLIB_LIBS export MOZ_APP_NAME
--- a/docshell/test/chrome/gen_template.pl +++ b/docshell/test/chrome/gen_template.pl @@ -1,19 +1,21 @@ #!/usr/bin/perl + +# This script makes docshell test case templates. It takes one argument: # -# gen_template.pl -# Makes docshell test case templates. -# Takes one argument: +# -b: a bugnumber # -# -b : a bugnumber +# For example, this command: +# +# perl gen_template.pl -b 303267 # -# e.g.: perl gen_template.pl -b 303267 -# -# Writes test case template files for bug 303267 to the current directory. +# Writes test case template files test_bug303267.xul and bug303267_window.xul +# to the current directory. + use FindBin; use Getopt::Long; GetOptions("b=i"=> \$bug_number); $template = "$FindBin::RealBin/test.template.txt"; open(IN,$template) or die("Failed to open input file for reading."); open(OUT, ">>test_bug" . $bug_number . ".xul") or die("Failed to open output file for appending.");
--- a/dom/audiochannel/tests/moz.build +++ b/dom/audiochannel/tests/moz.build @@ -1,23 +1,16 @@ # -*- 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/. -CppUnitTests([ +GeckoCppUnitTests([ 'TestAudioChannelService', ]) if CONFIG['OS_ARCH'] == 'WINNT': DEFINES['NOMINMAX'] = True MOCHITEST_MANIFESTS += ['mochitest.ini'] FAIL_ON_WARNINGS = True - -USE_LIBS += [ - 'mozalloc', - 'nspr', - 'xpcomglue_s', - 'xul', -]
--- a/dom/base/Console.cpp +++ b/dom/base/Console.cpp @@ -1190,17 +1190,17 @@ Console::ProcessCallData(ConsoleCallData js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal); js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK, JS::PrivateValue(aData->mStack.get())); if (!JS_DefineProperty(cx, eventObj, "stacktrace", JS::UndefinedHandleValue, JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER, - JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()), + JS_DATA_TO_FUNC_PTR(JSNative, funObj.get()), nullptr)) { return; } } } if (!mStorage) { mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
--- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -71,17 +71,17 @@ class WebSocketImpl MOZ_FINAL : public n public: NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIWEBSOCKETLISTENER NS_DECL_NSIOBSERVER NS_DECL_NSIREQUEST NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIEVENTTARGET - WebSocketImpl(WebSocket* aWebSocket) + explicit WebSocketImpl(WebSocket* aWebSocket) : mWebSocket(aWebSocket) , mOnCloseScheduled(false) , mFailed(false) , mDisconnected(false) , mCloseEventWasClean(false) , mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL) , mScriptLine(0) , mInnerWindowID(0) @@ -320,21 +320,29 @@ WebSocketImpl::PrintErrorOnConsole(const rv = strBundle->FormatStringFromName(aError, aFormatStrings, aFormatStringsLen, getter_Copies(message)); } else { rv = strBundle->GetStringFromName(aError, getter_Copies(message)); } NS_ENSURE_SUCCESS(rv, rv); - rv = errorObject->InitWithWindowID(message, - NS_ConvertUTF8toUTF16(mScriptFile), - EmptyString(), mScriptLine, 0, - nsIScriptError::errorFlag, "Web Socket", - mInnerWindowID); + if (mInnerWindowID) { + rv = errorObject->InitWithWindowID(message, + NS_ConvertUTF8toUTF16(mScriptFile), + EmptyString(), mScriptLine, 0, + nsIScriptError::errorFlag, "Web Socket", + mInnerWindowID); + } else { + rv = errorObject->Init(message, + NS_ConvertUTF8toUTF16(mScriptFile), + EmptyString(), mScriptLine, 0, + nsIScriptError::errorFlag, "Web Socket"); + } + NS_ENSURE_SUCCESS(rv, rv); // print the error message directly to the JS console rv = console->LogMessage(errorObject); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -458,17 +466,17 @@ WebSocketImpl::FailConnection(uint16_t a CloseConnection(aReasonCode, aReasonString); } namespace { class DisconnectInternalRunnable MOZ_FINAL : public WorkerMainThreadRunnable { public: - DisconnectInternalRunnable(WebSocketImpl* aImpl) + explicit DisconnectInternalRunnable(WebSocketImpl* aImpl) : WorkerMainThreadRunnable(aImpl->mWorkerPrivate) , mImpl(aImpl) { } bool MainThreadRun() MOZ_OVERRIDE { mImpl->DisconnectInternal(); return true; @@ -861,47 +869,67 @@ public: // Walk up to our containing page WorkerPrivate* wp = mWorkerPrivate; while (wp->GetParent()) { wp = wp->GetParent(); } nsPIDOMWindow* window = wp->GetWindow(); - if (!window) { - mRv.Throw(NS_ERROR_FAILURE); - return true; + if (window) { + return InitWithWindow(window); } + return InitWindowless(); + } + +private: + bool InitWithWindow(nsPIDOMWindow* aWindow) + { AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(window))) { + if (NS_WARN_IF(!jsapi.Init(aWindow))) { mRv.Throw(NS_ERROR_FAILURE); return true; } ClearException ce(jsapi.cx()); - nsIDocument* doc = window->GetExtantDoc(); + nsIDocument* doc = aWindow->GetExtantDoc(); if (!doc) { mRv.Throw(NS_ERROR_FAILURE); return true; } nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal(); if (!principal) { mRv.Throw(NS_ERROR_FAILURE); return true; } mImpl->Init(jsapi.cx(), principal, mURL, mProtocolArray, mScriptFile, mScriptLine, mRv, mConnectionFailed); return true; } -private: + bool InitWindowless() + { + MOZ_ASSERT(NS_IsMainThread()); + + WorkerPrivate* wp = mWorkerPrivate; + while (wp->GetParent()) { + wp = wp->GetParent(); + } + + MOZ_ASSERT(!wp->GetWindow()); + + mImpl->Init(nullptr, wp->GetPrincipal(), mURL, mProtocolArray, mScriptFile, + mScriptLine, mRv, mConnectionFailed); + return true; + } + // Raw pointer. This worker runs synchronously. WebSocketImpl* mImpl; const nsAString& mURL; nsTArray<nsString>& mProtocolArray; nsCString mScriptFile; uint32_t mScriptLine; ErrorResult& mRv; @@ -1031,17 +1059,17 @@ WebSocket::Constructor(const GlobalObjec // called asynchrounsly. if (!webSocket->mImpl->mChannel) { return webSocket.forget(); } class MOZ_STACK_CLASS ClearWebSocket { public: - ClearWebSocket(WebSocketImpl* aWebSocketImpl) + explicit ClearWebSocket(WebSocketImpl* aWebSocketImpl) : mWebSocketImpl(aWebSocketImpl) , mDone(false) { } void Done() { mDone = true; @@ -1197,26 +1225,32 @@ WebSocketImpl::Init(JSContext* aCx, return; } } if (mWorkerPrivate) { mScriptFile = aScriptFile; mScriptLine = aScriptLine; } else { + MOZ_ASSERT(aCx); + unsigned lineno; JS::AutoFilename file; if (JS::DescribeScriptedCaller(aCx, &file, &lineno)) { mScriptFile = file.get(); mScriptLine = lineno; } } - // Get WindowID - mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx); + // If we don't have aCx, we are window-less, so we don't have a + // inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in + // DedicateWorkers created by JSM. + if (aCx) { + mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx); + } // parses the url aRv = ParseURL(PromiseFlatString(aURL)); if (NS_WARN_IF(aRv.Failed())) { return; } nsIScriptContext* sc = nullptr; @@ -1582,17 +1616,17 @@ WebSocket::CreateAndDispatchCloseEvent(b return DispatchDOMEvent(nullptr, event, nullptr, nullptr); } namespace { class PrefEnabledRunnable MOZ_FINAL : public WorkerMainThreadRunnable { public: - PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate) + explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate) : WorkerMainThreadRunnable(aWorkerPrivate) , mEnabled(false) { } bool MainThreadRun() MOZ_OVERRIDE { AssertIsOnMainThread(); mEnabled = WebSocket::PrefEnabled(nullptr, nullptr);
--- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1029,18 +1029,19 @@ nsDOMClassInfo::ResolveConstructor(JSCon if (!val.isPrimitive()) { // If val is not an (non-null) object there either is no // constructor for this class, or someone messed with // window.classname, just fall through and let the JS engine // return the Object constructor. JS::Rooted<jsid> id(cx, sConstructor_id); - if (!::JS_DefinePropertyById(cx, obj, id, val, JSPROP_ENUMERATE, - JS_PropertyStub, JS_StrictPropertyStub)) { + if (!::JS_DefinePropertyById(cx, obj, id, val, + JSPROP_ENUMERATE, + JS_STUBGETTER, JS_STUBSETTER)) { return NS_ERROR_UNEXPECTED; } *objp = obj; } return NS_OK; } @@ -1244,18 +1245,22 @@ nsDOMClassInfo::PostCreatePrototype(JSCo JS::Rooted<JSPropertyDescriptor> desc(cx); nsresult rv = ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16, mData, nullptr, nameSpaceManager, proto, &desc); NS_ENSURE_SUCCESS(rv, rv); if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() && !JS_DefineUCProperty(cx, global, mData->mNameUTF16, NS_strlen(mData->mNameUTF16), - desc.value(), desc.attributes(), - desc.getter(), desc.setter())) { + desc.value(), + // Descriptors never store JSNatives for accessors: + // they have either JSFunctions or JSPropertyOps. + desc.attributes() | JSPROP_PROPOP_ACCESSORS, + JS_PROPERTYOP_GETTER(desc.getter()), + JS_PROPERTYOP_SETTER(desc.setter()))) { return NS_ERROR_UNEXPECTED; } return NS_OK; } // static nsIClassInfo * @@ -1446,19 +1451,18 @@ DefineInterfaceConstants(JSContext *cx, JS::Rooted<JS::Value> v(cx); for (i = parent_constant_count; i < constant_count; i++) { nsXPIDLCString name; rv = if_info->GetConstant(i, &v, getter_Copies(name)); NS_ENSURE_TRUE(NS_SUCCEEDED(rv), rv); if (!::JS_DefineProperty(cx, obj, name, v, - JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT, - JS_PropertyStub, JS_StrictPropertyStub)) { + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, + JS_STUBGETTER, JS_STUBSETTER)) { return NS_ERROR_UNEXPECTED; } } return NS_OK; } class nsDOMConstructor MOZ_FINAL : public nsIDOMDOMConstructor @@ -2046,17 +2050,17 @@ ResolvePrototype(nsIXPConnect *aXPConnec v = OBJECT_TO_JSVAL(dot_prototype); JSAutoCompartment ac(cx, class_obj); // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly} if (!JS_WrapValue(cx, &v) || !JS_DefineProperty(cx, class_obj, "prototype", v, JSPROP_PERMANENT | JSPROP_READONLY, - JS_PropertyStub, JS_StrictPropertyStub)) { + JS_STUBGETTER, JS_STUBSETTER)) { return NS_ERROR_UNEXPECTED; } return NS_OK; } static bool OldBindingConstructorEnabled(const nsGlobalNameStruct *aStruct, @@ -2505,17 +2509,17 @@ LookupComponentsShim(JSContext *cx, JS:: NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY); // Create a fake interfaces object. JS::Rooted<JSObject*> interfaces(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global)); NS_ENSURE_TRUE(interfaces, NS_ERROR_OUT_OF_MEMORY); bool ok = JS_DefineProperty(cx, components, "interfaces", interfaces, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, - JS_PropertyStub, JS_StrictPropertyStub); + JS_STUBGETTER, JS_STUBSETTER); NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM // interfaces with constants. for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) { // Grab the names from the table. const char *geckoName = kInterfaceShimMap[i].geckoName; @@ -2528,17 +2532,17 @@ LookupComponentsShim(JSContext *cx, JS:: if (!v.isObject()) { NS_WARNING("Unable to find interface object on global"); continue; } // Define the shim on the interfaces object. ok = JS_DefineProperty(cx, interfaces, geckoName, v, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, - JS_PropertyStub, JS_StrictPropertyStub); + JS_STUBGETTER, JS_STUBSETTER); NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); } FillPropertyDescriptor(desc, global, JS::ObjectValue(*components), false); return NS_OK; }
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2586,18 +2586,19 @@ nsGlobalWindow::SetNewDocument(nsIDocume // Set scriptability based on the state of the docshell. bool allow = GetDocShell()->GetCanExecuteScripts(); xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow); if (!aState) { JS::Rooted<JSObject*> rootedWrapper(cx, GetWrapperPreserveColor()); if (!JS_DefineProperty(cx, newInnerGlobal, "window", rootedWrapper, - JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, - JS_PropertyStub, JS_StrictPropertyStub)) { + JSPROP_ENUMERATE | JSPROP_READONLY | + JSPROP_PERMANENT, + JS_STUBGETTER, JS_STUBSETTER)) { NS_ERROR("can't create the 'window' property"); return NS_ERROR_FAILURE; } } } JSAutoCompartment ac(cx, GetWrapperPreserveColor()); @@ -4523,17 +4524,17 @@ nsGlobalWindow::SetOpener(JSContext* aCx JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor()); if (!thisObj) { aError.Throw(NS_ERROR_UNEXPECTED); return; } if (!JS_WrapObject(aCx, &thisObj) || !JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE, - JS_PropertyStub, JS_StrictPropertyStub)) { + JS_STUBGETTER, JS_STUBSETTER)) { aError.Throw(NS_ERROR_FAILURE); } return; } if (!aOpener.isObjectOrNull()) { // Chrome code trying to set some random value as opener @@ -14017,19 +14018,18 @@ NS_IMETHODIMP nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue) { JS::Rooted<JSObject*> thisObj(aCx, GetWrapper()); if (!thisObj) { return NS_ERROR_UNEXPECTED; } if (!JS_WrapObject(aCx, &thisObj) || - !JS_DefineProperty(aCx, thisObj, "console", aValue, - JSPROP_ENUMERATE, JS_PropertyStub, - JS_StrictPropertyStub)) { + !JS_DefineProperty(aCx, thisObj, "console", aValue, JSPROP_ENUMERATE, + JS_STUBGETTER, JS_STUBSETTER)) { return NS_ERROR_FAILURE; } return NS_OK; } Console* nsGlobalWindow::GetConsole(ErrorResult& aRv)
--- a/dom/base/test/moz.build +++ b/dom/base/test/moz.build @@ -5,17 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini'] # FIXME/bug 575918: out-of-process xpcshell is broken on OS X if CONFIG['OS_ARCH'] != 'Darwin': XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini'] -CppUnitTests([ +GeckoCppUnitTests([ 'TestCSPParser', 'TestGetURL', 'TestNativeXMLHttpRequest', 'TestPlainTextSerializer', ]) MOCHITEST_MANIFESTS += [ 'chrome/mochitest.ini', @@ -33,15 +33,8 @@ if CONFIG['MOZ_CHILD_PERMISSIONS']: MOCHITEST_CHROME_MANIFESTS += [ 'chrome.ini', 'chrome/chrome.ini', 'csp/chrome.ini', ] BROWSER_CHROME_MANIFESTS += ['browser.ini'] - -USE_LIBS += [ - 'mozalloc', - 'nspr', - 'xpcomglue_s', - 'xul', -]
--- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -523,18 +523,19 @@ CreateInterfaceObject(JSContext* cx, JS: int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE; while (namedConstructors->mName) { JS::Rooted<JSObject*> namedConstructor(cx, CreateConstructor(cx, global, namedConstructors->mName, &namedConstructors->mHolder, namedConstructors->mNargs)); if (!namedConstructor || !JS_DefineProperty(cx, namedConstructor, "prototype", - proto, JSPROP_PERMANENT | JSPROP_READONLY, - JS_PropertyStub, JS_StrictPropertyStub) || + proto, + JSPROP_PERMANENT | JSPROP_READONLY, + JS_STUBGETTER, JS_STUBSETTER) || (defineOnGlobal && !DefineConstructor(cx, global, namedConstructors->mName, namedConstructor))) { return nullptr; } js::SetReservedSlot(constructor, namedConstructorSlot++, JS::ObjectValue(*namedConstructor)); ++namedConstructors; @@ -963,34 +964,34 @@ XrayResolveAttribute(JSContext* cx, JS:: for ( ; attributeIds[i] != JSID_VOID; ++i) { if (id == attributeIds[i]) { cacheOnHolder = true; const JSPropertySpec& attrSpec = attributeSpecs[i]; // Because of centralization, we need to make sure we fault in the // JitInfos as well. At present, until the JSAPI changes, the easiest // way to do this is wrap them up as functions ourselves. - desc.setAttributes(attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS); + desc.setAttributes(attrSpec.flags); // They all have getters, so we can just make it. JS::Rooted<JSFunction*> fun(cx, - JS_NewFunctionById(cx, (JSNative)attrSpec.getter.propertyOp.op, + JS_NewFunctionById(cx, attrSpec.getter.native.op, 0, 0, wrapper, id)); if (!fun) return false; - SET_JITINFO(fun, attrSpec.getter.propertyOp.info); + SET_JITINFO(fun, attrSpec.getter.native.info); JSObject *funobj = JS_GetFunctionObject(fun); desc.setGetterObject(funobj); desc.attributesRef() |= JSPROP_GETTER; - if (attrSpec.setter.propertyOp.op) { + if (attrSpec.setter.native.op) { // We have a setter! Make it. - fun = JS_NewFunctionById(cx, (JSNative)attrSpec.setter.propertyOp.op, 1, 0, + fun = JS_NewFunctionById(cx, attrSpec.setter.native.op, 1, 0, wrapper, id); if (!fun) return false; - SET_JITINFO(fun, attrSpec.setter.propertyOp.info); + SET_JITINFO(fun, attrSpec.setter.native.info); funobj = JS_GetFunctionObject(fun); desc.setSetterObject(funobj); desc.attributesRef() |= JSPROP_SETTER; } else { desc.setSetter(nullptr); } desc.object().set(wrapper); return true;
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2407,17 +2407,17 @@ class AttrDefiner(PropertyDefiner): assert not self.hasChromeOnly() and not self.hasNonChromeOnly() def generateArray(self, array, name, doIdArrays): if len(array) == 0: return "" def flags(attr): unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else "" - return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" + + return ("JSPROP_SHARED | JSPROP_ENUMERATE" + unforgeable) def getter(attr): if self.static: accessor = 'get_' + IDLToCIdentifier(attr.identifier.name) jitinfo = "nullptr" else: if attr.hasLenientThis(): @@ -2425,38 +2425,38 @@ class AttrDefiner(PropertyDefiner): elif attr.getExtendedAttribute("CrossOriginReadable"): accessor = "genericCrossOriginGetter" elif self.descriptor.needsSpecialGenericOps(): accessor = "genericGetter" else: accessor = "GenericBindingGetter" jitinfo = ("&%s_getterinfo" % IDLToCIdentifier(attr.identifier.name)) - return "{ { JS_CAST_NATIVE_TO(%s, JSPropertyOp), %s } }" % \ + return "{ { %s, %s } }" % \ (accessor, jitinfo) def setter(attr): if (attr.readonly and attr.getExtendedAttribute("PutForwards") is None and attr.getExtendedAttribute("Replaceable") is None): - return "JSOP_NULLWRAPPER" + return "JSNATIVE_WRAPPER(nullptr)" if self.static: accessor = 'set_' + IDLToCIdentifier(attr.identifier.name) jitinfo = "nullptr" else: if attr.hasLenientThis(): accessor = "genericLenientSetter" elif IsCrossOriginWritable(attr, self.descriptor): accessor = "genericCrossOriginSetter" elif self.descriptor.needsSpecialGenericOps(): accessor = "genericSetter" else: accessor = "GenericBindingSetter" jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name) - return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \ + return "{ { %s, %s } }" % \ (accessor, jitinfo) def specData(attr): return (attr.identifier.name, flags(attr), getter(attr), setter(attr)) return self.generatePrefableArray( array, name, @@ -7626,18 +7626,19 @@ class CGNewResolveHook(CGAbstractBinding if (!desc.object()) { return true; } // If desc.value() is undefined, then the DoNewResolve call // has already defined it on the object. Don't try to also // define it. if (!desc.value().isUndefined() && !JS_DefinePropertyById(cx, obj, id, desc.value(), - desc.attributes(), - desc.getter(), desc.setter())) { + desc.attributes() | JSPROP_PROPOP_ACCESSORS, + JS_PROPERTYOP_GETTER(desc.getter()), + JS_PROPERTYOP_SETTER(desc.setter()))) { return false; } objp.set(obj); return true; """)) def definition_body(self): if self.descriptor.isGlobal(): @@ -9598,18 +9599,19 @@ class CGResolveOwnPropertyViaNewresolve( return false; } // If desc.value() is undefined, then the DoNewResolve call // has already defined the property on the object. Don't // try to also define it. if (objDesc.object() && !objDesc.value().isUndefined() && !JS_DefinePropertyById(cx, obj, id, objDesc.value(), - objDesc.attributes(), - objDesc.getter(), objDesc.setter())) { + objDesc.attributes() | JSPROP_PROPOP_ACCESSORS, + JS_PROPERTYOP_GETTER(objDesc.getter()), + JS_PROPERTYOP_SETTER(objDesc.setter()))) { return false; } } return self->DoNewResolve(cx, wrapper, id, desc); """)) class CGEnumerateOwnProperties(CGAbstractStaticMethod):
--- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -605,182 +605,190 @@ public: return TexSubImage2D_base(texImageTarget.get(), level, xoffset, yoffset, size.width, size.height, data->Stride(), format, type, data->GetData(), byteLength, js::Scalar::TypeMax, srcFormat, mPixelStorePremultiplyAlpha); } - void Uniform1i(WebGLUniformLocation* location, GLint x); - void Uniform2i(WebGLUniformLocation* location, GLint x, GLint y); - void Uniform3i(WebGLUniformLocation* location, GLint x, GLint y, - GLint z); - void Uniform4i(WebGLUniformLocation* location, GLint x, GLint y, - GLint z, GLint w); + void Uniform1i(WebGLUniformLocation* loc, GLint x); + void Uniform2i(WebGLUniformLocation* loc, GLint x, GLint y); + void Uniform3i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z); + void Uniform4i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z, + GLint w); + + void Uniform1f(WebGLUniformLocation* loc, GLfloat x); + void Uniform2f(WebGLUniformLocation* loc, GLfloat x, GLfloat y); + void Uniform3f(WebGLUniformLocation* loc, GLfloat x, GLfloat y, GLfloat z); + void Uniform4f(WebGLUniformLocation* loc, GLfloat x, GLfloat y, GLfloat z, + GLfloat w); - void Uniform1f(WebGLUniformLocation* location, GLfloat x); - void Uniform2f(WebGLUniformLocation* location, GLfloat x, GLfloat y); - void Uniform3f(WebGLUniformLocation* location, GLfloat x, GLfloat y, - GLfloat z); - void Uniform4f(WebGLUniformLocation* location, GLfloat x, GLfloat y, - GLfloat z, GLfloat w); - - void Uniform1iv(WebGLUniformLocation* location, - const dom::Int32Array& arr) { + // Int array + void Uniform1iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) { arr.ComputeLengthAndData(); - Uniform1iv_base(location, arr.Length(), arr.Data()); + Uniform1iv_base(loc, arr.Length(), arr.Data()); } - void Uniform1iv(WebGLUniformLocation* location, - const dom::Sequence<GLint>& arr) { - Uniform1iv_base(location, arr.Length(), arr.Elements()); + void Uniform1iv(WebGLUniformLocation* loc, + const dom::Sequence<GLint>& arr) + { + Uniform1iv_base(loc, arr.Length(), arr.Elements()); } - void Uniform1iv_base(WebGLUniformLocation* location, uint32_t arrayLength, + void Uniform1iv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLint* data); - void Uniform2iv(WebGLUniformLocation* location, - const dom::Int32Array& arr) { + void Uniform2iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) { arr.ComputeLengthAndData(); - Uniform2iv_base(location, arr.Length(), arr.Data()); + Uniform2iv_base(loc, arr.Length(), arr.Data()); + } + void Uniform2iv(WebGLUniformLocation* loc, + const dom::Sequence<GLint>& arr) + { + Uniform2iv_base(loc, arr.Length(), arr.Elements()); } - void Uniform2iv(WebGLUniformLocation* location, - const dom::Sequence<GLint>& arr) { - Uniform2iv_base(location, arr.Length(), arr.Elements()); + void Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLint* data); + + void Uniform3iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) { + arr.ComputeLengthAndData(); + Uniform3iv_base(loc, arr.Length(), arr.Data()); } - void Uniform2iv_base(WebGLUniformLocation* location, uint32_t arrayLength, + void Uniform3iv(WebGLUniformLocation* loc, + const dom::Sequence<GLint>& arr) + { + Uniform3iv_base(loc, arr.Length(), arr.Elements()); + } + void Uniform3iv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLint* data); - void Uniform3iv(WebGLUniformLocation* location, - const dom::Int32Array& arr) { + void Uniform4iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) { arr.ComputeLengthAndData(); - Uniform3iv_base(location, arr.Length(), arr.Data()); + Uniform4iv_base(loc, arr.Length(), arr.Data()); } - void Uniform3iv(WebGLUniformLocation* location, - const dom::Sequence<GLint>& arr) { - Uniform3iv_base(location, arr.Length(), arr.Elements()); + void Uniform4iv(WebGLUniformLocation* loc, + const dom::Sequence<GLint>& arr) + { + Uniform4iv_base(loc, arr.Length(), arr.Elements()); } - void Uniform3iv_base(WebGLUniformLocation* location, uint32_t arrayLength, + void Uniform4iv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLint* data); - void Uniform4iv(WebGLUniformLocation* location, - const dom::Int32Array& arr) { + // Float array + void Uniform1fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) { arr.ComputeLengthAndData(); - Uniform4iv_base(location, arr.Length(), arr.Data()); - } - void Uniform4iv(WebGLUniformLocation* location, - const dom::Sequence<GLint>& arr) { - Uniform4iv_base(location, arr.Length(), arr.Elements()); + Uniform1fv_base(loc, arr.Length(), arr.Data()); } - void Uniform4iv_base(WebGLUniformLocation* location, uint32_t arrayLength, - const GLint* data); - - void Uniform1fv(WebGLUniformLocation* location, - const dom::Float32Array& arr) { - arr.ComputeLengthAndData(); - Uniform1fv_base(location, arr.Length(), arr.Data()); + void Uniform1fv(WebGLUniformLocation* loc, + const dom::Sequence<GLfloat>& arr) + { + Uniform1fv_base(loc, arr.Length(), arr.Elements()); } - void Uniform1fv(WebGLUniformLocation* location, - const dom::Sequence<GLfloat>& arr) { - Uniform1fv_base(location, arr.Length(), arr.Elements()); - } - void Uniform1fv_base(WebGLUniformLocation* location, uint32_t arrayLength, + void Uniform1fv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLfloat* data); - void Uniform2fv(WebGLUniformLocation* location, - const dom::Float32Array& arr) { + void Uniform2fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) { arr.ComputeLengthAndData(); - Uniform2fv_base(location, arr.Length(), arr.Data()); + Uniform2fv_base(loc, arr.Length(), arr.Data()); } - void Uniform2fv(WebGLUniformLocation* location, - const dom::Sequence<GLfloat>& arr) { - Uniform2fv_base(location, arr.Length(), arr.Elements()); + void Uniform2fv(WebGLUniformLocation* loc, + const dom::Sequence<GLfloat>& arr) + { + Uniform2fv_base(loc, arr.Length(), arr.Elements()); } - void Uniform2fv_base(WebGLUniformLocation* location, uint32_t arrayLength, + void Uniform2fv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLfloat* data); - void Uniform3fv(WebGLUniformLocation* location, - const dom::Float32Array& arr) { + void Uniform3fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) { arr.ComputeLengthAndData(); - Uniform3fv_base(location, arr.Length(), arr.Data()); + Uniform3fv_base(loc, arr.Length(), arr.Data()); } - void Uniform3fv(WebGLUniformLocation* location, - const dom::Sequence<GLfloat>& arr) { - Uniform3fv_base(location, arr.Length(), arr.Elements()); + void Uniform3fv(WebGLUniformLocation* loc, + const dom::Sequence<GLfloat>& arr) + { + Uniform3fv_base(loc, arr.Length(), arr.Elements()); } - void Uniform3fv_base(WebGLUniformLocation* location, uint32_t arrayLength, + void Uniform3fv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLfloat* data); - void Uniform4fv(WebGLUniformLocation* location, - const dom::Float32Array& arr) { + void Uniform4fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) { arr.ComputeLengthAndData(); - Uniform4fv_base(location, arr.Length(), arr.Data()); + Uniform4fv_base(loc, arr.Length(), arr.Data()); } - void Uniform4fv(WebGLUniformLocation* location, - const dom::Sequence<GLfloat>& arr) { - Uniform4fv_base(location, arr.Length(), arr.Elements()); + void Uniform4fv(WebGLUniformLocation* loc, + const dom::Sequence<GLfloat>& arr) + { + Uniform4fv_base(loc, arr.Length(), arr.Elements()); } - void Uniform4fv_base(WebGLUniformLocation* location, uint32_t arrayLength, + void Uniform4fv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLfloat* data); - void UniformMatrix2fv(WebGLUniformLocation* location, - WebGLboolean transpose, - const dom::Float32Array &value) { + // Matrix + void UniformMatrix2fv(WebGLUniformLocation* loc, WebGLboolean transpose, + const dom::Float32Array& value) + { value.ComputeLengthAndData(); - UniformMatrix2fv_base(location, transpose, value.Length(), value.Data()); + UniformMatrix2fv_base(loc, transpose, value.Length(), value.Data()); } - void UniformMatrix2fv(WebGLUniformLocation* location, - WebGLboolean transpose, - const dom::Sequence<float> &value) { - UniformMatrix2fv_base(location, transpose, value.Length(), + void UniformMatrix2fv(WebGLUniformLocation* loc, WebGLboolean transpose, + const dom::Sequence<float>& value) + { + UniformMatrix2fv_base(loc, transpose, value.Length(), value.Elements()); } - void UniformMatrix2fv_base(WebGLUniformLocation* location, - WebGLboolean transpose, uint32_t arrayLength, + void UniformMatrix2fv_base(WebGLUniformLocation* loc, + WebGLboolean transpose, size_t arrayLength, const float* data); - void UniformMatrix3fv(WebGLUniformLocation* location, - WebGLboolean transpose, - const dom::Float32Array &value) { + void UniformMatrix3fv(WebGLUniformLocation* loc, WebGLboolean transpose, + const dom::Float32Array& value) + { value.ComputeLengthAndData(); - UniformMatrix3fv_base(location, transpose, value.Length(), value.Data()); + UniformMatrix3fv_base(loc, transpose, value.Length(), value.Data()); + } + void UniformMatrix3fv(WebGLUniformLocation* loc, WebGLboolean transpose, + const dom::Sequence<float>& value) + { + UniformMatrix3fv_base(loc, transpose, value.Length(), value.Elements()); } - void UniformMatrix3fv(WebGLUniformLocation* location, - WebGLboolean transpose, - const dom::Sequence<float> &value) { - UniformMatrix3fv_base(location, transpose, value.Length(), + void UniformMatrix3fv_base(WebGLUniformLocation* loc, + WebGLboolean transpose, size_t arrayLength, + const float* data); + + void UniformMatrix4fv(WebGLUniformLocation* loc, WebGLboolean transpose, + const dom::Float32Array& value) + { + value.ComputeLengthAndData(); + UniformMatrix4fv_base(loc, transpose, value.Length(), value.Data()); + } + void UniformMatrix4fv(WebGLUniformLocation* loc, WebGLboolean transpose, + const dom::Sequence<float>& value) + { + UniformMatrix4fv_base(loc, transpose, value.Length(), value.Elements()); } - void UniformMatrix3fv_base(WebGLUniformLocation* location, - WebGLboolean transpose, uint32_t arrayLength, - const float* data); - - void UniformMatrix4fv(WebGLUniformLocation* location, - WebGLboolean transpose, - const dom::Float32Array &value) { - value.ComputeLengthAndData(); - UniformMatrix4fv_base(location, transpose, value.Length(), value.Data()); - } - void UniformMatrix4fv(WebGLUniformLocation* location, - WebGLboolean transpose, - const dom::Sequence<float> &value) { - UniformMatrix4fv_base(location, transpose, value.Length(), - value.Elements()); - } - void UniformMatrix4fv_base(WebGLUniformLocation* location, - WebGLboolean transpose, uint32_t arrayLength, + void UniformMatrix4fv_base(WebGLUniformLocation* loc, + WebGLboolean transpose, size_t arrayLength, const float* data); void UseProgram(WebGLProgram *prog); bool ValidateAttribArraySetter(const char* name, uint32_t cnt, uint32_t arrayLength); - bool ValidateUniformArraySetter(const char* name, uint32_t expectedElemSize, WebGLUniformLocation *location_object, - GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength); - bool ValidateUniformMatrixArraySetter(const char* name, int dim, WebGLUniformLocation *location_object, - GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength, - WebGLboolean aTranspose); - bool ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location); + bool ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterSize, + GLenum setterType, const char* info, + GLuint* out_rawLoc); + bool ValidateUniformArraySetter(WebGLUniformLocation* loc, + uint8_t setterElemSize, GLenum setterType, + size_t setterArraySize, const char* info, + GLuint* out_rawLoc, + GLsizei* out_numElementsToUpload); + bool ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc, + uint8_t setterDims, GLenum setterType, + size_t setterArraySize, + bool setterTranspose, + const char* info, GLuint* out_rawLoc, + GLsizei* out_numElementsToUpload); void ValidateProgram(WebGLProgram *prog); bool ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object); bool ValidateSamplerUniformSetter(const char* info, WebGLUniformLocation *location, GLint value); void Viewport(GLint x, GLint y, GLsizei width, GLsizei height); // ----------------------------------------------------------------------------- // WEBGL_lose_context @@ -1042,16 +1050,22 @@ protected: int32_t mGLMaxVertexTextureImageUnits; int32_t mGLMaxVaryingVectors; int32_t mGLMaxFragmentUniformVectors; int32_t mGLMaxVertexUniformVectors; int32_t mGLMaxColorAttachments; int32_t mGLMaxDrawBuffers; uint32_t mGLMaxTransformFeedbackSeparateAttribs; +public: + GLuint MaxVertexAttribs() const { + return mGLMaxVertexAttribs; + } + +protected: // Represents current status of the context with respect to context loss. // That is, whether the context is lost, and what part of the context loss // process we currently are at. // This is used to support the WebGL spec's asyncronous nature in handling // context loss. enum ContextStatus { // The context is stable; there either are none or we don't know of any. ContextNotLost, @@ -1110,17 +1124,16 @@ protected: bool ValidateComparisonEnum(GLenum target, const char *info); bool ValidateStencilOpEnum(GLenum action, const char *info); bool ValidateFaceEnum(GLenum face, const char *info); bool ValidateTexInputData(GLenum type, js::Scalar::Type jsArrayType, WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateDrawModeEnum(GLenum mode, const char *info); - bool ValidateAttribIndex(GLuint index, const char *info); bool ValidateStencilParamsForDrawCall(); bool ValidateGLSLVariableName(const nsAString& name, const char *info); bool ValidateGLSLCharacter(char16_t c); bool ValidateGLSLString(const nsAString& string, const char *info); bool ValidateCopyTexImage(GLenum internalformat, WebGLTexImageFunc func,
--- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -115,37 +115,40 @@ WebGLContext::AttachShader(WebGLProgram // leave it for when we support more than one shader of each type. if (program->HasAttachedShaderOfType(shader->ShaderType())) return ErrorInvalidOperation("attachShader: only one of each type of shader may be attached to a program"); if (!program->AttachShader(shader)) return ErrorInvalidOperation("attachShader: shader is already attached"); } - void -WebGLContext::BindAttribLocation(WebGLProgram *prog, GLuint location, +WebGLContext::BindAttribLocation(WebGLProgram* prog, GLuint location, const nsAString& name) { if (IsContextLost()) return; if (!ValidateObject("bindAttribLocation: program", prog)) return; GLuint progname = prog->GLName(); if (!ValidateGLSLVariableName(name, "bindAttribLocation")) return; - if (!ValidateAttribIndex(location, "bindAttribLocation")) - return; + if (location >= MaxVertexAttribs()) { + return ErrorInvalidValue("bindAttribLocation: `location` must be less" + " than MAX_VERTEX_ATTRIBS."); + } if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) - return ErrorInvalidOperation("bindAttribLocation: can't set the location of a name that starts with 'gl_'"); + return ErrorInvalidOperation("bindAttribLocation: can't set the" + " location of a name that starts with" + " 'gl_'."); NS_LossyConvertUTF16toASCII cname(name); nsCString mappedName; if (mShaderValidation) { WebGLProgram::HashMapIdentifier(cname, &mappedName); } else { mappedName.Assign(cname); } @@ -422,16 +425,27 @@ WebGLContext::CopyTexSubImage2D_base(Tex } TexInternalFormat effectiveInternalFormat = EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalformat, framebuffertype); // this should never fail, validation happened earlier. MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE); + const bool widthOrHeightIsZero = (width == 0 || height == 0); + if (gl->WorkAroundDriverBugs() && + sub && widthOrHeightIsZero) + { + // NV driver on Linux complains that CopyTexSubImage2D(level=0, + // xoffset=0, yoffset=2, x=0, y=0, width=0, height=0) from a 300x150 FB + // to a 0x2 texture. This a useless thing to do, but technically legal. + // NV331.38 generates INVALID_VALUE. + return DummyFramebufferOperation(info); + } + // check if the memory size of this texture may change with this call bool sizeMayChange = !sub; if (!sub && tex->HasImageInfoAt(texImageTarget, level)) { const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level); sizeMayChange = width != imageInfo.Width() || height != imageInfo.Height() || effectiveInternalFormat != imageInfo.EffectiveInternalFormat(); } @@ -874,19 +888,29 @@ WebGLContext::GetActiveAttrib(WebGLProgr { if (IsContextLost()) return nullptr; if (!ValidateObject("getActiveAttrib: program", prog)) return nullptr; MakeContextCurrent(); + GLuint progname = prog->GLName(); + + GLuint activeAttribs = 0; + gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTES, + (GLint*)&activeAttribs); + if (index >= activeAttribs) { + ErrorInvalidValue("`index` (%i) must be less than ACTIVE_ATTRIBUTES" + " (%i).", + index, activeAttribs); + return nullptr; + } GLint len = 0; - GLuint progname = prog->GLName();; gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len); if (len == 0) return nullptr; nsAutoArrayPtr<char> name(new char[len]); GLint attrsize = 0; GLuint attrtype = 0; @@ -972,19 +996,29 @@ WebGLContext::GetActiveUniform(WebGLProg { if (IsContextLost()) return nullptr; if (!ValidateObject("getActiveUniform: program", prog)) return nullptr; MakeContextCurrent(); + GLuint progname = prog->GLName(); + + GLuint activeUniforms = 0; + gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS, + (GLint*)&activeUniforms); + if (index >= activeUniforms) { + ErrorInvalidValue("`index` (%i) must be less than ACTIVE_UNIFORMS" + " (%i).", + index, activeUniforms); + return nullptr; + } GLint len = 0; - GLuint progname = prog->GLName(); gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &len); if (len == 0) return nullptr; nsAutoArrayPtr<char> name(new char[len]); GLint usize = 0; GLuint utype = 0; @@ -1064,18 +1098,24 @@ WebGLContext::GetAttribLocation(WebGLPro } JS::Value WebGLContext::GetBufferParameter(GLenum target, GLenum pname) { if (IsContextLost()) return JS::NullValue(); - if (target != LOCAL_GL_ARRAY_BUFFER && target != LOCAL_GL_ELEMENT_ARRAY_BUFFER) { - ErrorInvalidEnumInfo("getBufferParameter: target", target); + + WebGLRefPtr<WebGLBuffer>* slot = GetBufferSlotByTarget(target, + "getBufferParameter"); + if (!slot) + return JS::NullValue(); + + if (!*slot) { + ErrorInvalidOperation("No buffer bound to `target` (0x%4x).", target); return JS::NullValue(); } MakeContextCurrent(); switch (pname) { case LOCAL_GL_BUFFER_SIZE: case LOCAL_GL_BUFFER_USAGE: @@ -2685,297 +2725,333 @@ WebGLContext::SurfaceFromElementResultTo return NS_ERROR_NOT_IMPLEMENTED; } imageOut = data; return NS_OK; } - +//////////////////////////////////////////////////////////////////////////////// +// Uniform setters. void -WebGLContext::Uniform1i(WebGLUniformLocation *location_object, GLint a1) +WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1) { - GLint location; - if (!ValidateUniformSetter("Uniform1i", location_object, location)) + GLuint rawLoc; + if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, "uniform1i", &rawLoc)) return; // Only uniform1i can take sampler settings. - if (!ValidateSamplerUniformSetter("Uniform1i", location_object, a1)) + if (!ValidateSamplerUniformSetter("Uniform1i", loc, a1)) return; MakeContextCurrent(); - gl->fUniform1i(location, a1); + gl->fUniform1i(rawLoc, a1); } void -WebGLContext::Uniform2i(WebGLUniformLocation *location_object, GLint a1, - GLint a2) +WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2) { - GLint location; - if (!ValidateUniformSetter("Uniform2i", location_object, location)) + GLuint rawLoc; + if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT, "uniform2i", &rawLoc)) return; MakeContextCurrent(); - gl->fUniform2i(location, a1, a2); + gl->fUniform2i(rawLoc, a1, a2); } void -WebGLContext::Uniform3i(WebGLUniformLocation *location_object, GLint a1, - GLint a2, GLint a3) +WebGLContext::Uniform3i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3) { - GLint location; - if (!ValidateUniformSetter("Uniform3i", location_object, location)) + GLuint rawLoc; + if (!ValidateUniformSetter(loc, 3, LOCAL_GL_INT, "uniform3i", &rawLoc)) return; MakeContextCurrent(); - gl->fUniform3i(location, a1, a2, a3); + gl->fUniform3i(rawLoc, a1, a2, a3); } void -WebGLContext::Uniform4i(WebGLUniformLocation *location_object, GLint a1, - GLint a2, GLint a3, GLint a4) +WebGLContext::Uniform4i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3, + GLint a4) { - GLint location; - if (!ValidateUniformSetter("Uniform4i", location_object, location)) + GLuint rawLoc; + if (!ValidateUniformSetter(loc, 4, LOCAL_GL_INT, "uniform4i", &rawLoc)) return; MakeContextCurrent(); - gl->fUniform4i(location, a1, a2, a3, a4); + gl->fUniform4i(rawLoc, a1, a2, a3, a4); } void -WebGLContext::Uniform1f(WebGLUniformLocation *location_object, GLfloat a1) +WebGLContext::Uniform1f(WebGLUniformLocation* loc, GLfloat a1) { - GLint location; - if (!ValidateUniformSetter("Uniform1f", location_object, location)) + GLuint rawLoc; + if (!ValidateUniformSetter(loc, 1, LOCAL_GL_FLOAT, "uniform1f", &rawLoc)) return; + MakeContextCurrent(); - gl->fUniform1f(location, a1); + gl->fUniform1f(rawLoc, a1); } void -WebGLContext::Uniform2f(WebGLUniformLocation *location_object, GLfloat a1, - GLfloat a2) +WebGLContext::Uniform2f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2) { - GLint location; - if (!ValidateUniformSetter("Uniform2f", location_object, location)) + GLuint rawLoc; + if (!ValidateUniformSetter(loc, 2, LOCAL_GL_FLOAT, "uniform2f", &rawLoc)) return; + MakeContextCurrent(); - gl->fUniform2f(location, a1, a2); + gl->fUniform2f(rawLoc, a1, a2); } void -WebGLContext::Uniform3f(WebGLUniformLocation *location_object, GLfloat a1, - GLfloat a2, GLfloat a3) +WebGLContext::Uniform3f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2, + GLfloat a3) { - GLint location; - if (!ValidateUniformSetter("Uniform3f", location_object, location)) + GLuint rawLoc; + if (!ValidateUniformSetter(loc, 3, LOCAL_GL_FLOAT, "uniform3f", &rawLoc)) return; + MakeContextCurrent(); - gl->fUniform3f(location, a1, a2, a3); + gl->fUniform3f(rawLoc, a1, a2, a3); } void -WebGLContext::Uniform4f(WebGLUniformLocation *location_object, GLfloat a1, - GLfloat a2, GLfloat a3, GLfloat a4) +WebGLContext::Uniform4f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2, + GLfloat a3, GLfloat a4) { - GLint location; - if (!ValidateUniformSetter("Uniform4f", location_object, location)) + GLuint rawLoc; + if (!ValidateUniformSetter(loc, 4, LOCAL_GL_FLOAT, "uniform4f", &rawLoc)) return; + MakeContextCurrent(); - gl->fUniform4f(location, a1, a2, a3, a4); + gl->fUniform4f(rawLoc, a1, a2, a3, a4); +} + +//////////////////////////////////////// +// Array + +void +WebGLContext::Uniform1iv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLint* data) +{ + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_INT, arrayLength, + "uniform1iv", &rawLoc, + &numElementsToUpload)) + { + return; + } + + if (!ValidateSamplerUniformSetter("uniform1iv", loc, data[0])) + return; + + MakeContextCurrent(); + gl->fUniform1iv(rawLoc, numElementsToUpload, data); } void -WebGLContext::Uniform1iv_base(WebGLUniformLocation *location_object, - uint32_t arrayLength, const GLint* data) +WebGLContext::Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLint* data) { - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformArraySetter("Uniform1iv", 1, location_object, location, - numElementsToUpload, arrayLength)) { + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_INT, arrayLength, + "uniform2iv", &rawLoc, + &numElementsToUpload)) + { return; } - if (!ValidateSamplerUniformSetter("Uniform1iv", location_object, data[0])) - return; - - MakeContextCurrent(); - gl->fUniform1iv(location, numElementsToUpload, data); -} - -void -WebGLContext::Uniform2iv_base(WebGLUniformLocation *location_object, - uint32_t arrayLength, const GLint* data) -{ - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformArraySetter("Uniform2iv", 2, location_object, location, - numElementsToUpload, arrayLength)) { - return; - } - - if (!ValidateSamplerUniformSetter("Uniform2iv", location_object, data[0]) || - !ValidateSamplerUniformSetter("Uniform2iv", location_object, data[1])) + if (!ValidateSamplerUniformSetter("uniform2iv", loc, data[0]) || + !ValidateSamplerUniformSetter("uniform2iv", loc, data[1])) { return; } MakeContextCurrent(); - gl->fUniform2iv(location, numElementsToUpload, data); + gl->fUniform2iv(rawLoc, numElementsToUpload, data); } void -WebGLContext::Uniform3iv_base(WebGLUniformLocation *location_object, - uint32_t arrayLength, const GLint* data) +WebGLContext::Uniform3iv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLint* data) { - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformArraySetter("Uniform3iv", 3, location_object, location, - numElementsToUpload, arrayLength)) { + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_INT, arrayLength, + "uniform3iv", &rawLoc, + &numElementsToUpload)) + { return; } - if (!ValidateSamplerUniformSetter("Uniform3iv", location_object, data[0]) || - !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[1]) || - !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[2])) + if (!ValidateSamplerUniformSetter("uniform3iv", loc, data[0]) || + !ValidateSamplerUniformSetter("uniform3iv", loc, data[1]) || + !ValidateSamplerUniformSetter("uniform3iv", loc, data[2])) { return; } MakeContextCurrent(); - gl->fUniform3iv(location, numElementsToUpload, data); + gl->fUniform3iv(rawLoc, numElementsToUpload, data); } void -WebGLContext::Uniform4iv_base(WebGLUniformLocation *location_object, - uint32_t arrayLength, const GLint* data) +WebGLContext::Uniform4iv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLint* data) { - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformArraySetter("Uniform4iv", 4, location_object, location, - numElementsToUpload, arrayLength)) { + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_INT, arrayLength, + "uniform4iv", &rawLoc, + &numElementsToUpload)) + { + return; + } + + if (!ValidateSamplerUniformSetter("uniform4iv", loc, data[0]) || + !ValidateSamplerUniformSetter("uniform4iv", loc, data[1]) || + !ValidateSamplerUniformSetter("uniform4iv", loc, data[2]) || + !ValidateSamplerUniformSetter("uniform4iv", loc, data[3])) + { return; } - if (!ValidateSamplerUniformSetter("Uniform4iv", location_object, data[0]) || - !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[1]) || - !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[2]) || - !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[3])) + MakeContextCurrent(); + gl->fUniform4iv(rawLoc, numElementsToUpload, data); +} + +void +WebGLContext::Uniform1fv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLfloat* data) +{ + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_FLOAT, arrayLength, + "uniform1fv", &rawLoc, + &numElementsToUpload)) + { + return; + } + + MakeContextCurrent(); + gl->fUniform1fv(rawLoc, numElementsToUpload, data); +} + +void +WebGLContext::Uniform2fv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLfloat* data) +{ + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_FLOAT, arrayLength, + "uniform2fv", &rawLoc, + &numElementsToUpload)) { return; } MakeContextCurrent(); - gl->fUniform4iv(location, numElementsToUpload, data); -} - -void -WebGLContext::Uniform1fv_base(WebGLUniformLocation *location_object, - uint32_t arrayLength, const GLfloat* data) -{ - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformArraySetter("Uniform1fv", 1, location_object, location, - numElementsToUpload, arrayLength)) { - return; - } - MakeContextCurrent(); - gl->fUniform1fv(location, numElementsToUpload, data); + gl->fUniform2fv(rawLoc, numElementsToUpload, data); } void -WebGLContext::Uniform2fv_base(WebGLUniformLocation *location_object, - uint32_t arrayLength, const GLfloat* data) +WebGLContext::Uniform3fv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLfloat* data) { - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformArraySetter("Uniform2fv", 2, location_object, location, - numElementsToUpload, arrayLength)) { + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_FLOAT, arrayLength, + "uniform3fv", &rawLoc, + &numElementsToUpload)) + { return; } + MakeContextCurrent(); - gl->fUniform2fv(location, numElementsToUpload, data); -} - -void -WebGLContext::Uniform3fv_base(WebGLUniformLocation *location_object, - uint32_t arrayLength, const GLfloat* data) -{ - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformArraySetter("Uniform3fv", 3, location_object, location, - numElementsToUpload, arrayLength)) { - return; - } - MakeContextCurrent(); - gl->fUniform3fv(location, numElementsToUpload, data); + gl->fUniform3fv(rawLoc, numElementsToUpload, data); } void -WebGLContext::Uniform4fv_base(WebGLUniformLocation *location_object, - uint32_t arrayLength, const GLfloat* data) +WebGLContext::Uniform4fv_base(WebGLUniformLocation* loc, size_t arrayLength, + const GLfloat* data) { - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformArraySetter("Uniform4fv", 4, location_object, location, - numElementsToUpload, arrayLength)) { + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_FLOAT, arrayLength, + "uniform4fv", &rawLoc, + &numElementsToUpload)) + { return; } + MakeContextCurrent(); - gl->fUniform4fv(location, numElementsToUpload, data); + gl->fUniform4fv(rawLoc, numElementsToUpload, data); } +//////////////////////////////////////// +// Matrix + void -WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* location_object, - WebGLboolean aTranspose, uint32_t arrayLength, - const float* data) +WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* loc, bool transpose, + size_t arrayLength, const float* data) { - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformMatrixArraySetter("UniformMatrix2fv", 2, location_object, location, - numElementsToUpload, arrayLength, aTranspose)) { + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformMatrixArraySetter(loc, 2, LOCAL_GL_FLOAT, arrayLength, + transpose, "uniformMatrix2fv", + &rawLoc, &numElementsToUpload)) + { return; } + MakeContextCurrent(); - gl->fUniformMatrix2fv(location, numElementsToUpload, false, data); + gl->fUniformMatrix2fv(rawLoc, numElementsToUpload, false, data); } void -WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* location_object, - WebGLboolean aTranspose, uint32_t arrayLength, - const float* data) +WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* loc, bool transpose, + size_t arrayLength, const float* data) { - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformMatrixArraySetter("UniformMatrix3fv", 3, location_object, location, - numElementsToUpload, arrayLength, aTranspose)) { + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformMatrixArraySetter(loc, 3, LOCAL_GL_FLOAT, arrayLength, + transpose, "uniformMatrix3fv", + &rawLoc, &numElementsToUpload)) + { return; } + MakeContextCurrent(); - gl->fUniformMatrix3fv(location, numElementsToUpload, false, data); + gl->fUniformMatrix3fv(rawLoc, numElementsToUpload, false, data); } void -WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* location_object, - WebGLboolean aTranspose, uint32_t arrayLength, - const float* data) +WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* loc, bool transpose, + size_t arrayLength, const float* data) { - uint32_t numElementsToUpload; - GLint location; - if (!ValidateUniformMatrixArraySetter("UniformMatrix4fv", 4, location_object, location, - numElementsToUpload, arrayLength, aTranspose)) { + GLuint rawLoc; + GLsizei numElementsToUpload; + if (!ValidateUniformMatrixArraySetter(loc, 4, LOCAL_GL_FLOAT, arrayLength, + transpose, "uniformMatrix4fv", + &rawLoc, &numElementsToUpload)) + { return; } + MakeContextCurrent(); - gl->fUniformMatrix4fv(location, numElementsToUpload, false, data); + gl->fUniformMatrix4fv(rawLoc, numElementsToUpload, false, data); } +//////////////////////////////////////////////////////////////////////////////// + void WebGLContext::UseProgram(WebGLProgram *prog) { if (IsContextLost()) return; if (!ValidateObjectAllowNull("useProgram", prog)) return; @@ -4262,19 +4338,28 @@ void WebGLContext::Finish() { if (IsContextLost()) return; MakeContextCurrent(); gl->fFinish(); } void -WebGLContext::LineWidth(GLfloat width) { +WebGLContext::LineWidth(GLfloat width) +{ if (IsContextLost()) return; + + // Doing it this way instead of `if (width <= 0.0)` handles NaNs. + const bool isValid = width > 0.0; + if (!isValid) { + ErrorInvalidValue("lineWidth: `width` must be positive and non-zero."); + return; + } + MakeContextCurrent(); gl->fLineWidth(width); } void WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) { if (IsContextLost()) return;
--- a/dom/canvas/WebGLContextUtils.cpp +++ b/dom/canvas/WebGLContextUtils.cpp @@ -669,17 +669,17 @@ WebGLContext::IsTextureFormatCompressed( { return IsCompressedTextureFormat(format.get()); } GLenum WebGLContext::GetAndFlushUnderlyingGLErrors() { // Get and clear GL error in ALL cases. - GLenum error = gl->GetAndClearError(); + GLenum error = gl->fGetError(); // Only store in mUnderlyingGLError if is hasn't already recorded an // error. if (!mUnderlyingGLError) mUnderlyingGLError = error; return error; }
--- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -1317,119 +1317,178 @@ WebGLContext::ValidateAttribArraySetter( } if (arrayLength < cnt) { ErrorInvalidOperation("%s: array must be >= %d elements", name, cnt); return false; } return true; } -bool -WebGLContext::ValidateUniformArraySetter(const char* name, uint32_t expectedElemSize, WebGLUniformLocation *location_object, - GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength) +static bool +IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType) { - if (IsContextLost()) - return false; - if (!ValidateUniformLocation(name, location_object)) - return false; - location = location_object->Location(); - uint32_t uniformElemSize = location_object->ElementSize(); - if (expectedElemSize != uniformElemSize) { - ErrorInvalidOperation("%s: this function expected a uniform of element size %d," - " got a uniform of element size %d", name, - expectedElemSize, - uniformElemSize); + switch (uniformType) { + case LOCAL_GL_BOOL: + case LOCAL_GL_BOOL_VEC2: + case LOCAL_GL_BOOL_VEC3: + case LOCAL_GL_BOOL_VEC4: + return true; // GLfloat(0.0) sets a bool to false. + + case LOCAL_GL_INT: + case LOCAL_GL_SAMPLER_2D: + case LOCAL_GL_SAMPLER_CUBE: + case LOCAL_GL_INT_VEC2: + case LOCAL_GL_INT_VEC3: + case LOCAL_GL_INT_VEC4: + return setterType == LOCAL_GL_INT; + + case LOCAL_GL_FLOAT: + case LOCAL_GL_FLOAT_VEC2: + case LOCAL_GL_FLOAT_VEC3: + case LOCAL_GL_FLOAT_VEC4: + case LOCAL_GL_FLOAT_MAT2: + case LOCAL_GL_FLOAT_MAT3: + case LOCAL_GL_FLOAT_MAT4: + return setterType == LOCAL_GL_FLOAT; + + default: + MOZ_ASSERT(false); // should never get here return false; } - if (arrayLength == 0 || - arrayLength % expectedElemSize) - { - ErrorInvalidValue("%s: expected an array of length a multiple" - " of %d, got an array of length %d", name, - expectedElemSize, - arrayLength); +} + +static bool +CheckUniformSizeAndType(WebGLContext& webgl, WebGLUniformLocation* loc, + uint8_t setterElemSize, GLenum setterType, + const char* info) +{ + if (setterElemSize != loc->ElementSize()) { + webgl.ErrorInvalidOperation("%s: Bad uniform size: %i", info, + loc->ElementSize()); + return false; + } + + if (!IsUniformSetterTypeValid(setterType, loc->Info().type)) { + webgl.ErrorInvalidOperation("%s: Bad uniform type: %i", info, + loc->Info().type); return false; } - const WebGLUniformInfo& info = location_object->Info(); - if (!info.isArray && - arrayLength != expectedElemSize) { - ErrorInvalidOperation("%s: expected an array of length exactly" - " %d (since this uniform is not an array" - " uniform), got an array of length %d", name, - expectedElemSize, - arrayLength); + + return true; +} + +static bool +CheckUniformArrayLength(WebGLContext& webgl, WebGLUniformLocation* loc, + uint8_t setterElemSize, size_t setterArraySize, + const char* info) +{ + if (setterArraySize == 0 || + setterArraySize % setterElemSize) + { + webgl.ErrorInvalidValue("%s: expected an array of length a multiple of" + " %d, got an array of length %d.", info, + setterElemSize, setterArraySize); return false; } - numElementsToUpload = - std::min(info.arraySize, arrayLength / expectedElemSize); + + if (!loc->Info().isArray && + setterArraySize != setterElemSize) + { + webgl.ErrorInvalidOperation("%s: expected an array of length exactly %d" + " (since this uniform is not an array" + " uniform), got an array of length %d.", + info, setterElemSize, setterArraySize); + return false; + } + return true; } bool -WebGLContext::ValidateUniformMatrixArraySetter(const char* name, int dim, WebGLUniformLocation *location_object, - GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength, - WebGLboolean aTranspose) +WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc, + uint8_t setterElemSize, GLenum setterType, + const char* info, GLuint* out_rawLoc) { - uint32_t expectedElemSize = (dim)*(dim); if (IsContextLost()) return false; - if (!ValidateUniformLocation(name, location_object)) - return false; - location = location_object->Location(); - uint32_t uniformElemSize = location_object->ElementSize(); - if (expectedElemSize != uniformElemSize) { - ErrorInvalidOperation("%s: this function expected a uniform of element size %d," - " got a uniform of element size %d", name, - expectedElemSize, - uniformElemSize); - return false; - } - if (arrayLength == 0 || - arrayLength % expectedElemSize) - { - ErrorInvalidValue("%s: expected an array of length a multiple" - " of %d, got an array of length %d", name, - expectedElemSize, - arrayLength); + + if (!ValidateUniformLocation(info, loc)) return false; - } - const WebGLUniformInfo& info = location_object->Info(); - if (!info.isArray && - arrayLength != expectedElemSize) { - ErrorInvalidOperation("%s: expected an array of length exactly" - " %d (since this uniform is not an array" - " uniform), got an array of length %d", name, - expectedElemSize, - arrayLength); + + if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info)) return false; - } - if (aTranspose) { - ErrorInvalidValue("%s: transpose must be FALSE as per the " - "OpenGL ES 2.0 spec", name); - return false; - } - numElementsToUpload = - std::min(info.arraySize, arrayLength / (expectedElemSize)); + + *out_rawLoc = loc->Location(); return true; } bool -WebGLContext::ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location) +WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc, + uint8_t setterElemSize, GLenum setterType, + size_t setterArraySize, + const char* info, GLuint* out_rawLoc, + GLsizei* out_numElementsToUpload) { if (IsContextLost()) return false; - if (!ValidateUniformLocation(name, location_object)) + + if (!ValidateUniformLocation(info, loc)) + return false; + + if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info)) return false; - location = location_object->Location(); + + if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize, + info)) + { + return false; + } + + *out_rawLoc = loc->Location(); + *out_numElementsToUpload = std::min((size_t)loc->Info().arraySize, + setterArraySize / setterElemSize); return true; } -bool WebGLContext::ValidateAttribIndex(GLuint index, const char *info) +bool +WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc, + uint8_t setterDims, + GLenum setterType, + size_t setterArraySize, + bool setterTranspose, + const char* info, + GLuint* out_rawLoc, + GLsizei* out_numElementsToUpload) { - return mBoundVertexArray->EnsureAttrib(index, info); + uint8_t setterElemSize = setterDims * setterDims; + + if (IsContextLost()) + return false; + + if (!ValidateUniformLocation(info, loc)) + return false; + + if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info)) + return false; + + if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize, + info)) + { + return false; + } + + if (setterTranspose) { + ErrorInvalidValue("%s: `transpose` must be false.", info); + return false; + } + + *out_rawLoc = loc->Location(); + *out_numElementsToUpload = std::min((size_t)loc->Info().arraySize, + setterArraySize / setterElemSize); + return true; } bool WebGLContext::ValidateStencilParamsForDrawCall() { const char *msg = "%s set different front and back stencil %s. Drawing in this configuration is not allowed."; if (mStencilRefFront != mStencilRefBack) { ErrorInvalidOperation(msg, "stencilFuncSeparate", "reference values"); return false; @@ -1615,41 +1674,32 @@ WebGLContext::InitAndValidateGL() mGLMaxFragmentUniformVectors /= 4; gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors); mGLMaxVertexUniformVectors /= 4; // we are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS and GL_MAX_FRAGMENT_INPUT_COMPONENTS, // however these constants only entered the OpenGL standard at OpenGL 3.2. So we will try reading, // and check OpenGL error for INVALID_ENUM. - // before we start, we check that no error already occurred, to prevent hiding it in our subsequent error handling - error = gl->GetAndClearError(); - if (error != LOCAL_GL_NO_ERROR) { - GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); - return false; - } - // On the public_webgl list, "problematic GetParameter pnames" thread, the following formula was given: // mGLMaxVaryingVectors = min (GL_MAX_VERTEX_OUTPUT_COMPONENTS, GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4 - GLint maxVertexOutputComponents, - minFragmentInputComponents; - gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, &maxVertexOutputComponents); - gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, &minFragmentInputComponents); + GLint maxVertexOutputComponents = 0; + GLint maxFragmentInputComponents = 0; - error = gl->GetAndClearError(); - switch (error) { - case LOCAL_GL_NO_ERROR: - mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, minFragmentInputComponents) / 4; - break; - case LOCAL_GL_INVALID_ENUM: - mGLMaxVaryingVectors = 16; // = 64/4, 64 is the min value for maxVertexOutputComponents in OpenGL 3.2 spec - break; - default: - GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); - return false; + const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, + &maxVertexOutputComponents) && + gl->GetPotentialInteger(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, + &maxFragmentInputComponents)); + + if (ok) { + mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, + maxFragmentInputComponents) / 4; + } else { + mGLMaxVaryingVectors = 16; + // = 64/4, 64 is the min value for maxVertexOutputComponents in OpenGL 3.2 spec } } } // Always 1 for GLES2 mMaxFramebufferColorAttachments = 1; if (!gl->IsGLES()) { @@ -1689,19 +1739,20 @@ WebGLContext::InitAndValidateGL() GenerateWarning("GLSL translator initialization failed!"); return false; } } // Mesa can only be detected with the GL_VERSION string, of the form "2.1 Mesa 7.11.0" mIsMesa = strstr((const char *)(gl->fGetString(LOCAL_GL_VERSION)), "Mesa"); - // notice that the point of calling GetAndClearError here is not only to check for error, - // it is also to reset the error flags so that a subsequent WebGL getError call will give the correct result. - error = gl->GetAndClearError(); + // Notice that the point of calling fGetError here is not only to check for + // errors, but also to reset the error flags so that a subsequent WebGL + // getError call will give the correct result. + error = gl->fGetError(); if (error != LOCAL_GL_NO_ERROR) { GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); return false; } if (IsWebGL2() && !InitWebGL2()) {
--- a/dom/canvas/WebGLContextVertices.cpp +++ b/dom/canvas/WebGLContextVertices.cpp @@ -15,22 +15,46 @@ #include "WebGLTexture.h" #include "WebGLUniformInfo.h" #include "WebGLVertexArray.h" #include "WebGLVertexAttribData.h" using namespace mozilla; using namespace dom; +static bool +CheckAttribIndex(WebGLContext& webgl, GLuint index, const char* info) +{ + if (index >= webgl.MaxVertexAttribs()) { + if (index == GLuint(-1)) { + webgl.ErrorInvalidValue("%s: -1 is not a valid `index`. This value" + " probably comes from a getAttribLocation()" + " call, where this return value -1 means" + " that the passed name didn't correspond to" + " an active attribute in the specified" + " program.", info); + } else { + webgl.ErrorInvalidValue("%s: `index` must be less than" + " MAX_VERTEX_ATTRIBS.", info); + } + return false; + } + + return true; +} + void WebGLContext::VertexAttrib1f(GLuint index, GLfloat x0) { if (IsContextLost()) return; + if (!CheckAttribIndex(*this, index, "vertexAttrib1f")) + return; + MakeContextCurrent(); if (index) { gl->fVertexAttrib1f(index, x0); } else { mVertexAttrib0Vector[0] = x0; mVertexAttrib0Vector[1] = 0; mVertexAttrib0Vector[2] = 0; @@ -41,16 +65,19 @@ WebGLContext::VertexAttrib1f(GLuint inde } void WebGLContext::VertexAttrib2f(GLuint index, GLfloat x0, GLfloat x1) { if (IsContextLost()) return; + if (!CheckAttribIndex(*this, index, "vertexAttrib2f")) + return; + MakeContextCurrent(); if (index) { gl->fVertexAttrib2f(index, x0, x1); } else { mVertexAttrib0Vector[0] = x0; mVertexAttrib0Vector[1] = x1; mVertexAttrib0Vector[2] = 0; @@ -61,16 +88,19 @@ WebGLContext::VertexAttrib2f(GLuint inde } void WebGLContext::VertexAttrib3f(GLuint index, GLfloat x0, GLfloat x1, GLfloat x2) { if (IsContextLost()) return; + if (!CheckAttribIndex(*this, index, "vertexAttrib3f")) + return; + MakeContextCurrent(); if (index) { gl->fVertexAttrib3f(index, x0, x1, x2); } else { mVertexAttrib0Vector[0] = x0; mVertexAttrib0Vector[1] = x1; mVertexAttrib0Vector[2] = x2; @@ -82,158 +112,178 @@ WebGLContext::VertexAttrib3f(GLuint inde void WebGLContext::VertexAttrib4f(GLuint index, GLfloat x0, GLfloat x1, GLfloat x2, GLfloat x3) { if (IsContextLost()) return; + if (!CheckAttribIndex(*this, index, "vertexAttrib4f")) + return; + MakeContextCurrent(); if (index) { gl->fVertexAttrib4f(index, x0, x1, x2, x3); } else { mVertexAttrib0Vector[0] = x0; mVertexAttrib0Vector[1] = x1; mVertexAttrib0Vector[2] = x2; mVertexAttrib0Vector[3] = x3; if (gl->IsGLES()) gl->fVertexAttrib4f(index, x0, x1, x2, x3); } } void -WebGLContext::VertexAttrib1fv_base(GLuint idx, uint32_t arrayLength, +WebGLContext::VertexAttrib1fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr) { if (!ValidateAttribArraySetter("VertexAttrib1fv", 1, arrayLength)) return; + if (!CheckAttribIndex(*this, index, "vertexAttrib1fv")) + return; + MakeContextCurrent(); - if (idx) { - gl->fVertexAttrib1fv(idx, ptr); + if (index) { + gl->fVertexAttrib1fv(index, ptr); } else { mVertexAttrib0Vector[0] = ptr[0]; mVertexAttrib0Vector[1] = GLfloat(0); mVertexAttrib0Vector[2] = GLfloat(0); mVertexAttrib0Vector[3] = GLfloat(1); if (gl->IsGLES()) - gl->fVertexAttrib1fv(idx, ptr); + gl->fVertexAttrib1fv(index, ptr); } } void -WebGLContext::VertexAttrib2fv_base(GLuint idx, uint32_t arrayLength, +WebGLContext::VertexAttrib2fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr) { if (!ValidateAttribArraySetter("VertexAttrib2fv", 2, arrayLength)) return; + if (!CheckAttribIndex(*this, index, "vertexAttrib2fv")) + return; + MakeContextCurrent(); - if (idx) { - gl->fVertexAttrib2fv(idx, ptr); + if (index) { + gl->fVertexAttrib2fv(index, ptr); } else { mVertexAttrib0Vector[0] = ptr[0]; mVertexAttrib0Vector[1] = ptr[1]; mVertexAttrib0Vector[2] = GLfloat(0); mVertexAttrib0Vector[3] = GLfloat(1); if (gl->IsGLES()) - gl->fVertexAttrib2fv(idx, ptr); + gl->fVertexAttrib2fv(index, ptr); } } void -WebGLContext::VertexAttrib3fv_base(GLuint idx, uint32_t arrayLength, +WebGLContext::VertexAttrib3fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr) { if (!ValidateAttribArraySetter("VertexAttrib3fv", 3, arrayLength)) return; + if (!CheckAttribIndex(*this, index, "vertexAttrib3fv")) + return; + MakeContextCurrent(); - if (idx) { - gl->fVertexAttrib3fv(idx, ptr); + if (index) { + gl->fVertexAttrib3fv(index, ptr); } else { mVertexAttrib0Vector[0] = ptr[0]; mVertexAttrib0Vector[1] = ptr[1]; mVertexAttrib0Vector[2] = ptr[2]; mVertexAttrib0Vector[3] = GLfloat(1); if (gl->IsGLES()) - gl->fVertexAttrib3fv(idx, ptr); + gl->fVertexAttrib3fv(index, ptr); } } void -WebGLContext::VertexAttrib4fv_base(GLuint idx, uint32_t arrayLength, +WebGLContext::VertexAttrib4fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr) { if (!ValidateAttribArraySetter("VertexAttrib4fv", 4, arrayLength)) return; + if (!CheckAttribIndex(*this, index, "vertexAttrib4fv")) + return; + MakeContextCurrent(); - if (idx) { - gl->fVertexAttrib4fv(idx, ptr); + if (index) { + gl->fVertexAttrib4fv(index, ptr); } else { mVertexAttrib0Vector[0] = ptr[0]; mVertexAttrib0Vector[1] = ptr[1]; mVertexAttrib0Vector[2] = ptr[2]; mVertexAttrib0Vector[3] = ptr[3]; if (gl->IsGLES()) - gl->fVertexAttrib4fv(idx, ptr); + gl->fVertexAttrib4fv(index, ptr); } } void WebGLContext::EnableVertexAttribArray(GLuint index) { if (IsContextLost()) return; - if (!ValidateAttribIndex(index, "enableVertexAttribArray")) + if (!CheckAttribIndex(*this, index, "enableVertexAttribArray")) return; MakeContextCurrent(); InvalidateBufferFetching(); gl->fEnableVertexAttribArray(index); - MOZ_ASSERT(mBoundVertexArray->HasAttrib(index)); // should have been validated earlier + + MOZ_ASSERT(mBoundVertexArray); + mBoundVertexArray->EnsureAttrib(index); mBoundVertexArray->mAttribs[index].enabled = true; } void WebGLContext::DisableVertexAttribArray(GLuint index) { if (IsContextLost()) return; - if (!ValidateAttribIndex(index, "disableVertexAttribArray")) + if (!CheckAttribIndex(*this, index, "disableVertexAttribArray")) return; MakeContextCurrent(); InvalidateBufferFetching(); if (index || gl->IsGLES()) gl->fDisableVertexAttribArray(index); - MOZ_ASSERT(mBoundVertexArray->HasAttrib(index)); // should have been validated earlier + MOZ_ASSERT(mBoundVertexArray); + mBoundVertexArray->EnsureAttrib(index); mBoundVertexArray->mAttribs[index].enabled = false; } - JS::Value WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname, ErrorResult& rv) { if (IsContextLost()) return JS::NullValue(); - if (!ValidateAttribIndex(index, "getVertexAttrib")) + if (!CheckAttribIndex(*this, index, "getVertexAttrib")) return JS::NullValue(); + MOZ_ASSERT(mBoundVertexArray); + mBoundVertexArray->EnsureAttrib(index); + MakeContextCurrent(); switch (pname) { case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].buf.get(), rv); } @@ -295,45 +345,49 @@ WebGLContext::GetVertexAttrib(JSContext* return JS::BooleanValue(mBoundVertexArray->mAttribs[index].normalized); } default: break; } ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname); - return JS::NullValue(); } WebGLsizeiptr WebGLContext::GetVertexAttribOffset(GLuint index, GLenum pname) { if (IsContextLost()) return 0; - if (!ValidateAttribIndex(index, "getVertexAttribOffset")) + if (!CheckAttribIndex(*this, index, "getVertexAttribOffset")) return 0; if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) { ErrorInvalidEnum("getVertexAttribOffset: bad parameter"); return 0; } + MOZ_ASSERT(mBoundVertexArray); + mBoundVertexArray->EnsureAttrib(index); return mBoundVertexArray->mAttribs[index].byteOffset; } void WebGLContext::VertexAttribPointer(GLuint index, GLint size, GLenum type, WebGLboolean normalized, GLsizei stride, WebGLintptr byteOffset) { if (IsContextLost()) return; + if (!CheckAttribIndex(*this, index, "vertexAttribPointer")) + return; + if (mBoundArrayBuffer == nullptr) return ErrorInvalidOperation("vertexAttribPointer: must have valid GL_ARRAY_BUFFER binding"); GLsizei requiredAlignment = 1; switch (type) { case LOCAL_GL_BYTE: case LOCAL_GL_UNSIGNED_BYTE: requiredAlignment = 1; @@ -348,19 +402,18 @@ WebGLContext::VertexAttribPointer(GLuint break; default: return ErrorInvalidEnumInfo("vertexAttribPointer: type", type); } // requiredAlignment should always be a power of two. GLsizei requiredAlignmentMask = requiredAlignment - 1; - if (!ValidateAttribIndex(index, "vertexAttribPointer")) { - return; - } + MOZ_ASSERT(mBoundVertexArray); + mBoundVertexArray->EnsureAttrib(index); if (size < 1 || size > 4) return ErrorInvalidValue("vertexAttribPointer: invalid element size"); if (stride < 0 || stride > 255) // see WebGL spec section 6.6 "Vertex Attribute Data Stride" return ErrorInvalidValue("vertexAttribPointer: negative or too large stride"); if (byteOffset < 0) @@ -401,19 +454,21 @@ WebGLContext::VertexAttribPointer(GLuint } void WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor) { if (IsContextLost()) return; - if (!ValidateAttribIndex(index, "vertexAttribDivisor")) { + if (!CheckAttribIndex(*this, index, "vertexAttribDivisor")) return; - } + + MOZ_ASSERT(mBoundVertexArray); + mBoundVertexArray->EnsureAttrib(index); WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index]; vd.divisor = divisor; InvalidateBufferFetching(); MakeContextCurrent();
--- a/dom/canvas/WebGLVertexArray.cpp +++ b/dom/canvas/WebGLVertexArray.cpp @@ -44,34 +44,24 @@ WebGLVertexArray::Delete() { DeleteImpl(); LinkedListElement<WebGLVertexArray>::removeFrom(mContext->mVertexArrays); mElementArrayBuffer = nullptr; mAttribs.Clear(); } -bool -WebGLVertexArray::EnsureAttrib(GLuint index, const char *info) +void +WebGLVertexArray::EnsureAttrib(GLuint index) { - if (index >= GLuint(mContext->mGLMaxVertexAttribs)) { - if (index == GLuint(-1)) { - mContext->ErrorInvalidValue("%s: index -1 is invalid. That probably comes from a getAttribLocation() call, " - "where this return value -1 means that the passed name didn't correspond to an active attribute in " - "the specified program.", info); - } else { - mContext->ErrorInvalidValue("%s: index %d is out of range", info, index); - } - return false; - } - else if (index >= mAttribs.Length()) { + MOZ_ASSERT(index < GLuint(mContext->mGLMaxVertexAttribs)); + + if (index >= mAttribs.Length()) { mAttribs.SetLength(index + 1); } - - return true; } NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLVertexArray, mAttribs, mElementArrayBuffer) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLVertexArray, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLVertexArray, Release)
--- a/dom/canvas/WebGLVertexArray.h +++ b/dom/canvas/WebGLVertexArray.h @@ -56,17 +56,17 @@ public: virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLVertexArray) // ------------------------------------------------------------------------- // MEMBER FUNCTIONS - bool EnsureAttrib(GLuint index, const char *info); + void EnsureAttrib(GLuint index); bool HasAttrib(GLuint index) { return index < mAttribs.Length(); } bool IsAttribArrayEnabled(GLuint index) { return HasAttrib(index) && mAttribs[index].enabled; }
--- a/dom/canvas/compiledtest/moz.build +++ b/dom/canvas/compiledtest/moz.build @@ -1,22 +1,15 @@ # -*- 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/. -CppUnitTests([ +GeckoCppUnitTests([ 'TestWebGLElementArrayCache', ]) FAIL_ON_WARNINGS = True LOCAL_INCLUDES += [ '../', ] - -USE_LIBS += [ - 'mozalloc', - 'nspr', - 'xpcomglue_s', - 'xul', -]
--- a/dom/canvas/test/webgl-conformance/mochi-single.html +++ b/dom/canvas/test/webgl-conformance/mochi-single.html @@ -125,19 +125,17 @@ function GetExpectedTestFailSet() { failSet['conformance/textures/tex-image-with-format-and-type.html'] = true; failSet['conformance/textures/tex-sub-image-2d.html'] = true; failSet['conformance/textures/texture-mips.html'] = true; failSet['conformance/textures/texture-npot.html'] = true; failSet['conformance/textures/texture-size-cube-maps.html'] = true; } else { // Android 2.3 slaves. failSet['conformance/glsl/functions/glsl-function-sin.html'] = true; - failSet['conformance/misc/error-reporting.html'] = true; failSet['conformance/misc/object-deletion-behaviour.html'] = true; - failSet['conformance/programs/get-active-test.html'] = true; failSet['conformance/textures/tex-image-and-sub-image-2d-with-video.html'] = true; failSet['conformance/textures/texture-mips.html'] = true; failSet['conformance/textures/texture-npot.html'] = true; } break; case DriverInfo.OS.B2G: failSet['conformance/context/context-attributes-alpha-depth-stencil-antialias.html'] = true;
--- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -263,17 +263,18 @@ protected: // True when popup control check should rely on event.type, not // WidgetEvent.message. bool mWantsPopupControlCheck; }; class MOZ_STACK_CLASS WantsPopupControlCheck { public: - WantsPopupControlCheck(nsIDOMEvent* aEvent) : mEvent(aEvent->InternalDOMEvent()) + explicit WantsPopupControlCheck(nsIDOMEvent* aEvent) : + mEvent(aEvent->InternalDOMEvent()) { mOriginalWantsPopupControlCheck = mEvent->GetWantsPopupControlCheck(); mEvent->SetWantsPopupControlCheck(mEvent->IsTrusted()); } ~WantsPopupControlCheck() { mEvent->SetWantsPopupControlCheck(mOriginalWantsPopupControlCheck);
--- a/dom/events/MessageEvent.cpp +++ b/dom/events/MessageEvent.cpp @@ -6,16 +6,17 @@ #include "mozilla/dom/MessageEvent.h" #include "mozilla/dom/MessageEventBinding.h" #include "mozilla/dom/MessagePort.h" #include "mozilla/dom/MessagePortBinding.h" #include "mozilla/dom/MessagePortList.h" #include "mozilla/HoldDropJSObjects.h" #include "jsapi.h" +#include "nsGlobalWindow.h" // So we can assign an nsGlobalWindow* to mWindowSource namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(MessageEvent) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessageEvent, Event) tmp->mData = JSVAL_VOID; @@ -136,18 +137,18 @@ MessageEvent::Constructor(const GlobalOb event->mOrigin = aParam.mOrigin.Value(); } if (aParam.mLastEventId.WasPassed()) { event->mLastEventId = aParam.mLastEventId.Value(); } if (!aParam.mSource.IsNull()) { - if (aParam.mSource.Value().IsWindowProxy()) { - event->mWindowSource = aParam.mSource.Value().GetAsWindowProxy(); + if (aParam.mSource.Value().IsWindow()) { + event->mWindowSource = aParam.mSource.Value().GetAsWindow(); } else { event->mPortSource = aParam.mSource.Value().GetAsMessagePort(); } MOZ_ASSERT(event->mWindowSource || event->mPortSource); } if (aParam.mPorts.WasPassed() && !aParam.mPorts.Value().IsNull()) {
--- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -66,17 +66,17 @@ private: class MainThreadFetchResolver MOZ_FINAL : public FetchDriverObserver { nsRefPtr<Promise> mPromise; nsRefPtr<InternalResponse> mInternalResponse; NS_DECL_OWNINGTHREAD public: - MainThreadFetchResolver(Promise* aPromise); + explicit MainThreadFetchResolver(Promise* aPromise); void OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE; private: ~MainThreadFetchResolver(); }; @@ -183,17 +183,17 @@ MainThreadFetchResolver::~MainThreadFetc { NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); } class WorkerFetchResponseRunnable : public WorkerRunnable { nsRefPtr<WorkerFetchResolver> mResolver; public: - WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver) + explicit WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver) : WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount) , mResolver(aResolver) { } bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE {
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -1782,17 +1782,21 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted already_AddRefed<DOMMediaStream> HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded) { nsIDOMWindow* window = OwnerDoc()->GetInnerWindow(); if (!window) { return nullptr; } - +#ifdef MOZ_EME + if (ContainsRestrictedContent()) { + return nullptr; + } +#endif OutputMediaStream* out = mOutputStreams.AppendElement(); #ifdef DEBUG // Estimate hints based on the type of the media element // under the preference media.capturestream_hints for the // debug builds only. This allows WebRTC Peer Connection // to behave appropriately when media streams generated // via mozCaptureStream*() are added to the Peer Connection. // This functionality is planned to be used as part of Audio @@ -3987,20 +3991,31 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayC #ifdef MOZ_EME MediaKeys* HTMLMediaElement::GetMediaKeys() const { return mMediaKeys; } +bool +HTMLMediaElement::ContainsRestrictedContent() +{ + return GetMediaKeys() != nullptr; +} + already_AddRefed<Promise> HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys, ErrorResult& aRv) { + if (MozAudioCaptured()) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(OwnerDoc()->GetInnerWindow()); if (!global) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } nsRefPtr<Promise> promise = Promise::Create(global, aRv); if (aRv.Failed()) { @@ -4025,16 +4040,18 @@ HTMLMediaElement::SetMediaKeys(mozilla:: if (NS_FAILED(mMediaKeys->Bind(this))) { promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); mMediaKeys = nullptr; return promise.forget(); } if (mDecoder) { mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy()); } + // Update the same-origin status. + NotifyDecoderPrincipalChanged(); } promise->MaybeResolve(JS::UndefinedHandleValue); return promise.forget(); } MediaWaitingFor HTMLMediaElement::WaitingFor() const {
--- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -545,16 +545,17 @@ public: bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE; // Returns the principal of the "top level" document; the origin displayed // in the URL bar of the browser window. already_AddRefed<nsIPrincipal> GetTopLevelPrincipal(); + bool ContainsRestrictedContent(); #endif // MOZ_EME bool MozAutoplayEnabled() const { return mAutoplayEnabled; } already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
--- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -724,17 +724,17 @@ private: // Nothing needs to be done here. } }; class EmptyBlobImpl MOZ_FINAL : public FileImplBase { public: - EmptyBlobImpl(const nsAString& aContentType) + explicit EmptyBlobImpl(const nsAString& aContentType) : FileImplBase(aContentType, 0) { mImmutable = true; } EmptyBlobImpl(const nsAString& aName, const nsAString& aContentType, uint64_t aLastModifiedDate) @@ -835,17 +835,17 @@ struct MOZ_STACK_CLASS CreateBlobImplMet { nsString mContentType; nsString mName; uint64_t mLength; uint64_t mLastModifiedDate; bool mHasRecursed; const bool mIsSameProcessActor; - CreateBlobImplMetadata(bool aIsSameProcessActor) + explicit CreateBlobImplMetadata(bool aIsSameProcessActor) : mLength(0) , mLastModifiedDate(0) , mHasRecursed(false) , mIsSameProcessActor(aIsSameProcessActor) { MOZ_COUNT_CTOR(CreateBlobImplMetadata); mName.SetIsVoid(true); @@ -1673,17 +1673,17 @@ class BlobChild::RemoteBlobImpl::CreateS Monitor mMonitor; nsRefPtr<RemoteBlobImpl> mRemoteBlobImpl; nsRefPtr<RemoteInputStream> mInputStream; const uint64_t mStart; const uint64_t mLength; bool mDone; public: - CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl); + explicit CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl); nsresult GetStream(nsIInputStream** aInputStream); NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIRUNNABLE private:
--- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -294,16 +294,18 @@ ThreadedDriver::RunThread() mNextStateComputedTime = mGraphImpl->RoundUpToNextAudioBlock( nextCurrentTime + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS)); STREAM_LOG(PR_LOG_DEBUG, ("interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)mStateComputedTime, (long)mNextStateComputedTime)); + mGraphImpl->mFlushSourcesNow = mGraphImpl->mFlushSourcesOnNextIteration; + mGraphImpl->mFlushSourcesOnNextIteration = false; stillProcessing = mGraphImpl->OneIteration(prevCurrentTime, nextCurrentTime, StateComputedTime(), mNextStateComputedTime); if (mNextDriver && stillProcessing) { STREAM_LOG(PR_LOG_DEBUG, ("Switching to AudioCallbackDriver")); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd, @@ -791,17 +793,19 @@ bool AudioCallbackDriver::OSXDeviceSwitchingWorkaround() { MonitorAutoLock mon(GraphImpl()->GetMonitor()); if (mSelfReference) { // Apparently, depending on the osx version, on device switch, the // callback is called "some" number of times, and then stops being called, // and then gets called again. 10 is to be safe, it's a low-enough number // of milliseconds anyways (< 100ms) + //STREAM_LOG(PR_LOG_DEBUG, ("Callbacks during switch: %d", mCallbackReceivedWhileSwitching+1)); if (mCallbackReceivedWhileSwitching++ >= 10) { + STREAM_LOG(PR_LOG_DEBUG, ("Got %d callbacks, switching back to CallbackDriver", mCallbackReceivedWhileSwitching)); // If we have a self reference, we have fallen back temporarily on a // system clock driver, but we just got called back, that means the osx // audio backend has switched to the new device. // Ask the graph to switch back to the previous AudioCallbackDriver // (`this`), and when the graph has effectively switched, we can drop // the self reference and unref the SystemClockDriver we fallen back on. if (GraphImpl()->CurrentDriver() == this) { mSelfReference.Drop(this); @@ -1027,18 +1031,20 @@ AudioCallbackDriver::DeviceChangedCallba // SourceMediaStream. if (!GraphImpl()->Running()) { return; } if (mSelfReference) { return; } + STREAM_LOG(PR_LOG_ERROR, ("Switching to SystemClockDriver during output switch")); mSelfReference.Take(this); mCallbackReceivedWhileSwitching = 0; + mGraphImpl->mFlushSourcesOnNextIteration = true; mNextDriver = new SystemClockDriver(GraphImpl()); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd, mStateComputedTime, mNextStateComputedTime); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); #endif }
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -2970,18 +2970,18 @@ void MediaDecoderStateMachine::StartBuff // We can't just directly send an asynchronous runnable that // eventually fires the "waiting" event. The problem is that // there might be pending main-thread events, such as "data // received" notifications, that mean we're not actually still // buffering by the time this runnable executes. So instead // we just trigger UpdateReadyStateForData; when it runs, it // will check the current state and decide whether to tell // the element we're buffering or not. + SetState(DECODER_STATE_BUFFERING); UpdateReadyState(); - SetState(DECODER_STATE_BUFFERING); DECODER_LOG("Changed state from DECODING to BUFFERING, decoded for %.3lfs", decodeDuration.ToSeconds()); #ifdef PR_LOGGING MediaDecoder::Statistics stats = mDecoder->GetStatistics(); DECODER_LOG("Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s", stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)", stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"); #endif
--- a/dom/media/MediaSegment.h +++ b/dom/media/MediaSegment.h @@ -84,16 +84,20 @@ public: */ virtual void AppendSlice(const MediaSegment& aSource, TrackTicks aStart, TrackTicks aEnd) = 0; /** * Replace all contents up to aDuration with null data. */ virtual void ForgetUpTo(TrackTicks aDuration) = 0; /** + * Forget all data buffered after a given point + */ + virtual void FlushAfter(TrackTicks aNewEnd) = 0; + /** * Insert aDuration of null data at the start of the segment. */ virtual void InsertNullDataAtStart(TrackTicks aDuration) = 0; /** * Insert aDuration of null data at the end of the segment. */ virtual void AppendNullData(TrackTicks aDuration) = 0; /** @@ -171,16 +175,39 @@ public: mDuration += extraToForget; } return; } RemoveLeading(aDuration, 0); mChunks.InsertElementAt(0)->SetNull(aDuration); mDuration += aDuration; } + virtual void FlushAfter(TrackTicks aNewEnd) + { + if (mChunks.IsEmpty()) { + return; + } + + if (mChunks[0].IsNull()) { + TrackTicks extraToKeep = aNewEnd - mChunks[0].GetDuration(); + if (extraToKeep < 0) { + // reduce the size of the Null, get rid of everthing else + mChunks[0].SetNull(aNewEnd); + extraToKeep = 0; + } + RemoveTrailing(extraToKeep, 1); + } else { + if (aNewEnd > mDuration) { + NS_ASSERTION(aNewEnd <= mDuration, "can't add data in FlushAfter"); + return; + } + RemoveTrailing(aNewEnd, 0); + } + mDuration = aNewEnd; + } virtual void InsertNullDataAtStart(TrackTicks aDuration) { if (aDuration <= 0) { return; } if (!mChunks.IsEmpty() && mChunks[0].IsNull()) { mChunks[0].mDuration += aDuration; } else { @@ -345,16 +372,38 @@ protected: } t -= c->GetDuration(); chunksToRemove = i + 1 - aStartIndex; } mChunks.RemoveElementsAt(aStartIndex, chunksToRemove); mDuration -= aDuration - t; } + void RemoveTrailing(TrackTicks aKeep, uint32_t aStartIndex) + { + NS_ASSERTION(aKeep >= 0, "Can't keep negative duration"); + TrackTicks t = aKeep; + uint32_t i; + for (i = aStartIndex; i < mChunks.Length(); ++i) { + Chunk* c = &mChunks[i]; + if (c->GetDuration() > t) { + c->SliceTo(0, t); + break; + } + t -= c->GetDuration(); + if (t == 0) { + break; + } + } + if (i+1 < mChunks.Length()) { + mChunks.RemoveElementsAt(i+1, mChunks.Length() - (i+1)); + } + // Caller must adjust mDuration + } + nsTArray<Chunk> mChunks; #ifdef MOZILLA_INTERNAL_API mozilla::TimeStamp mTimeStamp; #endif }; }
--- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -1434,16 +1434,17 @@ MediaStreamGraphImpl::OneIteration(Graph // stream is responsible for calling Destroy on them. return false; } CurrentDriver()->WaitForNextIteration(); SwapMessageQueues(); } + mFlushSourcesNow = false; return true; } void MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate) { mMonitor.AssertCurrentThreadOwns(); @@ -2717,16 +2718,18 @@ MediaStreamGraphImpl::MediaStreamGraphIm , mNeedAnotherIteration(false) , mGraphDriverAsleep(false) , mMonitor("MediaStreamGraphImpl") , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED) , mEndTime(GRAPH_TIME_MAX) , mSampleRate(aSampleRate) , mForceShutDown(false) , mPostedRunInStableStateEvent(false) + , mFlushSourcesNow(false) + , mFlushSourcesOnNextIteration(false) , mDetectedNotRunning(false) , mPostedRunInStableState(false) , mRealtime(aRealtime) , mNonRealtimeProcessing(false) , mStreamOrderDirty(false) , mLatencyLog(AsyncLatencyLogger::Get()) #ifdef MOZ_WEBRTC , mFarendObserverRef(nullptr)
--- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -605,16 +605,23 @@ public: */ bool mForceShutDown; /** * True when we have posted an event to the main thread to run * RunInStableState() and the event hasn't run yet. */ bool mPostedRunInStableStateEvent; + /** + * Used to flush any accumulated data when the output streams + * may have stalled (on Mac after an output device change) + */ + bool mFlushSourcesNow; + bool mFlushSourcesOnNextIteration; + // Main thread only /** * Messages posted by the current event loop task. These are forwarded to * the media graph thread during RunInStableState. We can't forward them * immediately because we want all messages between stable states to be * processed as an atomic batch. */
--- a/dom/media/StreamBuffer.h +++ b/dom/media/StreamBuffer.h @@ -149,16 +149,22 @@ public: MediaSegment* RemoveSegment() { return mSegment.forget(); } void ForgetUpTo(TrackTicks aTime) { mSegment->ForgetUpTo(aTime); } + void FlushAfter(TrackTicks aNewEnd) + { + // Forget everything after a given endpoint + // a specified amount + mSegment->FlushAfter(aNewEnd); + } size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t amount = aMallocSizeOf(this); if (mSegment) { amount += mSegment->SizeOfIncludingThis(aMallocSizeOf); } return amount;
--- a/dom/media/TrackUnionStream.cpp +++ b/dom/media/TrackUnionStream.cpp @@ -258,18 +258,19 @@ TrackUnionStream::TrackUnionStream(DOMMe StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd); TrackTicks inputTrackEndPoint = aInputTrack->GetEnd(); if (aInputTrack->IsEnded() && aInputTrack->GetEndTimeRoundDown() <= inputEnd) { *aOutputTrackFinished = true; } - if (interval.mStart >= interval.mEnd) + if (interval.mStart >= interval.mEnd) { break; + } next = interval.mEnd; // Ticks >= startTicks and < endTicks are in the interval StreamTime outputEnd = GraphTimeToStreamTime(interval.mEnd); TrackTicks startTicks = outputTrack->GetEnd(); StreamTime outputStart = GraphTimeToStreamTime(interval.mStart); MOZ_ASSERT(startTicks == TimeToTicksRoundUp(rate, outputStart), "Samples missing"); TrackTicks endTicks = TimeToTicksRoundUp(rate, outputEnd); @@ -300,16 +301,24 @@ TrackUnionStream::TrackUnionStream(DOMMe // Start of a new series of intervals where neither stream is blocked. map->mEndOfConsumedInputTicks = TimeToTicksRoundDown(rate, inputStart) - 1; } TrackTicks inputStartTicks = map->mEndOfConsumedInputTicks; TrackTicks inputEndTicks = inputStartTicks + ticks; map->mEndOfConsumedInputTicks = inputEndTicks; map->mEndOfLastInputIntervalInInputStream = inputEnd; map->mEndOfLastInputIntervalInOutputStream = outputEnd; + + if (GraphImpl()->mFlushSourcesNow) { + TrackTicks flushto = inputEndTicks; + STREAM_LOG(PR_LOG_DEBUG, ("TrackUnionStream %p flushing after %lld of %lld ticks of input data from track %d for track %d", + this, flushto, aInputTrack->GetSegment()->GetDuration(), aInputTrack->GetID(), outputTrack->GetID())); + aInputTrack->FlushAfter(flushto); + MOZ_ASSERT(inputTrackEndPoint >= aInputTrack->GetEnd()); + } // Now we prove that the above properties hold: // Property #1: trivial by construction. // Property #3: trivial by construction. Between every two // intervals where both streams are not blocked, the above if condition // is false and mEndOfConsumedInputTicks advances exactly to match // the ticks that were consumed. // Property #2: // Let originalOutputStart be the value of outputStart and originalInputStart
--- a/dom/media/compiledtest/moz.build +++ b/dom/media/compiledtest/moz.build @@ -1,23 +1,16 @@ # -*- 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/. -CppUnitTests([ +GeckoCppUnitTests([ 'TestAudioBuffers', 'TestAudioMixer' ]) FAIL_ON_WARNINGS = True LOCAL_INCLUDES += [ '..', ] - -USE_LIBS += [ - 'mozalloc', - 'nspr', - 'xpcomglue_s', - 'xul', -]
--- a/dom/media/fmp4/MP4Reader.cpp +++ b/dom/media/fmp4/MP4Reader.cpp @@ -526,16 +526,17 @@ MP4Reader::Decode(TrackType aTrack) (data.mInputExhausted || (data.mNumSamplesInput - data.mNumSamplesOutput) < data.mDecodeAhead) && !data.mEOS) { data.mMonitor.AssertCurrentThreadOwns(); data.mMonitor.Unlock(); nsAutoPtr<MP4Sample> compressed(PopSample(aTrack)); if (!compressed) { // EOS, or error. Send the decoder a signal to drain. + LOG("MP4Reader: EOS or error - no samples available"); LOG("Draining %s", TrackTypeToStr(aTrack)); data.mMonitor.Lock(); MOZ_ASSERT(!data.mEOS); data.mEOS = true; MOZ_ASSERT(!data.mDrainComplete); data.mDrainComplete = false; data.mMonitor.Unlock(); data.mDecoder->Drain();
--- a/dom/media/fmp4/apple/AppleATDecoder.cpp +++ b/dom/media/fmp4/apple/AppleATDecoder.cpp @@ -267,17 +267,17 @@ AppleATDecoder::SampleCallback(uint32_t OSStatus rv = AudioConverterFillComplexBuffer(mConverter, _PassthroughInputDataCallback, &userData, &numFrames /* in/out */, &decBuffer, packets.get()); if (rv && rv != kNeedMoreData) { - LOG("Error decoding audio stream: %#x\n", rv); + LOG("Error decoding audio stream: %d\n", rv); mCallback->Error(); break; } LOG("%d frames decoded", numFrames); // If we decoded zero frames then AudioConverterFillComplexBuffer is out // of data to provide. We drained its internal buffer completely on the // last pass.
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp +++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp @@ -104,17 +104,17 @@ public: WriteRecord(TruncateRecordId, nullptr, 0, new ReadThenTask(TruncateRecordId, new TestEmptyContinuation())); delete this; } }; class VerifyAndFinishContinuation : public ReadContinuation { public: - VerifyAndFinishContinuation(string aValue) + explicit VerifyAndFinishContinuation(string aValue) : mValue(aValue) {} void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE { if (aData != mValue) { FakeDecryptor::Message("FAIL VerifyAndFinishContinuation read data doesn't match expected data"); } sFinishedReplaceTest = true; MaybeFinish(); @@ -141,17 +141,17 @@ public: string mValue; string mOverwrite; }; static const string OpenAgainRecordId = "open-again-record-id"; class OpenedSecondTimeContinuation : public OpenContinuation { public: - OpenedSecondTimeContinuation(GMPRecord* aRecord) + explicit OpenedSecondTimeContinuation(GMPRecord* aRecord) : mRecord(aRecord) { } virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE { if (GMP_SUCCEEDED(aStatus)) { FakeDecryptor::Message("FAIL OpenSecondTimeContinuation should not be able to re-open record."); } @@ -243,17 +243,17 @@ public: delete this; } const string mRecordId; const string mValue; }; class ReportReadStatusContinuation : public ReadContinuation { public: - ReportReadStatusContinuation(const string& aRecordId) + explicit ReportReadStatusContinuation(const string& aRecordId) : mRecordId(aRecordId) {} void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE { if (GMP_FAILED(aErr)) { FakeDecryptor::Message("retrieve " + mRecordId + " failed"); } else { stringstream ss; ss << aData.size(); @@ -264,17 +264,17 @@ public: } delete this; } string mRecordId; }; class ReportReadRecordContinuation : public ReadContinuation { public: - ReportReadRecordContinuation(const string& aRecordId) + explicit ReportReadRecordContinuation(const string& aRecordId) : mRecordId(aRecordId) {} void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE { if (GMP_FAILED(aErr)) { FakeDecryptor::Message("retrieved " + mRecordId + " failed"); } else { FakeDecryptor::Message("retrieved " + mRecordId + " " + aData); } @@ -325,17 +325,17 @@ FakeDecryptor::UpdateSession(uint32_t aP } } else if (task == "retrieve-shutdown-token") { ReadRecord("shutdown-token", new ReportReadRecordContinuation("shutdown-token")); } } class CompleteShutdownTask : public GMPTask { public: - CompleteShutdownTask(GMPAsyncShutdownHost* aHost) + explicit CompleteShutdownTask(GMPAsyncShutdownHost* aHost) : mHost(aHost) { } virtual void Run() { mHost->ShutdownComplete(); } virtual void Destroy() { delete this; } GMPAsyncShutdownHost* mHost;
--- a/dom/media/gmp-plugin/gmp-test-decryptor.h +++ b/dom/media/gmp-plugin/gmp-test-decryptor.h @@ -75,17 +75,17 @@ private: void TestStorage(); GMPDecryptorCallback* mCallback; }; class TestAsyncShutdown : public GMPAsyncShutdown { public: - TestAsyncShutdown(GMPAsyncShutdownHost* aHost) + explicit TestAsyncShutdown(GMPAsyncShutdownHost* aHost) : mHost(aHost) { } virtual void BeginShutdown() MOZ_OVERRIDE; private: GMPAsyncShutdownHost* mHost; };
--- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -356,17 +356,17 @@ GMPParent::Shutdown() nsRefPtr<GMPParent> self(this); mService->ReAddOnGMPThread(self); } // else we've been asked to die and stay dead MOZ_ASSERT(mState == GMPStateNotLoaded); } class NotifyGMPShutdownTask : public nsRunnable { public: - NotifyGMPShutdownTask(const nsAString& aNodeId) + explicit NotifyGMPShutdownTask(const nsAString& aNodeId) : mNodeId(aNodeId) { } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService(); MOZ_ASSERT(obsService); if (obsService) {
--- a/dom/media/gmp/GMPStorageParent.cpp +++ b/dom/media/gmp/GMPStorageParent.cpp @@ -122,17 +122,17 @@ CloseFile(const nsACString& key, PRFileD if (PR_Close(entry) != PR_SUCCESS) { NS_WARNING("GMPDiskStorage Failed to clsose file."); } return PL_DHASH_REMOVE; } class GMPDiskStorage : public GMPStorage { public: - GMPDiskStorage(const nsCString& aNodeId) + explicit GMPDiskStorage(const nsCString& aNodeId) : mNodeId(aNodeId) { } ~GMPDiskStorage() { mFiles.Enumerate(CloseFile, nullptr); MOZ_ASSERT(!mFiles.Count()); }
--- a/dom/media/gtest/MockMediaResource.h +++ b/dom/media/gtest/MockMediaResource.h @@ -10,17 +10,17 @@ #include "mozilla/Atomics.h" namespace mozilla { class MockMediaResource : public MediaResource { public: - MockMediaResource(const char* aFileName); + explicit MockMediaResource(const char* aFileName); virtual nsIURI* URI() const MOZ_OVERRIDE { return nullptr; } virtual nsresult Close() MOZ_OVERRIDE { return NS_OK; } virtual void Suspend(bool aCloseImmediately) MOZ_OVERRIDE {} virtual void Resume() MOZ_OVERRIDE {} virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() MOZ_OVERRIDE { return nullptr; }
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp +++ b/dom/media/gtest/TestGMPCrossOrigin.cpp @@ -163,17 +163,17 @@ private: nsRefPtr<nsIRunnable> mContinuation; const nsString mNodeId; }; NS_IMPL_ISUPPORTS(GMPShutdownObserver, nsIRunnable, nsIObserver) class NotifyObserversTask : public nsRunnable { public: - NotifyObserversTask(const char* aTopic) + explicit NotifyObserversTask(const char* aTopic) : mTopic(aTopic) {} NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); if (observerService) { observerService->NotifyObservers(nullptr, mTopic, nullptr);
--- a/dom/media/gtest/TestMP4Reader.cpp +++ b/dom/media/gtest/TestMP4Reader.cpp @@ -19,17 +19,17 @@ class TestBinding { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestBinding); nsRefPtr<MP4Decoder> decoder; nsRefPtr<MockMediaResource> resource; nsRefPtr<MP4Reader> reader; - TestBinding(const char* aFileName = "gizmo.mp4") + explicit TestBinding(const char* aFileName = "gizmo.mp4") : decoder(new MP4Decoder()) , resource(new MockMediaResource(aFileName)) , reader(new MP4Reader(decoder)) { EXPECT_EQ(NS_OK, Preferences::SetBool( "media.fragmented-mp4.use-blank-decoder", true)); EXPECT_EQ(NS_OK, resource->Open(nullptr));
--- a/dom/media/mediasource/MediaSourceResource.h +++ b/dom/media/mediasource/MediaSourceResource.h @@ -21,17 +21,17 @@ extern PRLogModuleInfo* GetMediaSourceAP #define UNIMPLEMENTED() MSE_DEBUG("MediaSourceResource(%p): UNIMPLEMENTED FUNCTION at %s:%d", this, __FILE__, __LINE__) namespace mozilla { class MediaSourceResource MOZ_FINAL : public MediaResource { public: - MediaSourceResource(nsIPrincipal* aPrincipal = nullptr) + explicit MediaSourceResource(nsIPrincipal* aPrincipal = nullptr) : mPrincipal(aPrincipal) {} virtual nsresult Close() MOZ_OVERRIDE { return NS_OK; } virtual void Suspend(bool aCloseImmediately) MOZ_OVERRIDE { UNIMPLEMENTED(); } virtual void Resume() MOZ_OVERRIDE { UNIMPLEMENTED(); } virtual bool CanClone() MOZ_OVERRIDE { UNIMPLEMENTED(); return false; } virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) MOZ_OVERRIDE { UNIMPLEMENTED(); return nullptr; } virtual void SetReadMode(MediaCacheStream::ReadMode aMode) MOZ_OVERRIDE { UNIMPLEMENTED(); }
--- a/dom/media/mediasource/SourceBufferDecoder.h +++ b/dom/media/mediasource/SourceBufferDecoder.h @@ -79,16 +79,28 @@ public: } void SetTaskQueue(MediaTaskQueue* aTaskQueue) { MOZ_ASSERT((!mTaskQueue && aTaskQueue) || (mTaskQueue && !aTaskQueue)); mTaskQueue = aTaskQueue; } + void BreakCycles() + { + if (mReader) { + mReader->BreakCycles(); + mReader = nullptr; + } + mTaskQueue = nullptr; +#ifdef MOZ_EME + mCDMProxy = nullptr; +#endif + } + #ifdef MOZ_EME virtual nsresult SetCDMProxy(CDMProxy* aProxy) { MOZ_ASSERT(NS_IsMainThread()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); mCDMProxy = aProxy; return NS_OK; }
--- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -386,17 +386,17 @@ TrackBuffer::ContainsTime(int64_t aTime) } void TrackBuffer::BreakCycles() { MOZ_ASSERT(NS_IsMainThread()); for (uint32_t i = 0; i < mDecoders.Length(); ++i) { - mDecoders[i]->GetReader()->BreakCycles(); + mDecoders[i]->BreakCycles(); } mDecoders.Clear(); // These are cleared in Shutdown() MOZ_ASSERT(mInitializedDecoders.IsEmpty()); MOZ_ASSERT(!mParentDecoder); }
new file mode 100644 --- /dev/null +++ b/dom/media/test/eme.js @@ -0,0 +1,190 @@ +const KEYSYSTEM_TYPE = "org.w3.clearkey"; + +function bail(message) +{ + return function(err) { + ok(false, message); + if (err) { + info(err); + } + SimpleTest.finish(); + } +} + +function ArrayBufferToString(arr) +{ + var str = ''; + var view = new Uint8Array(arr); + for (var i = 0; i < view.length; i++) { + str += String.fromCharCode(view[i]); + } + return str; +} + +function StringToArrayBuffer(str) +{ + var arr = new ArrayBuffer(str.length); + var view = new Uint8Array(arr); + for (var i = 0; i < str.length; i++) { + view[i] = str.charCodeAt(i); + } + return arr; +} + +function Base64ToHex(str) +{ + var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/")); + var res = ""; + for (var i = 0; i < bin.length; i++) { + res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2); + } + return res; +} + +function HexToBase64(hex) +{ + var bin = ""; + for (var i = 0; i < hex.length; i += 2) { + bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); +} + +function UpdateSessionFunc(test, token) { + return function(ev) { + var msgStr = ArrayBufferToString(ev.message); + var msg = JSON.parse(msgStr); + + info(token + " got message from CDM: " + msgStr); + is(msg.type, test.sessionType, token + " key session type should match"); + ok(msg.kids, token + " message event should contain key ID array"); + + var outKeys = []; + + for (var i = 0; i < msg.kids.length; i++) { + var id64 = msg.kids[i]; + var idHex = Base64ToHex(msg.kids[i]).toLowerCase(); + var key = test.keys[idHex]; + + if (key) { + info(token + " found key " + key + " for key id " + idHex); + outKeys.push({ + "kty":"oct", + "alg":"A128KW", + "kid":id64, + "k":HexToBase64(key) + }); + } else { + bail(token + " Couldn't find key for key id " + idHex); + } + } + + var update = JSON.stringify({ + "keys" : outKeys, + "type" : msg.type + }); + info(token + " sending update message to CDM: " + update); + + ev.target.update(StringToArrayBuffer(update)).then(function() { + info(token + " MediaKeySession update ok!"); + }, bail(token + " MediaKeySession update failed")); + } +} + +function PlayFragmented(test, elem) +{ + return new Promise(function(resolve, reject) { + var ms = new MediaSource(); + elem.src = URL.createObjectURL(ms); + + var sb; + var curFragment = 0; + + function addNextFragment() { + if (curFragment >= test.fragments.length) { + ms.endOfStream(); + resolve(); + return; + } + + var fragmentFile = test.fragments[curFragment++]; + + var req = new XMLHttpRequest(); + req.open("GET", fragmentFile); + req.responseType = "arraybuffer"; + + req.addEventListener("load", function() { + info("fetch of " + fragmentFile + " complete"); + sb.appendBuffer(new Uint8Array(req.response)); + }); + + req.addEventListener("error", bail("Error fetching " + fragmentFile)); + req.addEventListener("abort", bail("Aborted fetching " + fragmentFile)); + + info("fetching resource " + fragmentFile); + req.send(null); + } + + ms.addEventListener("sourceopen", function () { + sb = ms.addSourceBuffer(test.type); + sb.addEventListener("updateend", addNextFragment); + + addNextFragment(); + }) + }); +} + +// Returns a promise that is resovled when the media element is ready to have +// its play() function called; when it's loaded MSE fragments, or once the load +// has started for non-MSE video. +function LoadTest(test, elem) +{ + if (test.fragments) { + return PlayFragmented(test, elem); + } + + // This file isn't fragmented; set the media source normally. + return new Promise(function(resolve, reject) { + elem.src = test.name; + resolve(); + }); +} + +function SetupEME(test, token, params) +{ + var v = document.createElement("video"); + + // Log events dispatched to make debugging easier... + ["loadstart", "loadedmetadata", "loadeddata", "ended", + "play", "canplay", "playing", "canplaythrough"].forEach(function (e) { + v.addEventListener(e, function(event) { + info(token + " " + e); + }, false); + }); + + var onSetKeysFail = (params && params.onSetKeysFail) + ? params.onSetKeysFail + : bail(token + " Failed to set MediaKeys on <video> element"); + + v.addEventListener("encrypted", function(ev) { + info(token + " got encrypted event"); + MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) { + info(token + " created MediaKeys object ok"); + mediaKeys.sessions = []; + return v.setMediaKeys(mediaKeys); + }, bail("failed to create MediaKeys object")).then(function() { + info(token + " set MediaKeys on <video> element ok"); + + var session = v.mediaKeys.createSession(test.sessionType); + if (params && params.onsessioncreated) { + params.onsessioncreated(session); + } + session.addEventListener("message", UpdateSessionFunc(test, token)); + session.generateRequest(ev.initDataType, ev.initData).then(function() { + }, bail(token + " Failed to initialise MediaKeySession")); + + }, onSetKeysFail); + }); + + return v; +}
--- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -135,16 +135,17 @@ support-files = detodos.opus detodos.opus^headers^ detodos.webm detodos.webm^headers^ dirac.ogg dirac.ogg^headers^ dynamic_redirect.sjs dynamic_resource.sjs + eme.js gizmo-frag-cenc1.m4s gizmo-frag-cenc2.m4s gizmo-frag-cencinit.mp4 file_access_controls.html fragment_noplay.js fragment_play.js gizmo.mp4 gizmo.mp4^headers^ @@ -355,17 +356,21 @@ skip-if = (toolkit == 'android' && proce [test_contentDuration7.html] [test_controls.html] [test_currentTime.html] [test_decode_error.html] [test_decoder_disable.html] [test_defaultMuted.html] [test_delay_load.html] skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984 -[test_encryptedMediaExtensions.html] +[test_eme_canvas_blocked.html] +skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908 +[test_eme_playback.html] +skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908 +[test_eme_stream_capture_blocked.html] skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908 [test_error_in_video_document.html] skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634 [test_error_on_404.html] [test_fastSeek.html] skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439 [test_fastSeek-forwards.html] [test_imagecapture.html]
new file mode 100644 --- /dev/null +++ b/dom/media/test/test_eme_canvas_blocked.html @@ -0,0 +1,67 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test Encrypted Media Extensions</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="manifest.js"></script> + <script type="text/javascript" src="eme.js"></script> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> +var manager = new MediaTestManager; + +function startTest(test, token) +{ + manager.started(token); + + var sessions = []; + + var v = SetupEME(test, token); + v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this. + + v.addEventListener("loadeddata", function(ev) { + var video = ev.target; + var canvas = document.createElement("canvas"); + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + document.body.appendChild(canvas); + var ctx = canvas.getContext("2d"); + var threwError = false; + try { + ctx.drawImage(video, 0, 0); + } catch (ex) { + threwError = true; + } + ok(threwError, token + " - Should throw an error when trying to draw EME video to canvas."); + manager.finished(token); + }); + + v.addEventListener("error", bail(token + " got error event")); + + LoadTest(test, v); +} + +function beginTest() { + manager.runTests(gEMETests, startTest); +} + +var prefs = [ + [ "media.mediasource.enabled", true ], + [ "media.mediasource.ignore_codecs", true ], +]; + +if (/Linux/.test(navigator.userAgent) || + !document.createElement('video').canPlayType("video/mp4")) { + // XXX remove once we have mp4 PlatformDecoderModules on all platforms. + prefs.push([ "media.fragmented-mp4.exposed", true ]); + prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest); +</script> +</pre> +</body> +</html>
rename from dom/media/test/test_encryptedMediaExtensions.html rename to dom/media/test/test_eme_playback.html --- a/dom/media/test/test_encryptedMediaExtensions.html +++ b/dom/media/test/test_eme_playback.html @@ -1,246 +1,96 @@ <!DOCTYPE HTML> <html> <head> <title>Test Encrypted Media Extensions</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <script type="text/javascript" src="manifest.js"></script> + <script type="text/javascript" src="eme.js"></script> </head> <body> <pre id="test"> <script class="testbody" type="text/javascript"> var manager = new MediaTestManager; -const KEYSYSTEM_TYPE = "org.w3.clearkey"; -function bail(message) -{ - return function(err) { - ok(false, message); - if (err) { - info(err); - } - SimpleTest.finish(); - } -} - -function ArrayBufferToString(arr) -{ - var str = ''; - var view = new Uint8Array(arr); - for (var i = 0; i < view.length; i++) { - str += String.fromCharCode(view[i]); - } - return str; -} - -function StringToArrayBuffer(str) -{ - var arr = new ArrayBuffer(str.length); - var view = new Uint8Array(arr); - for (var i = 0; i < str.length; i++) { - view[i] = str.charCodeAt(i); - } - return arr; -} - -function Base64ToHex(str) -{ - var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/")); - var res = ""; - for (var i = 0; i < bin.length; i++) { - res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2); - } - return res; -} - -function HexToBase64(hex) -{ - var bin = ""; - for (var i = 0; i < hex.length; i += 2) { - bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); - } - return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); -} - -function UpdateSessionFunc(test) { - return function(ev) { - var msgStr = ArrayBufferToString(ev.message); - var msg = JSON.parse(msgStr); - - info("got message from CDM: " + msgStr); - is(msg.type, test.sessionType, "Key session type should match"); - ok(msg.kids, "message event should contain key ID array"); - - var outKeys = []; - - for (var i = 0; i < msg.kids.length; i++) { - var id64 = msg.kids[i]; - var idHex = Base64ToHex(msg.kids[i]).toLowerCase(); - var key = test.keys[idHex]; - - if (key) { - info("found key " + key + " for key id " + idHex); - outKeys.push({ - "kty":"oct", - "alg":"A128KW", - "kid":id64, - "k":HexToBase64(key) - }); - } else { - bail("Couldn't find key for key id " + idHex); - } - } - - var update = JSON.stringify({ - "keys" : outKeys, - "type" : msg.type - }); - info("sending update message to CDM: " + update); - - ev.target.update(StringToArrayBuffer(update)).then(function() { - info("MediaKeySession update ok!"); - }, bail("MediaKeySession update failed")); - } -} - -function PlayFragmented(test, elem) -{ - var ms = new MediaSource(); - elem.src = URL.createObjectURL(ms); - - var sb; - var curFragment = 0; - - function addNextFragment() { - if (curFragment >= test.fragments.length) { - ms.endOfStream(); - elem.play(); - return; - } - - var fragmentFile = test.fragments[curFragment++]; - - var req = new XMLHttpRequest(); - req.open("GET", fragmentFile); - req.responseType = "arraybuffer"; - - req.addEventListener("load", function() { - sb.appendBuffer(new Uint8Array(req.response)); - }); - - info("fetching resource " + fragmentFile); - req.send(null); - } - - ms.addEventListener("sourceopen", function () { - sb = ms.addSourceBuffer(test.type); - sb.addEventListener("updateend", addNextFragment); - - addNextFragment(); - }); -} - -function PlayTest(test, elem) -{ - if (test.fragments) { - PlayFragmented(test, elem); - return; - } - - // This file isn't fragmented; set the media source normally. - elem.src = test.name; - elem.play(); -} - -function KeysChangeFunc(session, keys) { +function KeysChangeFunc(session, keys, token) { session.keyIdsReceived = []; for (var keyid in keys) { info("Set " + keyid + " to false in session.keyIdsReceived"); session.keyIdsReceived[keyid] = false; } return function(ev) { var session = ev.target; session.gotKeysChanged = true; session.getUsableKeyIds().then(function(keyIds) { for (var k = 0; k < keyIds.length; k++) { var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k]))); - ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected."); + ok(kid in session.keyIdsReceived, token + " session.keyIdsReceived contained " + kid + " as expected."); session.keyIdsReceived[kid] = true; } }, bail("Failed to get keyIds")); } } function startTest(test, token) { - manager.started(test._token); + manager.started(token); + + var sessions = []; - var v = document.createElement("video"); + var v = SetupEME(test, token, + { + onsessioncreated: function(session) { + sessions.push(session); + session.addEventListener("keyschange", KeysChangeFunc(session, test.keys, token), false); + } + } + ); + var gotEncrypted = false; var gotPlaying = false; v.addEventListener("encrypted", function(ev) { - gotEncrypted = true; - - info(token + " got encrypted event"); ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type), token + " MediaKeys should support this keysystem"); - - MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) { - info(token + " created MediaKeys object ok"); - mediaKeys.sessions = []; - return v.setMediaKeys(mediaKeys); - }, bail("failed to create MediaKeys object")).then(function() { - info(token + " set MediaKeys on <video> element ok"); - - ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type), - "MediaKeys should still support keysystem after CDM created..."); - - var session = v.mediaKeys.createSession(test.sessionType); - v.mediaKeys.sessions.push(session); - session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false); - session.addEventListener("message", UpdateSessionFunc(test)); - session.generateRequest(ev.initDataType, ev.initData).then(function() { - }, bail(token + " Failed to initialise MediaKeySession")); - - }, bail(token + " Failed to set MediaKeys on <video> element")); + gotEncrypted = true; }); v.addEventListener("playing", function () { gotPlaying = true; }); v.addEventListener("ended", function(ev) { ok(true, token + " got ended event"); - manager.finished(test._token); ok(gotEncrypted, token + " encrypted event should have fired"); ok(gotPlaying, token + " playing event should have fired"); ok(Math.abs(test.duration - v.duration) < 0.1, token + " Duration of video should be corrrect"); ok(Math.abs(test.duration - v.currentTime) < 0.1, token + " Current time should be same as duration"); + // Verify all sessions had all keys went sent the to the CDM usable, and thus // that we received keyschange event(s). - var sessions = v.mediaKeys.sessions; - is(sessions.length, 1, "should have 1 session"); + is(sessions.length, 1, token + " should have 1 session"); for (var i = 0; i < sessions.length; i++) { var session = sessions[i]; - ok(session.gotKeysChanged, "Should have received at least one keychange event"); + ok(session.gotKeysChanged, token + " should have received at least one keychange event"); for (var kid in session.keyIdsReceived) { - ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected"); + ok(session.keyIdsReceived[kid], token + " key with id " + kid + " was usable as expected"); } } - }); + + manager.finished(token); + }); v.addEventListener("error", bail(token + " got error event")); - PlayTest(test, v); + LoadTest(test, v).then(function(){v.play();}, bail(token + " failed to load")); } function testIsTypeSupported() { var t = MediaKeys.isTypeSupported; const clearkey = "org.w3.clearkey"; ok(!t("bogus", "bogon", "video/bogus"), "Invalid type."); ok(t(clearkey), "ClearKey supported.");
new file mode 100644 --- /dev/null +++ b/dom/media/test/test_eme_stream_capture_blocked.html @@ -0,0 +1,102 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test Encrypted Media Extensions</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="manifest.js"></script> + <script type="text/javascript" src="eme.js"></script> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> +var manager = new MediaTestManager; + +function startTest(test, token) +{ + // Three cases: + // 1. setting MediaKeys on an element captured by MediaElementSource should fail, and + // 2. creating a MediaElementSource on a media element with a MediaKeys should fail, and + // 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail. + + // Case 1. setting MediaKeys on an element captured by MediaElementSource should fail. + var case1token = token + "_case1"; + var setKeysFailed = function() { + ok(true, case1token + " setMediaKeys failed as expected."); + manager.finished(case1token); + }; + var v1 = SetupEME(test, case1token, { onSetKeysFail: setKeysFailed }); + var context = new AudioContext(); + var node = context.createMediaElementSource(v1); + v1.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this. + v1.addEventListener("error", bail(case1token + " got error event")); + v1.addEventListener("canplay", function(ev) { + ok(false, case1token + " should never reach canplay, as setMediaKeys should fail"); + }); + manager.started(case1token); + LoadTest(test, v1); + + + // Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail. + var case2token = token + "_case2"; + var v2 = SetupEME(test, case2token); + v2.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this. + v2.addEventListener("error", bail(case2token + " got error event")); + v2.addEventListener("canplay", function(ev) { + ok(true, case2token + " should reach canplay"); + var threw = false; + try { + var context = new AudioContext(); + var node = context.createMediaElementSource(v2); + } catch (e) { + threw = true; + } + ok(threw, "Should throw an error creating a MediaElementSource on an EME video."); + manager.finished(case2token); + }); + manager.started(case2token); + LoadTest(test, v2); + + + // Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail. + var case3token = token + "_case3"; + var v3 = SetupEME(test, case3token); + v3.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this. + v3.addEventListener("error", bail(case3token + " got error event")); + v3.addEventListener("canplay", function(ev) { + ok(true, case3token + " should reach canplay"); + var threw = false; + try { + var stream = v3.mozCaptureStreamUntilEnded(); + } catch (e) { + threw = true; + } + ok(threw, "Should throw an error calling mozCaptureStreamUntilEnded an EME video."); + manager.finished(case3token); + }); + manager.started(case3token); + LoadTest(test, v3); +} + +function beginTest() { + manager.runTests(gEMETests, startTest); +} + +var prefs = [ + [ "media.mediasource.enabled", true ], + [ "media.mediasource.ignore_codecs", true ], +]; + +if (/Linux/.test(navigator.userAgent) || + !document.createElement('video').canPlayType("video/mp4")) { + // XXX remove once we have mp4 PlatformDecoderModules on all platforms. + prefs.push([ "media.fragmented-mp4.exposed", true ]); + prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest); +</script> +</pre> +</body> +</html>
--- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -280,16 +280,22 @@ AudioContext::CreateAnalyser() already_AddRefed<MediaElementAudioSourceNode> AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement, ErrorResult& aRv) { if (mIsOffline) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } +#ifdef MOZ_EME + if (aMediaElement.ContainsRestrictedContent()) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } +#endif nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode = new MediaElementAudioSourceNode(this, stream); return mediaElementAudioSourceNode.forget(); }
--- a/dom/media/webaudio/compiledtest/moz.build +++ b/dom/media/webaudio/compiledtest/moz.build @@ -1,22 +1,15 @@ # -*- 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/. -CppUnitTests([ +GeckoCppUnitTests([ 'TestAudioEventTimeline', ]) FAIL_ON_WARNINGS = True LOCAL_INCLUDES += [ '..', ] - -USE_LIBS += [ - 'mozalloc', - 'nspr', - 'xpcomglue_s', - 'xul', -]
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h @@ -14,18 +14,18 @@ #undef FF #include "webrtc/video_engine/include/vie_capture.h" namespace mozilla { class MediaEngineCameraVideoSource : public MediaEngineVideoSource { public: - MediaEngineCameraVideoSource(int aIndex, - const char* aMonitorName = "Camera.Monitor") + explicit MediaEngineCameraVideoSource(int aIndex, + const char* aMonitorName = "Camera.Monitor") : MediaEngineVideoSource(kReleased) , mMonitor(aMonitorName) , mWidth(0) , mHeight(0) , mInitDone(false) , mHasDirectListeners(false) , mCaptureIndex(aIndex) , mTrackID(0)
--- a/dom/network/Connection.h +++ b/dom/network/Connection.h @@ -27,17 +27,17 @@ class Connection MOZ_FINAL : public DOME , public nsINetworkProperties { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSINETWORKPROPERTIES NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper) - Connection(nsPIDOMWindow *aWindow); + explicit Connection(nsPIDOMWindow *aWindow); void Shutdown(); // For IObserver void Notify(const hal::NetworkInformation& aNetworkInfo); // WebIDL
--- a/dom/network/TCPServerSocket.js +++ b/dom/network/TCPServerSocket.js @@ -46,17 +46,17 @@ function TCPServerSocket() { } // When this API moves to WebIDL and these __exposedProps__ go away, remove // this call here and remove the API from XPConnect. Cu.skipCOWCallableChecks(); TCPServerSocket.prototype = { __exposedProps__: { - port: 'r', + localPort: 'r', onconnect: 'rw', onerror: 'rw' }, get localPort() { return this._localPort; }, get onconnect() { return this._onconnect; @@ -72,17 +72,17 @@ TCPServerSocket.prototype = { }, _callListenerAcceptCommon: function tss_callListenerAcceptCommon(socket) { if (this._onconnect) { try { this["onconnect"].call(null, socket); } catch (e) { socket.close(); - } + } } else { socket.close(); dump("Received unexpected connection!"); } }, init: function tss_init(aWindowObj) { this.useWin = aWindowObj; @@ -123,17 +123,17 @@ TCPServerSocket.prototype = { }, callListenerError: function tss_callListenerError(message, filename, lineNumber, columnNumber) { if (this._onerror) { var type = "error"; var error = new Error(message, filename, lineNumber, columnNumber); this["onerror"].call(null, new TCPSocketEvent(type, this, error)); - } + } }, /* end nsITCPServerSocketInternal method */ close: function tss_close() { if (this._inChild) { this._serverBridge.close(); return; } @@ -143,17 +143,18 @@ TCPServerSocket.prototype = { this._neckoTCPServerSocket.close(); } }, // nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen) onSocketAccepted: function tss_onSocketAccepted(server, trans) { // precondition: this._inChild == false try { - let that = TCPSocketInternal.createAcceptedParent(trans, this._binaryType); + let that = TCPSocketInternal.createAcceptedParent(trans, this._binaryType, + this.useWin); this._callListenerAcceptCommon(that); } catch(e) { trans.close(Cr.NS_BINDING_ABORTED); } }, // nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen)
--- a/dom/network/TCPServerSocketParent.cpp +++ b/dom/network/TCPServerSocketParent.cpp @@ -2,16 +2,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/. */ #include "TCPServerSocketParent.h" #include "nsJSUtils.h" #include "TCPSocketParent.h" #include "mozilla/unused.h" #include "mozilla/AppProcessChecker.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/TabParent.h" namespace mozilla { namespace dom { static void FireInteralError(mozilla::net::PTCPServerSocketParent* aActor, uint32_t aLineNo) { @@ -53,24 +55,38 @@ TCPServerSocketParent::Init(PNeckoParent nsresult rv; mIntermediary = do_CreateInstance("@mozilla.org/tcp-socket-intermediary;1", &rv); if (NS_FAILED(rv)) { FireInteralError(this, __LINE__); return true; } - rv = mIntermediary->Listen(this, aLocalPort, aBacklog, aBinaryType, getter_AddRefs(mServerSocket)); + rv = mIntermediary->Listen(this, aLocalPort, aBacklog, aBinaryType, GetAppId(), + getter_AddRefs(mServerSocket)); if (NS_FAILED(rv) || !mServerSocket) { FireInteralError(this, __LINE__); return true; } return true; } +uint32_t +TCPServerSocketParent::GetAppId() +{ + uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID; + const PContentParent *content = Manager()->Manager(); + const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent(); + if (browsers.Length() > 0) { + TabParent *tab = static_cast<TabParent*>(browsers[0]); + appId = tab->OwnAppId(); + } + return appId; +}; + NS_IMETHODIMP TCPServerSocketParent::SendCallbackAccept(nsITCPSocketParent *socket) { TCPSocketParent* _socket = static_cast<TCPSocketParent*>(socket); PTCPSocketParent* _psocket = static_cast<PTCPSocketParent*>(_socket); _socket->AddIPDLReference();
--- a/dom/network/TCPServerSocketParent.h +++ b/dom/network/TCPServerSocketParent.h @@ -26,16 +26,18 @@ public: TCPServerSocketParent() : mNeckoParent(nullptr), mIPCOpen(false) {} bool Init(PNeckoParent* neckoParent, const uint16_t& aLocalPort, const uint16_t& aBacklog, const nsString& aBinaryType); virtual bool RecvClose() MOZ_OVERRIDE; virtual bool RecvRequestDelete() MOZ_OVERRIDE; + uint32_t GetAppId(); + void AddIPDLReference(); void ReleaseIPDLReference(); private: ~TCPServerSocketParent() {} virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
--- a/dom/network/TCPSocket.js +++ b/dom/network/TCPSocket.js @@ -72,26 +72,35 @@ function TCPSocketEvent(type, sock, data // When this API moves to WebIDL and these __exposedProps__ go away, remove // this call here and remove the API from XPConnect. Cu.skipCOWCallableChecks(); TCPSocketEvent.prototype = { __exposedProps__: { type: 'r', target: 'r', - data: 'r' + data: 'r', + // Promise::ResolveInternal tries to check if the thing being resolved is + // itself a promise through the presence of "then". Accordingly, we list + // it as an exposed property, although we return undefined for it. + // Bug 882123 covers making TCPSocket be a proper event target with proper + // events. + then: 'r' }, get type() { return this._type; }, get target() { return this._target; }, get data() { return this._data; + }, + get then() { + return undefined; } } /* * nsIDOMTCPSocket object */ function TCPSocket() { @@ -305,17 +314,17 @@ TCPSocket.prototype = { self._socketOutputStream.close(); self._readyState = kCLOSED; self.callListener("close"); } } } }, null); }, - + _initStream: function ts_initStream(binaryType) { this._binaryType = binaryType; this._socketInputStream = this._transport.openInputStream(0, 0, 0); this._socketOutputStream = this._transport.openOutputStream( Ci.nsITransport.OPEN_UNBUFFERED, 0, 0); // If the other side is not listening, we will // get an onInputStreamReady callback where available @@ -426,43 +435,45 @@ TCPSocket.prototype = { this.callListener("drain"); } } else { LOG("bufferedAmount is updated but haven't reaches zero. bufferedAmount: " + bufferedAmount); } }, - createAcceptedParent: function ts_createAcceptedParent(transport, binaryType) { + createAcceptedParent: function ts_createAcceptedParent(transport, binaryType, windowObject) { let that = new TCPSocket(); that._transport = transport; that._initStream(binaryType); // ReadyState is kOpen since accepted transport stream has already been connected that._readyState = kOPEN; that._inputStreamPump = new InputStreamPump(that._socketInputStream, -1, -1, 0, 0, false); that._inputStreamPump.asyncRead(that, null); // Grab host/port from SocketTransport. that._host = transport.host; that._port = transport.port; + that.useWin = windowObject; return that; }, createAcceptedChild: function ts_createAcceptedChild(socketChild, binaryType, windowObject) { let that = new TCPSocket(); that._binaryType = binaryType; that._inChild = true; that._readyState = kOPEN; socketChild.setSocketAndWindow(that, windowObject); that._socketBridge = socketChild; that._host = socketChild.host; that._port = socketChild.port; + that.useWin = windowObject; return that; }, setAppId: function ts_setAppId(appId) { #ifdef MOZ_WIDGET_GONK this._appId = appId; #else @@ -632,18 +643,17 @@ TCPSocket.prototype = { }, listen: function ts_listen(localPort, options, backlog) { // in the testing case, init won't be called and // hasPrivileges will be null. We want to proceed to test. if (this._hasPrivileges !== true && this._hasPrivileges !== null) { throw new Error("TCPSocket does not have permission in this context.\n"); } - - let that = new TCPServerSocket(this.useWin || this); + let that = new TCPServerSocket(this.useWin); options = options || { binaryType : this.binaryType }; backlog = backlog || -1; that.listen(localPort, options, backlog); return that; }, close: function ts_close() {
--- a/dom/network/TCPSocketParentIntermediary.js +++ b/dom/network/TCPSocketParentIntermediary.js @@ -38,47 +38,51 @@ TCPSocketParentIntermediary.prototype = open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType, aAppId) { let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket); let socket = baseSocket.open(aHost, aPort, {useSecureTransport: aUseSSL, binaryType: aBinaryType}); if (!socket) return null; let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal); - if (socketInternal) { - socketInternal.setAppId(aAppId); - } + socketInternal.setAppId(aAppId); // Handle parent's request to update buffered amount. socketInternal.setOnUpdateBufferedAmountHandler( this._onUpdateBufferedAmountHandler.bind(this, aParentSide)); // Handlers are set to the JS-implemented socket object on the parent side. this._setCallbacks(aParentSide, socket); return socket; }, - listen: function(aTCPServerSocketParent, aLocalPort, aBacklog, aBinaryType) { + listen: function(aTCPServerSocketParent, aLocalPort, aBacklog, aBinaryType, aAppId) { let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket); let serverSocket = baseSocket.listen(aLocalPort, { binaryType: aBinaryType }, aBacklog); if (!serverSocket) return null; let localPort = serverSocket.localPort; serverSocket["onconnect"] = function(socket) { var socketParent = Cc["@mozilla.org/tcp-socket-parent;1"] .createInstance(Ci.nsITCPSocketParent); var intermediary = new TCPSocketParentIntermediary(); + + let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal); + socketInternal.setAppId(aAppId); + socketInternal.setOnUpdateBufferedAmountHandler( + intermediary._onUpdateBufferedAmountHandler.bind(intermediary, socketParent)); + // Handlers are set to the JS-implemented socket object on the parent side, // so that the socket parent object can communicate data // with the corresponding socket child object through IPC. intermediary._setCallbacks(socketParent, socket); // The members in the socket parent object are set with arguments, - // so that the socket parent object can communicate data + // so that the socket parent object can communicate data // with the JS socket object on the parent side via the intermediary object. socketParent.setSocketAndIntermediary(socket, intermediary); aTCPServerSocketParent.sendCallbackAccept(socketParent); }; serverSocket["onerror"] = function(data) { var error = data.data;
--- a/dom/network/interfaces/nsIDOMTCPSocket.idl +++ b/dom/network/interfaces/nsIDOMTCPSocket.idl @@ -211,17 +211,17 @@ interface nsIDOMTCPSocket : nsISupports }; /* * This interface is implemented in TCPSocket.js as an internal interfaces * for use in cross-process socket implementation. * Needed to account for multiple possible types that can be provided to * the socket callbacks as arguments. */ -[scriptable, uuid(017f130f-2477-4215-8783-57eada957699)] +[scriptable, uuid(b1235064-9a08-4714-ad03-1212e4562803)] interface nsITCPSocketInternal : nsISupports { // Trigger the callback for |type| and provide a DOMError() object with the given data void callListenerError(in DOMString type, in DOMString name); // Trigger the callback for |type| and provide a string argument void callListenerData(in DOMString type, in DOMString data); // Trigger the callback for |type| and provide an ArrayBuffer argument @@ -248,18 +248,21 @@ interface nsITCPSocketInternal : nsISupp // Create a socket object on the parent side. // This is called in accepting any open request on the parent side. // // @param transport // The accepted socket transport. // @param binaryType // "arraybuffer" to use ArrayBuffer instances // in the ondata callback and as the argument to send. + // @param window + // An object to create ArrayBuffer for this window. See Bug 831107. nsIDOMTCPSocket createAcceptedParent(in nsISocketTransport transport, - in DOMString binaryType); + in DOMString binaryType, + in nsIDOMWindow window); // Create a DOM socket on the child side // This is called when the socket is accepted on the parent side. // // @param socketChild // The socket child object for the IPC implementation. // @param binaryType // "arraybuffer" to use ArrayBuffer instances
--- a/dom/network/interfaces/nsITCPSocketParent.idl +++ b/dom/network/interfaces/nsITCPSocketParent.idl @@ -71,16 +71,17 @@ interface nsITCPSocketIntermediary : nsI nsIDOMTCPSocket open(in nsITCPSocketParent parent, in DOMString host, in unsigned short port, in boolean useSSL, in DOMString binaryType, in unsigned long appId); // Listen on a port nsIDOMTCPServerSocket listen(in nsITCPServerSocketParent parent, in unsigned short port, in unsigned short backlog, - in DOMString binaryType); + in DOMString binaryType, + in unsigned long appId); // Called when received a child request to send a string. void onRecvSendString(in DOMString data, in uint32_t trackingNumber); // Called when received a child request to send an array buffer. void onRecvSendArrayBuffer(in jsval data, in uint32_t trackingNumber); };
new file mode 100644 --- /dev/null +++ b/dom/network/tests/add_task.js @@ -0,0 +1,83 @@ +// Temporary implementation of add_task for mochitest-plain until bug 1078657 is +// implemented. +SimpleTest.waitForExplicitFinish(); +(function(scope) { + var pendingTasks = []; + var pendingPromise = null; + + // Strict spawn function that takes a known generatorFunc and assumes that + // every yielded value will be a Promise. If nesting is desired, then yield* + // should be used! + function spawn(generatorFunc) { + return new Promise(function(resolve, reject) { + try { + var iterator = generatorFunc(); + } + catch (ex) { + ok(false, 'Problem invoking generator func: ' + ex + ': ' + ex.stack); + return; + } + var stepResolved = function(result) { + try { + var iterStep = iterator.next(result); + } + catch (ex) { + ok(false, 'Problem invoking iterator step: ' + ex + ': ' + ex.stack); + return; + } + if (iterStep.done) { + resolve(iterStep.value); + return; + } + if (!iterStep.value || !iterStep.value.then) { + ok(false, 'Iterator step returned non-Promise: ' + iterStep.value); + } + iterStep.value.then(stepResolved, generalErrback); + }; + stepResolved(); + }); + } + + function maybeSpawn(promiseOrGenerator) { + if (promiseOrGenerator.then) { + return promiseOrGenerator; + } + return spawn(promiseOrGenerator); + } + + scope.add_task = function(thing) { + pendingTasks.push(thing); + }; + + function generalErrback(ex) { + ok(false, + 'A rejection happened: ' + + (ex ? (ex + ': ' + ex.stack) : '')); + } + + function runNextTask() { + if (pendingTasks.length) { + pendingPromise = maybeSpawn(pendingTasks.shift()); + pendingPromise.then(runNextTask, generalErrback); + } else { + SimpleTest.finish(); + } + } + + // Trigger runNextTask after we think all JS files have been loaded. + // The primary goal is that we can call SimpleTest.finish() after all test + // code has been loaded and run. We gate this based on the document's + // readyState. + var running = false; + function maybeStartRunning() { + if (!running && document.readyState === 'complete') { + running = true; + document.removeEventListener('readystateChange', maybeStartRunning); + // Defer to a subsequent turn of the event loop to let micro-tasks and any + // other clever setTimeout(0) instances run first. + window.setTimeout(runNextTask, 0); + } + } + document.addEventListener('readystatechange', maybeStartRunning); + maybeStartRunning(); +})(this);
--- a/dom/network/tests/mochitest.ini +++ b/dom/network/tests/mochitest.ini @@ -1,14 +1,17 @@ [DEFAULT] support-files = + add_task.js file_udpsocket_iframe.html + test_tcpsocket_client_and_server_basics.js [test_network_basics.html] skip-if = toolkit == "gonk" || toolkit == 'android' +[test_tcpsocket_client_and_server_basics.html] [test_tcpsocket_default_permissions.html] skip-if = toolkit == "gonk" [test_tcpsocket_enabled_no_perm.html] skip-if = toolkit == "gonk" [test_tcpsocket_enabled_with_perm.html] skip-if = toolkit == "gonk" || e10s [test_networkstats_alarms.html] skip-if = toolkit != "gonk"
new file mode 100644 --- /dev/null +++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.html @@ -0,0 +1,27 @@ +<!DOCTYPE HTML> +<html> +<!-- +Core tests for TCPSocket and TCPServerSocket that replace their previous +separate xpcshell incarnations. This migration and cleanup occurred as part +of bug 1084245 in order to get coverage of the tests from content. + +https://bugzilla.mozilla.org/show_bug.cgi?id=1084245 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1084245</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="add_task.js"></script> + <script type="application/javascript;version=1.7" src="test_tcpsocket_client_and_server_basics.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1084245">Mozilla Bug 1084245</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js @@ -0,0 +1,363 @@ +'use strict'; + +const SERVER_BACKLOG = -1; + +const SOCKET_EVENTS = ['open', 'data', 'drain', 'error', 'close']; + +function concatUint8Arrays(a, b) { + let newArr = new Uint8Array(a.length + b.length); + newArr.set(a, 0); + newArr.set(b, a.length); + return newArr; +} + +function assertUint8ArraysEqual(a, b, comparingWhat) { + if (a.length !== b.length) { + ok(false, comparingWhat + ' arrays do not have the same length; ' + + a.length + ' versus ' + b.length); + return; + } + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + ok(false, comparingWhat + ' arrays differ at index ' + i + + a[i] + ' versus ' + b[i]); + return; + } + } + ok(true, comparingWhat + ' arrays were equivalent.'); +} + +/** + * Helper method to add event listeners to a socket and provide two Promise-returning + * helpers (see below for docs on them). This *must* be called during the turn of + * the event loop where TCPSocket.open is called or the onconnect method is being + * invoked. + */ +function listenForEventsOnSocket(socket, socketType) { + let wantDataLength = null; + let pendingResolve = null; + let receivedEvents = []; + let receivedData = null; + let handleGenericEvent = function(event) { + dump('(' + socketType + ' event: ' + event.type + ')\n'); + if (pendingResolve && wantDataLength === null) { + pendingResolve(event); + pendingResolve = null; + } else { + receivedEvents.push(event); + } + }; + + socket.onopen = handleGenericEvent; + socket.ondrain = handleGenericEvent; + socket.onerror = handleGenericEvent; + socket.onclose = handleGenericEvent; + socket.ondata = function(event) { + dump('(' + socketType + ' event: ' + event.type + ' length: ' + + event.data.byteLength + ')\n'); + var arr = new Uint8Array(event.data); + if (receivedData === null) { + receivedData = arr; + } else { + receivedData = concatUint8Arrays(receivedData, arr); + } + if (wantDataLength !== null && + receivedData.length >= wantDataLength) { + pendingResolve(receivedData); + pendingResolve = null; + receivedData = null; + wantDataLength = null; + } + }; + + + return { + /** + * Return a Promise that will be resolved with the next (non-data) event + * received by the socket. If there are queued events, the Promise will + * be immediately resolved (but you won't see that until a future turn of + * the event loop). + */ + waitForEvent: function() { + if (pendingResolve) { + throw new Error('only one wait allowed at a time.'); + } + + if (receivedEvents.length) { + return Promise.resolve(receivedEvents.shift()); + } + + dump('(' + socketType + ' waiting for event)\n'); + return new Promise(function(resolve, reject) { + pendingResolve = resolve; + }); + }, + /** + * Return a Promise that will be resolved with a Uint8Array of at least the + * given length. We buffer / accumulate received data until we have enough + * data. Data is buffered even before you call this method, so be sure to + * explicitly wait for any and all data sent by the other side. + */ + waitForDataWithAtLeastLength: function(length) { + if (pendingResolve) { + throw new Error('only one wait allowed at a time.'); + } + if (receivedData && receivedData.length >= length) { + let promise = Promise.resolve(receivedData); + receivedData = null; + return promise; + } + dump('(' + socketType + ' waiting for ' + length + ' bytes)\n'); + return new Promise(function(resolve, reject) { + pendingResolve = resolve; + wantDataLength = length; + }); + } + }; +} + +/** + * Return a promise that is resolved when the server receives a connection. The + * promise is resolved with { socket, queue } where `queue` is the result of + * calling listenForEventsOnSocket(socket). This must be done because we need + * to add the event listener during the connection. + */ +function waitForConnection(listeningServer) { + return new Promise(function(resolve, reject) { + // Because of the event model of sockets, we can't use the + // listenForEventsOnSocket mechanism; we need to hook up listeners during + // the connect event. + listeningServer.onconnect = function(socket) { + // Clobber the listener to get upset if it receives any more connections + // after this. + listeningServer.onconnect = function() { + ok(false, 'Received a connection when not expecting one.'); + }; + ok(true, 'Listening server accepted socket'); + resolve({ + socket: socket, + queue: listenForEventsOnSocket(socket, 'server') + }); + }; + }); +} + +function defer() { + var deferred = {}; + deferred.promise = new Promise(function(resolve, reject) { + deferred.resolve = resolve; + deferred.reject = reject; + }); + return deferred; +} + + +function* test_basics() { + // Enable our use of TCPSocket + let prefDeferred = defer(); + SpecialPowers.pushPrefEnv( + { set: [ ['dom.mozTCPSocket.enabled', true] ] }, + prefDeferred.resolve); + yield prefDeferred.promise; + + let permDeferred = defer(); + SpecialPowers.pushPermissions( + [ { type: 'tcp-socket', allow: true, context: document } ], + permDeferred.resolve); + yield permDeferred.promise; + + // See bug 903830; in e10s mode we never get to find out the localPort if we + // let it pick a free port by choosing 0. This is the same port the xpcshell + // test was using. + let serverPort = 8085; + + let TCPSocket = navigator.mozTCPSocket; + // - Start up a listening socket. + let listeningServer = TCPSocket.listen(serverPort, + { binaryType: 'arraybuffer' }, + SERVER_BACKLOG); + + let connectedPromise = waitForConnection(listeningServer); + + // -- Open a connection to the server + let clientSocket = TCPSocket.open('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); + let clientQueue = listenForEventsOnSocket(clientSocket, 'client'); + + // (the client connects) + is((yield clientQueue.waitForEvent()).type, 'open', 'got open event'); + is(clientSocket.readyState, 'open', 'client readyState is open'); + + // (the server connected) + let { socket: serverSocket, queue: serverQueue } = yield connectedPromise; + is(serverSocket.readyState, 'open', 'server readyState is open'); + + // -- Simple send / receive + // - Send data from client to server + // (But not so much we cross the drain threshold.) + let smallUint8Array = new Uint8Array(256); + for (let i = 0; i < smallUint8Array.length; i++) { + smallUint8Array[i] = i; + } + is(clientSocket.send(smallUint8Array.buffer, 0, smallUint8Array.length), true, + 'Client sending less than 64k, buffer should not be full.'); + + let serverReceived = yield serverQueue.waitForDataWithAtLeastLength(256); + assertUint8ArraysEqual(serverReceived, smallUint8Array, + 'Server received/client sent'); + + // - Send data from server to client + // (But not so much we cross the drain threshold.) + is(serverSocket.send(smallUint8Array.buffer, 0, smallUint8Array.length), true, + 'Server sending less than 64k, buffer should not be full.'); + + let clientReceived = yield clientQueue.waitForDataWithAtLeastLength(256); + assertUint8ArraysEqual(clientReceived, smallUint8Array, + 'Client received/server sent'); + + // -- Perform sending multiple times with different buffer slices + // - Send data from client to server + // (But not so much we cross the drain threshold.) + is(clientSocket.send(smallUint8Array.buffer, 0, 7), + true, 'Client sending less than 64k, buffer should not be full.'); + is(clientSocket.send(smallUint8Array.buffer, 7, smallUint8Array.length - 7), + true, 'Client sending less than 64k, buffer should not be full.'); + + serverReceived = yield serverQueue.waitForDataWithAtLeastLength(256); + assertUint8ArraysEqual(serverReceived, smallUint8Array, + 'Server received/client sent'); + + // - Send data from server to client + // (But not so much we cross the drain threshold.) + is(serverSocket.send(smallUint8Array.buffer, 0, 7), + true, 'Server sending less than 64k, buffer should not be full.'); + is(serverSocket.send(smallUint8Array.buffer, 7, smallUint8Array.length - 7), + true, 'Server sending less than 64k, buffer should not be full.'); + + clientReceived = yield clientQueue.waitForDataWithAtLeastLength(256); + assertUint8ArraysEqual(clientReceived, smallUint8Array, + 'Client received/server sent'); + + + // -- Send "big" data in both directions + // (Enough to cross the buffering/drain threshold; 64KiB) + let bigUint8Array = new Uint8Array(65536 + 3); + for (let i = 0; i < bigUint8Array.length; i++) { + bigUint8Array[i] = i % 256; + } + // Do this twice so we have confidence that the 'drain' event machinery + // doesn't break after the first use. + for (let iSend = 0; iSend < 2; iSend++) { + // - Send "big" data from the client to the server + is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false, + 'Client sending more than 64k should result in the buffer being full.'); + is((yield clientQueue.waitForEvent()).type, 'drain', + 'The drain event should fire after a large send that indicated full.'); + + serverReceived = yield serverQueue.waitForDataWithAtLeastLength( + bigUint8Array.length); + assertUint8ArraysEqual(serverReceived, bigUint8Array, + 'server received/client sent'); + + // - Send "big" data from the server to the client + is(serverSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false, + 'Server sending more than 64k should result in the buffer being full.'); + is((yield serverQueue.waitForEvent()).type, 'drain', + 'The drain event should fire after a large send that indicated full.'); + + clientReceived = yield clientQueue.waitForDataWithAtLeastLength( + bigUint8Array.length); + assertUint8ArraysEqual(clientReceived, bigUint8Array, + 'client received/server sent'); + } + + // -- Server closes the connection + serverSocket.close(); + is(serverSocket.readyState, 'closing', + 'readyState should be closing immediately after calling close'); + + is((yield clientQueue.waitForEvent()).type, 'close', + 'The client should get a close event when the server closes.'); + is(clientSocket.readyState, 'closed', + 'client readyState should be closed after close event'); + is((yield serverQueue.waitForEvent()).type, 'close', + 'The server should get a close event when it closes itself.'); + is(serverSocket.readyState, 'closed', + 'server readyState should be closed after close event'); + + // -- Re-establish connection + connectedPromise = waitForConnection(listeningServer); + clientSocket = TCPSocket.open('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); + clientQueue = listenForEventsOnSocket(clientSocket, 'client'); + is((yield clientQueue.waitForEvent()).type, 'open', 'got open event'); + + let connectedResult = yield connectedPromise; + // destructuring assignment is not yet ES6 compliant, must manually unpack + serverSocket = connectedResult.socket; + serverQueue = connectedResult.queue; + + // -- Client closes the connection + clientSocket.close(); + is(clientSocket.readyState, 'closing', + 'client readyState should be losing immediately after calling close'); + + is((yield clientQueue.waitForEvent()).type, 'close', + 'The client should get a close event when it closes itself.'); + is(clientSocket.readyState, 'closed', + 'client readyState should be closed after the close event is received'); + is((yield serverQueue.waitForEvent()).type, 'close', + 'The server should get a close event when the client closes.'); + is(serverSocket.readyState, 'closed', + 'server readyState should be closed after the close event is received'); + + + // -- Re-establish connection + connectedPromise = waitForConnection(listeningServer); + clientSocket = TCPSocket.open('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); + clientQueue = listenForEventsOnSocket(clientSocket, 'client'); + is((yield clientQueue.waitForEvent()).type, 'open', 'got open event'); + + connectedResult = yield connectedPromise; + // destructuring assignment is not yet ES6 compliant, must manually unpack + serverSocket = connectedResult.socket; + serverQueue = connectedResult.queue; + + // -- Call close after enqueueing a lot of data, make sure it goes through. + // We'll have the client send and close. + is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false, + 'Client sending more than 64k should result in the buffer being full.'); + clientSocket.close(); + // The drain will still fire + is((yield clientQueue.waitForEvent()).type, 'drain', + 'The drain event should fire after a large send that returned true.'); + // Then we'll get a close + is((yield clientQueue.waitForEvent()).type, 'close', + 'The close event should fire after the drain event.'); + + // The server will get its data + serverReceived = yield serverQueue.waitForDataWithAtLeastLength( + bigUint8Array.length); + assertUint8ArraysEqual(serverReceived, bigUint8Array, + 'server received/client sent'); + // And a close. + is((yield serverQueue.waitForEvent()).type, 'close', + 'The drain event should fire after a large send that returned true.'); + + + // -- Close the listening server (and try to connect) + // We want to verify that the server actually closes / stops listening when + // we tell it to. + listeningServer.close(); + + // - try and connect, get an error + clientSocket = TCPSocket.open('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); + clientQueue = listenForEventsOnSocket(clientSocket, 'client'); + is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect'); + is(clientSocket.readyState, 'closed', + 'client readyState should be closed after the failure to connect'); +} + +add_task(test_basics);
deleted file mode 100644 --- a/dom/network/tests/unit/test_multisend.js +++ /dev/null @@ -1,152 +0,0 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; -const CC = Components.Constructor; - -Cu.import("resource://gre/modules/Services.jsm"); - -const ServerSocket = CC("@mozilla.org/network/server-socket;1", - "nsIServerSocket", - "init"), - InputStreamPump = CC("@mozilla.org/network/input-stream-pump;1", - "nsIInputStreamPump", - "init"), - BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"), - BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", - "nsIBinaryOutputStream", - "setOutputStream"), - TCPSocket = new (CC("@mozilla.org/tcp-socket;1", - "nsIDOMTCPSocket"))(); - -var server = null, sock = null; - -/** - * Spin up a listening socket and associate at most one live, accepted socket - * with ourselves. - */ -function TestServer() { - this.listener = ServerSocket(-1, true, -1); - do_print('server: listening on ' + this.listener.port); - this.listener.asyncListen(this); - - this.binaryInput = null; - this.input = null; - this.binaryOutput = null; - this.output = null; - - this.onaccept = null; - this.ondata = null; - this.onclose = null; -} - -TestServer.prototype = { - onSocketAccepted: function(socket, trans) { - if (this.input) - do_throw("More than one live connection!?"); - - do_print('server: got client connection'); - this.input = trans.openInputStream(0, 0, 0); - this.binaryInput = new BinaryInputStream(this.input); - this.output = trans.openOutputStream(0, 0, 0); - this.binaryOutput = new BinaryOutputStream(this.output); - - new InputStreamPump(this.input, -1, -1, 0, 0, false).asyncRead(this, null); - - if (this.onaccept) - this.onaccept(); - else - do_throw("Received unexpected connection!"); - }, - - onStopListening: function(socket) { - }, - - onDataAvailable: function(request, context, inputStream, offset, count) { - var readData = this.binaryInput.readByteArray(count); - if (this.ondata) { - try { - this.ondata(readData); - } catch(ex) { - // re-throw if this is from do_throw - if (ex === Cr.NS_ERROR_ABORT) - throw ex; - // log if there was a test problem - do_print('Caught exception: ' + ex + '\n' + ex.stack); - do_throw('test is broken; bad ondata handler; see above'); - } - } else { - do_throw('Received ' + count + ' bytes of unexpected data!'); - } - }, - - onStartRequest: function(request, context) { - }, - - onStopRequest: function(request, context, status) { - if (this.onclose) - this.onclose(); - else - do_throw("Received unexpected close!"); - }, - - close: function() { - this.binaryInput.close(); - this.binaryOutput.close(); - }, - - /** - * Forget about the socket we knew about before. - */ - reset: function() { - this.binaryInput = null; - this.input = null; - this.binaryOutput = null; - this.output = null; - }, -}; - -function run_test() { - Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true); - - do_test_pending(); - - server = new TestServer(); - server.reset(); - sock = TCPSocket.open( - '127.0.0.1', server.listener.port, - { binaryType: 'arraybuffer' }); - - var encoder = new TextEncoder(); - var ok = encoder.encode("OKBYE"); - - var expected = ['O', 'K', 'B', 'Y', 'E'] - .map(function(c) { return c.charCodeAt(0); }); - var seenData = []; - - server.onaccept = function() {}; - server.ondata = function(data) { - do_print(data + ":" + data.length); - - seenData = seenData.concat(data); - - if (seenData.length == expected.length) { - do_print(expected); - do_check_eq(seenData.length, expected.length); - for (var i = 0; i < seenData.length; i++) { - do_check_eq(seenData[i], expected[i]); - } - sock.close(); - server.close(); - do_test_finished(); - } - }; - server.onclose = function() {}; - - sock.onopen = function() { - sock.send(ok.buffer, 0, 2); - sock.send(ok.buffer, 2, 3); - }; -}
deleted file mode 100644 --- a/dom/network/tests/unit/test_tcpserversocket.js +++ /dev/null @@ -1,311 +0,0 @@ -/** - * Test TCPSocket.js by creating an XPCOM-style server socket, then sending - * data in both directions and making sure each side receives their data - * correctly and with the proper events. - * - * This test is derived from netwerk/test/unit/test_socks.js, except we don't - * involve a subprocess. - * - * Future work: - * - SSL. see https://bugzilla.mozilla.org/show_bug.cgi?id=466524 - * https://bugzilla.mozilla.org/show_bug.cgi?id=662180 - * Alternatively, mochitests could be used. - * - Testing overflow logic. - * - **/ - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; -const CC = Components.Constructor; - -/** - * - * Constants - * - */ - -// Test parameter. -const PORT = 8085; -const BACKLOG = -1; - -// Some binary data to send. -const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0], - DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length), - TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER), - HELLO_WORLD = "hlo wrld. ", - BIG_ARRAY = new Array(65539); - -TYPED_DATA_ARRAY.set(DATA_ARRAY, 0); - -for (var i_big = 0; i_big < BIG_ARRAY.length; i_big++) { - BIG_ARRAY[i_big] = Math.floor(Math.random() * 256); -} - -const BIG_ARRAY_BUFFER = new ArrayBuffer(BIG_ARRAY.length); -const BIG_TYPED_ARRAY = new Uint8Array(BIG_ARRAY_BUFFER); -BIG_TYPED_ARRAY.set(BIG_ARRAY); - -const TCPSocket = new (CC("@mozilla.org/tcp-socket;1", - "nsIDOMTCPSocket"))(); - -const gInChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime) - .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -/** - * - * Helper functions - * - */ - - -function makeSuccessCase(name) { - return function() { - do_print('got expected: ' + name); - run_next_test(); - }; -} - -function makeJointSuccess(names) { - let funcs = {}, successCount = 0; - names.forEach(function(name) { - funcs[name] = function() { - do_print('got expected: ' + name); - if (++successCount === names.length) - run_next_test(); - }; - }); - return funcs; -} - -function makeFailureCase(name) { - return function() { - let argstr; - if (arguments.length) { - argstr = '(args: ' + - Array.map(arguments, function(x) { return x.data + ""; }).join(" ") + ')'; - } - else { - argstr = '(no arguments)'; - } - do_throw('got unexpected: ' + name + ' ' + argstr); - }; -} - -function makeExpectData(name, expectedData, fromEvent, callback) { - let dataBuffer = fromEvent ? null : [], done = false; - let dataBufferView = null; - return function(receivedData) { - if (receivedData.data) { - receivedData = receivedData.data; - } - let recvLength = receivedData.byteLength !== undefined ? - receivedData.byteLength : receivedData.length; - - if (fromEvent) { - if (dataBuffer) { - let newBuffer = new ArrayBuffer(dataBuffer.byteLength + recvLength); - let newBufferView = new Uint8Array(newBuffer); - newBufferView.set(dataBufferView, 0); - newBufferView.set(receivedData, dataBuffer.byteLength); - dataBuffer = newBuffer; - dataBufferView = newBufferView; - } - else { - dataBuffer = receivedData; - dataBufferView = new Uint8Array(dataBuffer); - } - } - else { - dataBuffer = dataBuffer.concat(receivedData); - } - do_print(name + ' received ' + recvLength + ' bytes'); - - if (done) - do_throw(name + ' Received data event when already done!'); - - let dataView = dataBuffer.byteLength !== undefined ? new Uint8Array(dataBuffer) : dataBuffer; - if (dataView.length >= expectedData.length) { - // check the bytes are equivalent - for (let i = 0; i < expectedData.length; i++) { - if (dataView[i] !== expectedData[i]) { - do_throw(name + ' Received mismatched character at position ' + i); - } - } - if (dataView.length > expectedData.length) - do_throw(name + ' Received ' + dataView.length + ' bytes but only expected ' + - expectedData.length + ' bytes.'); - - done = true; - if (callback) { - callback(); - } else { - run_next_test(); - } - } - }; -} - -var server = null, sock = null, connectedsock = null, failure_drain = null; -var count = 0; -/** - * - * Test functions - * - */ - -/** - * Connect the socket to the server. This test is added as the first - * test, and is also added after every test which results in the socket - * being closed. - */ - -function connectSock() { - if (server) { - server.close(); - } - - var yayFuncs = makeJointSuccess(['serveropen', 'clientopen']); - var options = { binaryType: 'arraybuffer' }; - - server = TCPSocket.listen(PORT, options, BACKLOG); - server.onconnect = function(socket) { - // Bug 937528 - Accepted client tcp socket (mozTcpSocket) has - // uninitialized host and port. - if (socket.host !== '127.0.0.1') { - do_throw('got unexpected: connected socket host should be 127.0.0.1 but not ' + socket.host); - } else { - do_print('Got expected connected socket host: ' + socket.host); - } - - connectedsock = socket; - connectedsock.ondata = makeFailureCase('serverdata'); - connectedsock.onerror = makeFailureCase('servererror'); - connectedsock.onclose = makeFailureCase('serverclose'); - yayFuncs.serveropen(); - }; - server.onerror = makeFailureCase('error'); - sock = TCPSocket.open( - '127.0.0.1', PORT, options); - sock.onopen = yayFuncs.clientopen; - sock.ondrain = null; - sock.ondata = makeFailureCase('data'); - sock.onerror = makeFailureCase('error'); - sock.onclose = makeFailureCase('close'); -} - -/** - * Connect the socket to the server after the server was closed. - * This test is added after test to close the server was conducted. - */ -function openSockInClosingServer() { - var success = makeSuccessCase('clientnotopen'); - var options = { binaryType: 'arraybuffer' }; - - sock = TCPSocket.open( - '127.0.0.1', PORT, options); - - sock.onopen = makeFailureCase('open'); - sock.onerror = success; -} - -/** - * Test that sending a small amount of data works, and that buffering - * does not take place for this small amount of data. - */ - -function sendDataToServer() { - connectedsock.ondata = makeExpectData('serverdata', DATA_ARRAY, true); - if (!sock.send(DATA_ARRAY_BUFFER)) { - do_throw("send should not have buffered such a small amount of data"); - } -} - -/** - * Test that data sent from the server correctly fires the ondata - * callback on the client side. - */ - -function receiveDataFromServer() { - connectedsock.ondata = makeFailureCase('serverdata'); - sock.ondata = makeExpectData('data', DATA_ARRAY, true); - - connectedsock.send(DATA_ARRAY_BUFFER); -} - -/** - * Test that when the server closes the connection, the onclose callback - * is fired on the client side. - */ - -function serverCloses() { - // we don't really care about the server's close event, but we do want to - // make sure it happened for sequencing purposes. - sock.ondata = makeFailureCase('data'); - sock.onclose = makeFailureCase('close1'); - connectedsock.onclose = makeFailureCase('close2'); - - server.close(); - run_next_test(); -} - -/** - * Test that when the client closes the connection, the onclose callback - * is fired on the server side. - */ - -function cleanup() { - do_print("Cleaning up"); - sock.onclose = null; - connectedsock.onclose = null; - - server.close(); - sock.close(); - if (count == 1){ - if (!gInChild) - Services.prefs.clearUserPref('dom.mozTCPSocket.enabled'); - } - count++; - run_next_test(); -} -// - connect, data and events work both ways -add_test(connectSock); - -add_test(sendDataToServer); - -add_test(receiveDataFromServer); -// - server closes on us -add_test(serverCloses); - -// - send and receive after closing server -add_test(sendDataToServer); -add_test(receiveDataFromServer); -// - check a connection refused from client to server after closing server -add_test(serverCloses); - -add_test(openSockInClosingServer); - -// - clean up -add_test(cleanup); - -// - send and receive in reverse order for client and server -add_test(connectSock); - -add_test(receiveDataFromServer); - -add_test(sendDataToServer); - -// - clean up - -add_test(cleanup); - -function run_test() { - if (!gInChild) - Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true); - - run_next_test(); -}
--- a/dom/network/tests/unit/test_tcpsocket.js +++ b/dom/network/tests/unit/test_tcpsocket.js @@ -69,22 +69,16 @@ const gInChild = Cc["@mozilla.org/xre/ap Cu.import("resource://gre/modules/Services.jsm"); /** * * Helper functions * */ -function get_platform() { - var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"] - .getService(Components.interfaces.nsIXULRuntime); - return xulRuntime.OS; -} - function is_content() { return this._inChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime) .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; } /** * Spin up a listening socket and associate at most one live, accepted socket * with ourselves. @@ -285,231 +279,24 @@ function connectSock() { sock.onerror = makeFailureCase('error'); sock.onclose = makeFailureCase('close'); server.onconnect = yayFuncs.serveropen; server.ondata = makeFailureCase('serverdata'); server.onclose = makeFailureCase('serverclose'); } -/** - * Test that sending a small amount of data works, and that buffering - * does not take place for this small amount of data. - */ - -function sendData() { - server.ondata = makeExpectData('serverdata', DATA_ARRAY); - if (!sock.send(DATA_ARRAY_BUFFER)) { - do_throw("send should not have buffered such a small amount of data"); - } -} - -/** - * Test that sending a large amount of data works, that buffering - * takes place (send returns true), and that ondrain is called once - * the data has been sent. - */ - -function sendBig() { - var yays = makeJointSuccess(['serverdata', 'clientdrain']), - amount = 0; - - server.ondata = function (data) { - amount += data.length; - if (amount === BIG_TYPED_ARRAY.length) { - yays.serverdata(); - } - }; - sock.ondrain = function(evt) { - if (sock.bufferedAmount) { - do_throw("sock.bufferedAmount was > 0 in ondrain"); - } - yays.clientdrain(evt); - } - if (sock.send(BIG_ARRAY_BUFFER)) { - do_throw("expected sock.send to return false on large buffer send"); - } -} - -/** - * Test that data sent from the server correctly fires the ondata - * callback on the client side. - */ - -function receiveData() { - server.ondata = makeFailureCase('serverdata'); - sock.ondata = makeExpectData('data', DATA_ARRAY, true); - - server.binaryOutput.writeByteArray(DATA_ARRAY, DATA_ARRAY.length); -} - -/** - * Test that when the server closes the connection, the onclose callback - * is fired on the client side. - */ - -function serverCloses() { - // we don't really care about the server's close event, but we do want to - // make sure it happened for sequencing purposes. - var yayFuncs = makeJointSuccess(['clientclose', 'serverclose']); - sock.ondata = makeFailureCase('data'); - sock.onclose = yayFuncs.clientclose; - server.onclose = yayFuncs.serverclose; - - server.close(); -} - -/** - * Test that when the client closes the connection, the onclose callback - * is fired on the server side. - */ - -function clientCloses() { - // we want to make sure the server heard the close and also that the client's - // onclose event fired for consistency. - var yayFuncs = makeJointSuccess(['clientclose', 'serverclose']); - server.onclose = yayFuncs.serverclose; - sock.onclose = yayFuncs.clientclose; - - sock.close(); -} - -/** - * Send a large amount of data and immediately call close - */ - -function bufferedClose() { - var yays = makeJointSuccess(['serverdata', 'clientclose', 'serverclose']); - server.ondata = makeExpectData( - "ondata", BIG_TYPED_ARRAY, false, yays.serverdata); - server.onclose = yays.serverclose; - sock.onclose = yays.clientclose; - sock.send(BIG_ARRAY_BUFFER); - sock.close(); -} - -/** - * Connect to a port we know is not listening so an error is assured, - * and make sure that onerror and onclose are fired on the client side. - */ - -function badConnect() { - // There's probably nothing listening on tcp port 2. - sock = TCPSocket.open('127.0.0.1', 2); - - sock.onopen = makeFailureCase('open'); - sock.ondata = makeFailureCase('data'); - - let success = makeSuccessCase('error'); - let gotError = false; - sock.onerror = function(event) { - do_check_eq(event.data.name, 'ConnectionRefusedError'); - gotError = true; - }; - sock.onclose = function() { - if (!gotError) - do_throw('got close without error!'); - else - success(); - }; -} - -/** - * Test that calling send with enough data to buffer causes ondrain to - * be invoked once the data has been sent, and then test that calling send - * and buffering again causes ondrain to be fired again. - */ - -function drainTwice() { - let yays = makeJointSuccess( - ['ondrain', 'ondrain2', - 'ondata', 'ondata2', - 'serverclose', 'clientclose']); - let ondrainCalled = false, - ondataCalled = false; - - function maybeSendNextData() { - if (!ondrainCalled || !ondataCalled) { - // make sure server got data and client got ondrain. - return; - } - - server.ondata = makeExpectData( - "ondata2", BIG_TYPED_ARRAY_2, false, yays.ondata2); - - sock.ondrain = yays.ondrain2; - - if (sock.send(BIG_ARRAY_BUFFER_2)) { - do_throw("sock.send(BIG_TYPED_ARRAY_2) did not return false to indicate buffering"); - } - - sock.close(); - } - - function clientOndrain() { - yays.ondrain(); - ondrainCalled = true; - maybeSendNextData(); - } - - function serverSideCallback() { - yays.ondata(); - ondataCalled = true; - maybeSendNextData(); - } - - server.onclose = yays.serverclose; - server.ondata = makeExpectData( - "ondata", BIG_TYPED_ARRAY, false, serverSideCallback); - - sock.onclose = yays.clientclose; - sock.ondrain = clientOndrain; - - if (sock.send(BIG_ARRAY_BUFFER)) { - throw new Error("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering"); - } -} - function cleanup() { do_print("Cleaning up"); sock.close(); if (!gInChild) Services.prefs.clearUserPref('dom.mozTCPSocket.enabled'); run_next_test(); } -/** - * Test that calling send with enough data to buffer twice in a row without - * waiting for ondrain still results in ondrain being invoked at least once. - */ - -function bufferTwice() { - let yays = makeJointSuccess( - ['ondata', 'ondrain', 'serverclose', 'clientclose']); - - let double_array = new Uint8Array(BIG_ARRAY.concat(BIG_ARRAY_2)); - server.ondata = makeExpectData( - "ondata", double_array, false, yays.ondata); - - server.onclose = yays.serverclose; - sock.onclose = yays.clientclose; - - sock.ondrain = function () { - sock.close(); - yays.ondrain(); - } - - if (sock.send(BIG_ARRAY_BUFFER)) { - throw new Error("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering"); - } - if (sock.send(BIG_ARRAY_BUFFER_2)) { - throw new Error("sock.send(BIG_TYPED_ARRAY_2) did not return false to indicate buffering on second synchronous call to send"); - } -} - // Test child behavior when child thinks it's buffering but parent doesn't // buffer. // 1. set bufferedAmount of content socket to a value that will make next // send() call return false. // 2. send a small data to make send() return false, but it won't make // parent buffer. // 3. we should get a ondrain. function childbuffered() { @@ -556,58 +343,29 @@ function childnotbuffered() { sock.ondrain = makeFailureCase('drain'); sock.onclose = yays.clientclose; server.onclose = yays.serverclose; do_timeout(1000, function() { sock.close(); }); }; -// - connect, data and events work both ways -add_test(connectSock); -add_test(sendData); -add_test(sendBig); -add_test(receiveData); -// - server closes on us -add_test(serverCloses); - -// - connect, we close on the server -add_test(connectSock); -add_test(clientCloses); - -// - connect, buffer, close -add_test(connectSock); -add_test(bufferedClose); - -if (get_platform() !== "Darwin") { - // This test intermittently fails way too often on OS X, for unknown reasons. - // Please, diagnose and fix it if you can. - // - get an error on an attempt to connect to a non-listening port - add_test(badConnect); -} - -// send a buffer, get a drain, send a buffer, get a drain -add_test(connectSock); -add_test(drainTwice); - -// send a buffer, get a drain, send a buffer, get a drain -add_test(connectSock); -add_test(bufferTwice); - if (is_content()) { add_test(connectSock); add_test(childnotbuffered); add_test(connectSock); add_test(childbuffered); + + // clean up + add_test(cleanup); +} else { + do_check_true(true, 'non-content process wants to pretend to one test'); } -// clean up -add_test(cleanup); - function run_test() { if (!gInChild) Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true); server = new TestServer(); run_next_test(); }
--- a/dom/network/tests/unit/xpcshell.ini +++ b/dom/network/tests/unit/xpcshell.ini @@ -1,10 +1,6 @@ [DEFAULT] head = tail = skip-if = toolkit == 'gonk' [test_tcpsocket.js] -[test_multisend.js] -[test_tcpserversocket.js] -run-sequentially = Uses hardcoded port, bug 903830. -skip-if = os == 'mac' # bug 953208 - frequent timeouts on OSX
deleted file mode 100644 --- a/dom/network/tests/unit_ipc/test_tcpserversocket_ipc.js +++ /dev/null @@ -1,9 +0,0 @@ -Components.utils.import("resource://gre/modules/Services.jsm"); - -function run_test() { - Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true); - run_test_in_child("../unit/test_tcpserversocket.js", function() { - Services.prefs.clearUserPref('dom.mozTCPSocket.enabled'); - do_test_finished(); - }); -}
--- a/dom/network/tests/unit_ipc/xpcshell.ini +++ b/dom/network/tests/unit_ipc/xpcshell.ini @@ -1,8 +1,6 @@ [DEFAULT] head = tail = skip-if = toolkit == 'android' || toolkit == 'gonk' [test_tcpsocket_ipc.js] -[test_tcpserversocket_ipc.js] -run-sequentially = Uses hardcoded port, bug 903830.
--- a/dom/plugins/base/nsPluginsDirDarwin.cpp +++ b/dom/plugins/base/nsPluginsDirDarwin.cpp @@ -469,47 +469,51 @@ nsresult nsPluginFile::GetPluginInfo(nsP // First look for data in a bundle plist if (bundle) { ParsePlistPluginInfo(info, bundle); ::CFRelease(bundle); if (info.fVariantCount > 0) return NS_OK; } - // Don't load "fbplugin" (a Facebook plugin) if we're running on OS X 10.10 - // (Yosemite) or later. It crashes on load, in the call to LoadPlugin() - // below. See bug 1086977. + // Don't load "fbplugin" or any plugins whose name starts with "fbplugin_" + // (Facebook plugins) if we're running on OS X 10.10 (Yosemite) or later. + // A "fbplugin" file crashes on load, in the call to LoadPlugin() below. + // See bug 1086977. if (nsCocoaFeatures::OnYosemiteOrLater()) { - if (fileName.EqualsLiteral("fbplugin")) { - NS_WARNING("Preventing load of fbplugin (see bug 1086977)"); + if (fileName.EqualsLiteral("fbplugin") || + StringBeginsWith(fileName, NS_LITERAL_CSTRING("fbplugin_"))) { + nsAutoCString msg; + msg.AppendPrintf("Preventing load of %s (see bug 1086977)", + fileName.get()); + NS_WARNING(msg.get()); return NS_ERROR_FAILURE; } #if defined(MOZ_CRASHREPORTER) // The block above assumes that "fbplugin" is the filename of the plugin - // to be blocked, but be don't yet know for sure if this is true. It might - // also be the name of a file indirectly loaded by the plugin. So for the - // time being we must record extra information in our crash logs. - CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug 1086977"), + // to be blocked, or that the filename starts with "fbplugin_". But we + // don't yet know for sure if this is always true. So for the time being + // record extra information in our crash logs. + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"), fileName); #endif } // It's possible that our plugin has 2 entry points that'll give us mime type // info. Quicktime does this to get around the need of having admin rights to // change mime info in the resource fork. We need to use this info instead of // the resource. See bug 113464. // Sadly we have to load the library for this to work. rv = LoadPlugin(outLibrary); #if defined(MOZ_CRASHREPORTER) if (nsCocoaFeatures::OnYosemiteOrLater()) { // If we didn't crash in LoadPlugin(), change the previous annotation so we - // don't sow confusion. Unfortunately there's not (yet) any way to get rid - // of the annotation completely. - CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug 1086977"), + // don't sow confusion. + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"), NS_LITERAL_CSTRING("Didn't crash, please ignore")); } #endif if (NS_FAILED(rv)) return rv; // Try to get data from NP_GetMIMEDescription if (pLibrary) {
--- a/dom/plugins/ipc/PluginScriptableObjectChild.h +++ b/dom/plugins/ipc/PluginScriptableObjectChild.h @@ -184,18 +184,18 @@ private: ~StoredIdentifier() { MOZ_COUNT_DTOR(StoredIdentifier); } }; public: class MOZ_STACK_CLASS StackIdentifier { public: - StackIdentifier(const PluginIdentifier& aIdentifier); - StackIdentifier(NPIdentifier aIdentifier); + explicit StackIdentifier(const PluginIdentifier& aIdentifier); + explicit StackIdentifier(NPIdentifier aIdentifier); ~StackIdentifier(); void MakePermanent() { if (mStored) { mStored->mPermanent = true; } }
--- a/dom/plugins/ipc/PluginScriptableObjectParent.cpp +++ b/dom/plugins/ipc/PluginScriptableObjectParent.cpp @@ -32,17 +32,18 @@ using namespace mozilla::plugins::parent * on the stack: although StackIdentifier roots all identifiers used, the GC has * no way to no that a jsid cast to an NPIdentifier needs to be fixed up if it * is moved. */ class MOZ_STACK_CLASS StackIdentifier { public: - StackIdentifier(const PluginIdentifier& aIdentifier, bool aIntern = false); + explicit StackIdentifier(const PluginIdentifier& aIdentifier, + bool aIntern = false); bool Failed() const { return mFailed; } NPIdentifier ToNPIdentifier() const { return mIdentifier; } private: bool mFailed; NPIdentifier mIdentifier; AutoSafeJSContext mCx;
deleted file mode 100644 --- a/dom/plugins/ipc/hangui/Makefile.in +++ /dev/null @@ -1,7 +0,0 @@ -# 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/. - -MOZ_GLUE_LDFLAGS = - -include $(topsrcdir)/config/rules.mk
--- a/dom/svg/SVGRectElement.cpp +++ b/dom/svg/SVGRectElement.cpp @@ -109,39 +109,43 @@ SVGRectElement::GetLengthInfo() //---------------------------------------------------------------------- // nsSVGPathGeometryElement methods bool SVGRectElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth, const Matrix& aTransform) { - Rect r; + Rect rect; Float rx, ry; - GetAnimatedLengthValues(&r.x, &r.y, &r.width, &r.height, &rx, &ry, nullptr); + GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width, + &rect.height, &rx, &ry, nullptr); - if (r.IsEmpty()) { + if (rect.IsEmpty()) { // Rendering of the element disabled - r.SetEmpty(); // make sure width/height are actually zero - *aBounds = r; + rect.SetEmpty(); // Make sure width/height are zero and not negative + *aBounds = rect; // We still want the x/y position from 'rect' return true; } - rx = std::max(rx, 0.0f); - ry = std::max(ry, 0.0f); + if (!aTransform.IsRectilinear()) { + // We can't ignore the radii in this case if we want tight bounds + rx = std::max(rx, 0.0f); + ry = std::max(ry, 0.0f); - if (rx != 0 || ry != 0) { - return false; + if (rx != 0 || ry != 0) { + return false; + } } if (aStrokeWidth > 0.f) { - r.Inflate(aStrokeWidth / 2.f); + rect.Inflate(aStrokeWidth / 2.f); } - *aBounds = aTransform.TransformBounds(r); + *aBounds = aTransform.TransformBounds(rect); return true; } void SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath) { float x, y, width, height, rx, ry; GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr); @@ -195,18 +199,17 @@ SVGRectElement::BuildPath(PathBuilder* a } else if (hasRy && !hasRx) { rx = ry; } // Clamp rx and ry to half the rect's width and height respectively: rx = std::min(rx, width / 2); ry = std::min(ry, height / 2); - Size cornerRadii(rx, ry); - Size radii[] = { cornerRadii, cornerRadii, cornerRadii, cornerRadii }; + RectCornerRadii radii(rx, ry); AppendRoundedRectToPath(aBuilder, Rect(x, y, width, height), radii); } return aBuilder->Finish(); } } // namespace dom } // namespace mozilla
--- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -62,17 +62,17 @@ public: } }; class Telephony::EnumerationAck : public nsRunnable { nsRefPtr<Telephony> mTelephony; public: - EnumerationAck(Telephony* aTelephony) + explicit EnumerationAck(Telephony* aTelephony) : mTelephony(aTelephony) { MOZ_ASSERT(mTelephony); } NS_IMETHOD Run() { mTelephony->NotifyEvent(NS_LITERAL_STRING("ready"));
--- a/dom/telephony/TelephonyCallback.h +++ b/dom/telephony/TelephonyCallback.h @@ -17,17 +17,17 @@ class Promise; namespace telephony { class TelephonyCallback : public nsITelephonyCallback { public: NS_DECL_ISUPPORTS NS_DECL_NSITELEPHONYCALLBACK - TelephonyCallback(Promise* aPromise); + explicit TelephonyCallback(Promise* aPromise); protected: virtual ~TelephonyCallback() {} protected: nsRefPtr<Promise> mPromise; };
--- a/dom/webidl/CompositionEvent.webidl +++ b/dom/webidl/CompositionEvent.webidl @@ -4,26 +4,24 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. * * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html#events-CompositionEvent * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. */ -interface WindowProxy; - interface CompositionEvent : UIEvent { readonly attribute DOMString? data; readonly attribute DOMString locale; }; partial interface CompositionEvent { [Throws] void initCompositionEvent(DOMString typeArg, boolean canBubbleArg, boolean cancelableArg, - WindowProxy? viewArg, + Window? viewArg, DOMString? dataArg, DOMString localeArg); };
--- a/dom/webidl/DragEvent.webidl +++ b/dom/webidl/DragEvent.webidl @@ -1,25 +1,23 @@ /* -*- Mode: IDL; 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/. */ -interface WindowProxy; - interface DragEvent : MouseEvent { readonly attribute DataTransfer? dataTransfer; [Throws] void initDragEvent(DOMString type, boolean canBubble, boolean cancelable, - WindowProxy? aView, + Window? aView, long aDetail, long aScreenX, long aScreenY, long aClientX, long aClientY, boolean aCtrlKey, boolean aAltKey, boolean aShiftKey,
--- a/dom/webidl/KeyEvent.webidl +++ b/dom/webidl/KeyEvent.webidl @@ -1,16 +1,14 @@ /* -*- Mode: IDL; 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/. */ -interface WindowProxy; - // http://www.w3.org/TR/1999/WD-DOM-Level-2-19990923/events.html#Events-KeyEvent interface KeyEvent { const unsigned long DOM_VK_CANCEL = 0x03; const unsigned long DOM_VK_HELP = 0x06; const unsigned long DOM_VK_BACK_SPACE = 0x08; const unsigned long DOM_VK_TAB = 0x09; const unsigned long DOM_VK_CLEAR = 0x0C; @@ -225,16 +223,16 @@ interface KeyEvent // OEM specific virtual keyCode of Windows should pass through DOM keyCode // for compatibility with the other web browsers on Windows. const unsigned long DOM_VK_WIN_OEM_CLEAR = 0xFE; [Throws] void initKeyEvent(DOMString type, boolean canBubble, boolean cancelable, - WindowProxy? view, + Window? view, boolean ctrlKey, boolean altKey, boolean shiftKey, boolean metaKey, unsigned long keyCode, unsigned long charCode); };
--- a/dom/webidl/KeyboardEvent.webidl +++ b/dom/webidl/KeyboardEvent.webidl @@ -1,16 +1,14 @@ /* -*- Mode: IDL; 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/. */ -interface WindowProxy; - [Constructor(DOMString typeArg, optional KeyboardEventInit keyboardEventInitDict)] interface KeyboardEvent : UIEvent { readonly attribute unsigned long charCode; readonly attribute unsigned long keyCode; readonly attribute boolean altKey; readonly attribute boolean ctrlKey;
--- a/dom/webidl/MessageEvent.webidl +++ b/dom/webidl/MessageEvent.webidl @@ -2,18 +2,16 @@ /* 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/. * * For more information on this interface, please see * http://www.whatwg.org/specs/web-apps/current-work/#messageevent */ -interface WindowProxy; - [Constructor(DOMString type, optional MessageEventInit eventInitDict), Exposed=(Window,Worker,System)] interface MessageEvent : Event { /** * Custom data associated with this event. */ [GetterThrows] readonly attribute any data; @@ -44,11 +42,11 @@ interface MessageEvent : Event { */ readonly attribute MessagePortList? ports; }; dictionary MessageEventInit : EventInit { any data; DOMString origin; DOMString lastEventId; - (WindowProxy or MessagePort)? source = null; + (Window or MessagePort)? source = null; sequence<MessagePort>? ports; };
--- a/dom/webidl/MouseEvent.webidl +++ b/dom/webidl/MouseEvent.webidl @@ -23,17 +23,17 @@ interface MouseEvent : UIEvent { readonly attribute unsigned short buttons; readonly attribute EventTarget? relatedTarget; readonly attribute DOMString? region; // Deprecated in DOM Level 3: [Throws] void initMouseEvent(DOMString typeArg, boolean canBubbleArg, boolean cancelableArg, - WindowProxy? viewArg, + Window? viewArg, long detailArg, long screenXArg, long screenYArg, long clientXArg, long clientYArg, boolean ctrlKeyArg, boolean altKeyArg, boolean shiftKeyArg, @@ -47,25 +47,17 @@ interface MouseEvent : UIEvent { // Event Constructor Syntax: [Constructor(DOMString typeArg, optional MouseEventInit mouseEventInitDict)] partial interface MouseEvent { }; // Suggested initMouseEvent replacement initializer: -dictionary MouseEventInit { - // Attributes from Event: - boolean bubbles = false; - boolean cancelable = false; - - // Attributes from UIEvent: - WindowProxy? view = null; - long detail = 0; - +dictionary MouseEventInit : UIEventInit { // Attributes for MouseEvent: long screenX = 0; long screenY = 0; long clientX = 0; long clientY = 0; boolean ctrlKey = false; boolean shiftKey = false; boolean altKey = false; @@ -95,17 +87,17 @@ partial interface MouseEvent const unsigned short MOZ_SOURCE_KEYBOARD = 6; readonly attribute unsigned short mozInputSource; [Throws] void initNSMouseEvent(DOMString typeArg, boolean canBubbleArg, boolean cancelableArg, - WindowProxy? viewArg, + Window? viewArg, long detailArg, long screenXArg, long screenYArg, long clientXArg, long clientYArg, boolean ctrlKeyArg, boolean altKeyArg, boolean shiftKeyArg,
--- a/dom/webidl/MouseScrollEvent.webidl +++ b/dom/webidl/MouseScrollEvent.webidl @@ -1,28 +1,26 @@ /* -*- Mode: IDL; 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/. */ -interface WindowProxy; - interface MouseScrollEvent : MouseEvent { const long HORIZONTAL_AXIS = 1; const long VERTICAL_AXIS = 2; readonly attribute long axis; [Throws] void initMouseScrollEvent(DOMString type, boolean canBubble, boolean cancelable, - WindowProxy? view, + Window? view, long detail, long screenX, long screenY, long clientX, long clientY, boolean ctrlKey, boolean altKey, boolean shiftKey,
--- a/dom/webidl/ScrollAreaEvent.webidl +++ b/dom/webidl/ScrollAreaEvent.webidl @@ -1,26 +1,24 @@ /* -*- Mode: IDL; 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/. */ -interface WindowProxy; - interface ScrollAreaEvent : UIEvent { readonly attribute float x; readonly attribute float y; readonly attribute float width; readonly attribute float height; [Throws] void initScrollAreaEvent(DOMString type, boolean canBubble, boolean cancelable, - WindowProxy? view, + Window? view, long detail, float x, float y, float width, float height); };
--- a/dom/webidl/SimpleGestureEvent.webidl +++ b/dom/webidl/SimpleGestureEvent.webidl @@ -1,18 +1,16 @@ /* -*- Mode: IDL; 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/. * * For more information see nsIDOMSimpleGestureEvent.idl. */ -interface WindowProxy; - interface SimpleGestureEvent : MouseEvent { const unsigned long DIRECTION_UP = 1; const unsigned long DIRECTION_DOWN = 2; const unsigned long DIRECTION_LEFT = 4; const unsigned long DIRECTION_RIGHT = 8; const unsigned long ROTATION_COUNTERCLOCKWISE = 1; @@ -25,17 +23,17 @@ interface SimpleGestureEvent : MouseEven readonly attribute double delta; readonly attribute unsigned long clickCount; [Throws] void initSimpleGestureEvent(DOMString typeArg, boolean canBubbleArg, boolean cancelableArg, - WindowProxy? viewArg, + Window? viewArg, long detailArg, long screenXArg, long screenYArg, long clientXArg, long clientYArg, boolean ctrlKeyArg, boolean altKeyArg, boolean shiftKeyArg,
--- a/dom/webidl/TimeEvent.webidl +++ b/dom/webidl/TimeEvent.webidl @@ -5,18 +5,16 @@ * * For more information on this interface please see * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. */ -interface WindowProxy; - interface TimeEvent : Event { readonly attribute long detail; readonly attribute WindowProxy? view; void initTimeEvent(DOMString aType, - WindowProxy? aView, + Window? aView, long aDetail); };
--- a/dom/webidl/TouchEvent.webidl +++ b/dom/webidl/TouchEvent.webidl @@ -1,32 +1,30 @@ /* -*- Mode: IDL; 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/. */ -interface WindowProxy; - [Func="mozilla::dom::TouchEvent::PrefEnabled"] interface TouchEvent : UIEvent { readonly attribute TouchList touches; readonly attribute TouchList targetTouches; readonly attribute TouchList changedTouches; readonly attribute boolean altKey; readonly attribute boolean metaKey; readonly attribute boolean ctrlKey; readonly attribute boolean shiftKey; [Throws] void initTouchEvent(DOMString type, boolean canBubble, boolean cancelable, - WindowProxy? view, + Window? view, long detail, boolean ctrlKey, boolean altKey, boolean shiftKey, boolean metaKey, TouchList? touches, TouchList? targetTouches, TouchList? changedTouches);
--- a/dom/webidl/UIEvent.webidl +++ b/dom/webidl/UIEvent.webidl @@ -5,27 +5,25 @@ * * For more information on this interface please see * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. */ -interface WindowProxy; - [Constructor(DOMString type, optional UIEventInit eventInitDict)] interface UIEvent : Event { readonly attribute WindowProxy? view; readonly attribute long detail; void initUIEvent(DOMString aType, boolean aCanBubble, boolean aCancelable, - WindowProxy? aView, + Window? aView, long aDetail); }; // Additional DOM0 properties. partial interface UIEvent { const long SCROLL_PAGE_UP = -32768; const long SCROLL_PAGE_DOWN = 32768; @@ -37,11 +35,11 @@ partial interface UIEvent { readonly attribute Node? rangeParent; readonly attribute long rangeOffset; attribute boolean cancelBubble; readonly attribute boolean isChar; }; dictionary UIEventInit : EventInit { - WindowProxy? view = null; - long detail = 0; + Window? view = null; + long detail = 0; };
--- a/dom/webidl/XULCommandEvent.webidl +++ b/dom/webidl/XULCommandEvent.webidl @@ -1,30 +1,28 @@ /* -*- Mode: IDL; 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/. */ -interface WindowProxy; - [Func="IsChromeOrXBL"] interface XULCommandEvent : UIEvent { readonly attribute boolean ctrlKey; readonly attribute boolean shiftKey; readonly attribute boolean altKey; readonly attribute boolean metaKey; readonly attribute Event? sourceEvent; [Throws] void initCommandEvent(DOMString type, boolean canBubble, boolean cancelable, - WindowProxy? view, + Window? view, long detail, boolean ctrlKey, boolean altKey, boolean shiftKey, boolean metaKey, Event? sourceEvent); };
--- a/dom/workers/WorkerDebuggerManager.cpp +++ b/dom/workers/WorkerDebuggerManager.cpp @@ -44,17 +44,17 @@ NS_IMPL_ISUPPORTS(RegisterDebuggerRunnab BEGIN_WORKERS_NAMESPACE class WorkerDebuggerEnumerator MOZ_FINAL : public nsISimpleEnumerator { nsTArray<nsCOMPtr<nsISupports>> mDebuggers; uint32_t mIndex; public: - WorkerDebuggerEnumerator(const nsTArray<WorkerDebugger*>& aDebuggers) + explicit WorkerDebuggerEnumerator(const nsTArray<WorkerDebugger*>& aDebuggers) : mIndex(0) { mDebuggers.AppendElements(aDebuggers); } NS_DECL_ISUPPORTS NS_DECL_NSISIMPLEENUMERATOR
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2176,18 +2176,16 @@ WorkerPrivateParent<Derived>::DisableDeb if (!self->mDebugger) { return; } if (NS_FAILED(UnregisterWorkerDebugger(self->mDebugger))) { NS_WARNING("Failed to unregister worker debugger!"); } - - self->mDebugger = nullptr; } template <class Derived> nsresult WorkerPrivateParent<Derived>::DispatchControlRunnable( WorkerControlRunnable* aWorkerControlRunnable) { // May be called on any thread! @@ -3499,46 +3497,163 @@ WorkerDebugger::WorkerDebugger(WorkerPri mIsEnabled(false) { mWorkerPrivate->AssertIsOnParentThread(); } WorkerDebugger::~WorkerDebugger() { MOZ_ASSERT(!mWorkerPrivate); + MOZ_ASSERT(!mIsEnabled); + + if (!NS_IsMainThread()) { + nsCOMPtr<nsIThread> mainThread; + if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread)))) { + NS_WARNING("Failed to proxy release of listeners, leaking instead!"); + } + + for (size_t index = 0; index < mListeners.Length(); ++index) { + nsIWorkerDebuggerListener* listener = nullptr; + mListeners[index].forget(&listener); + if (NS_FAILED(NS_ProxyRelease(mainThread, listener))) { + NS_WARNING("Failed to proxy release of listener, leaking instead!"); + } + } + } } NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger) NS_IMETHODIMP WorkerDebugger::GetIsClosed(bool* aResult) { AssertIsOnMainThread(); MutexAutoLock lock(mMutex); *aResult = !mWorkerPrivate; return NS_OK; } NS_IMETHODIMP +WorkerDebugger::GetIsChrome(bool* aResult) +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mMutex); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + *aResult = mWorkerPrivate->IsChromeWorker(); + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::GetParent(nsIWorkerDebugger** aResult) +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mMutex); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + WorkerPrivate* parent = mWorkerPrivate->GetParent(); + if (!parent) { + *aResult = nullptr; + return NS_OK; + } + + MOZ_ASSERT(mWorkerPrivate->IsDedicatedWorker()); + + nsCOMPtr<nsIWorkerDebugger> debugger = parent->Debugger(); + debugger.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::GetType(uint32_t* aResult) +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mMutex); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + *aResult = mWorkerPrivate->Type(); + return NS_OK; +} + +NS_IMETHODIMP WorkerDebugger::GetUrl(nsAString& aResult) { AssertIsOnMainThread(); MutexAutoLock lock(mMutex); if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } aResult = mWorkerPrivate->ScriptURL(); return NS_OK; } +NS_IMETHODIMP +WorkerDebugger::GetWindow(nsIDOMWindow** aResult) +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mMutex); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + if (mWorkerPrivate->GetParent() || !mWorkerPrivate->IsDedicatedWorker()) { + *aResult = nullptr; + return NS_OK; + } + + nsCOMPtr<nsPIDOMWindow> window = mWorkerPrivate->GetWindow(); + window.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::AddListener(nsIWorkerDebuggerListener* aListener) +{ + AssertIsOnMainThread(); + + if (mListeners.Contains(aListener)) { + return NS_ERROR_INVALID_ARG; + } + + mListeners.AppendElement(aListener); + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener) +{ + AssertIsOnMainThread(); + + if (!mListeners.Contains(aListener)) { + return NS_ERROR_INVALID_ARG; + } + + mListeners.RemoveElement(aListener); + return NS_OK; +} + void WorkerDebugger::WaitIsEnabled(bool aIsEnabled) { MutexAutoLock lock(mMutex); while (mIsEnabled != aIsEnabled) { mCondVar.Wait(); } @@ -3571,16 +3686,25 @@ WorkerDebugger::Disable() { AssertIsOnMainThread(); MutexAutoLock lock(mMutex); MOZ_ASSERT(mWorkerPrivate); mWorkerPrivate = nullptr; + { + MutexAutoUnlock unlock(mMutex); + + nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnClose(); + } + } + NotifyIsEnabled(false); } WorkerPrivate::WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, WorkerType aWorkerType, const nsACString& aSharedWorkerName,
--- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -59,16 +59,18 @@ class AutoSyncLoopHolder; class MessagePort; class SharedWorker; class WorkerControlRunnable; class WorkerGlobalScope; class WorkerPrivate; class WorkerRunnable; class WorkerDebugger; +// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to +// be updated too. enum WorkerType { WorkerTypeDedicated, WorkerTypeShared, WorkerTypeService }; // SharedMutex is a small wrapper around an (internal) reference-counted Mutex @@ -658,16 +660,22 @@ public: // The ability to be a chrome worker is orthogonal to the type of // worker [Dedicated|Shared|Service]. bool IsChromeWorker() const { return mIsChromeWorker; } + WorkerType + Type() const + { + return mWorkerType; + } + bool IsDedicatedWorker() const { return mWorkerType == WorkerTypeDedicated; } bool IsSharedWorker() const @@ -732,18 +740,21 @@ public: class WorkerDebugger : public nsIWorkerDebugger { mozilla::Mutex mMutex; mozilla::CondVar mCondVar; // Protected by mMutex WorkerPrivate* mWorkerPrivate; bool mIsEnabled; + // Only touched on the main thread. + nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> mListeners; + public: - WorkerDebugger(WorkerPrivate* aWorkerPrivate); + explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate); NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIWORKERDEBUGGER void AssertIsOnParentThread(); void WaitIsEnabled(bool aIsEnabled); @@ -863,16 +874,24 @@ public: static bool WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */); static nsresult GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, LoadInfo* aLoadInfo); + WorkerDebugger* + Debugger() const + { + AssertIsOnMainThread(); + MOZ_ASSERT(mDebugger); + return mDebugger; + } + void DoRunLoop(JSContext* aCx); bool InterruptCallback(JSContext* aCx); nsresult IsOnCurrentThread(bool* aIsOnCurrentThread);
--- a/dom/workers/nsIWorkerDebugger.idl +++ b/dom/workers/nsIWorkerDebugger.idl @@ -1,9 +1,33 @@ #include "nsISupports.idl" +interface nsIDOMWindow; + +[scriptable, uuid(54fd2dd3-c01b-4f71-888f-462f37a54f57)] +interface nsIWorkerDebuggerListener : nsISupports +{ + void onClose(); +}; + [scriptable, builtinclass, uuid(0833b363-bffe-4cdb-ad50-1c4563e0C8ff)] interface nsIWorkerDebugger : nsISupports { + const unsigned long TYPE_DEDICATED = 0; + const unsigned long TYPE_SHARED = 1; + const unsigned long TYPE_SERVICE = 2; + readonly attribute bool isClosed; + readonly attribute bool isChrome; + + readonly attribute nsIWorkerDebugger parent; + + readonly attribute unsigned long type; + readonly attribute DOMString url; + + readonly attribute nsIDOMWindow window; + + void addListener(in nsIWorkerDebuggerListener listener); + + void removeListener(in nsIWorkerDebuggerListener listener); };
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +onmessage = function () {};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_parentWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger_childWorker.js");
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_sharedWorker.js @@ -0,0 +1,11 @@ +"use strict"; + +onconnect = function (event) { + event.ports[0].onmessage = function (event) { + switch (event.data) { + case "close": + close(); + break; + } + }; +};
--- a/dom/workers/test/chrome.ini +++ b/dom/workers/test/chrome.ini @@ -1,12 +1,15 @@ [DEFAULT] support-files = WorkerDebuggerManager_childWorker.js WorkerDebuggerManager_parentWorker.js + WorkerDebugger_childWorker.js + WorkerDebugger_parentWorker.js + WorkerDebugger_sharedWorker.js WorkerTest.jsm WorkerTest_subworker.js WorkerTest_worker.js chromeWorker_subworker.js chromeWorker_worker.js dom_worker_helper.js file_url.jsm file_worker.js @@ -19,16 +22,17 @@ support-files = fileSlice_worker.js fileSubWorker_worker.js file_worker.js jsm_url_worker.js workersDisabled_worker.js file_url.jsm bug1062920_worker.js +[test_WorkerDebugger.xul] [test_WorkerDebuggerManager.xul] [test_bug883784.jsm] [test_bug883784.xul] [test_chromeWorker.xul] [test_chromeWorkerJSM.xul] [test_extension.xul] [test_extensionBootstrap.xul] [test_file.xul]
--- a/dom/workers/test/dom_worker_helper.js +++ b/dom/workers/test/dom_worker_helper.js @@ -79,16 +79,30 @@ function waitForUnregister(predicate = ( } wdm.removeListener(this); resolve(dbg); } }); }); } +function waitForDebuggerClose(dbg, predicate = () => true) { + return new Promise(function (resolve) { + dbg.addListener({ + onClose: function () { + if (!predicate()) { + return; + } + dbg.removeListener(this); + resolve(); + } + }); + }); +} + function waitForMultiple(promises) { return new Promise(function (resolve) { let results = []; for (let i = 0; i < promises.length; ++i) { let promise = promises[i]; let index = i; promise.then(function (result) { is(results.length, index, "events should occur in the specified order");
--- a/dom/workers/test/mochitest.ini +++ b/dom/workers/test/mochitest.ini @@ -83,16 +83,17 @@ support-files = subdir/relativeLoad_sub_worker2.js subdir/relativeLoad_sub_import.js test_worker_interfaces.js test_worker_performance_now.js worker_driver.js worker_wrapper.js bug1060621_worker.js bug1062920_worker.js + webSocket_sharedWorker.js [test_404.html] [test_atob.html] [test_blobConstructor.html] [test_blobWorkers.html] [test_bug1002702.html] [test_bug949946.html] [test_bug1010784.html] @@ -180,8 +181,10 @@ skip-if = (os == "win") || (os == "mac") [test_bug1060621.html] [test_websocket_basic.html] skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828 [test_websocket.html] skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828 [test_websocket_loadgroup.html] skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828 [test_bug1062920.html] +[test_webSocket_sharedWorker.html] +skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.xul @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebugger" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const PARENT_WORKER_URL = "WorkerDebugger_parentWorker.js"; + const CHILD_WORKER_URL = "WorkerDebugger_childWorker.js"; + const SHARED_WORKER_URL = "WorkerDebugger_sharedWorker.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + let promise = waitForMultiple([ + waitForRegister((dbg) => dbg.url === PARENT_WORKER_URL), + waitForRegister((dbg) => dbg.url === CHILD_WORKER_URL), + ]); + worker = new ChromeWorker(PARENT_WORKER_URL); + let dbgs = yield promise; + is(dbgs[0].isChrome, true, "debugger should be for chrome worker"); + is(dbgs[0].parent, null, + "debugger for a top-level worker should not have parent"); + is(dbgs[0].type, Ci.nsIWorkerDebugger.TYPE_DEDICATED, + "debugger should be for dedicated worker"); + is(dbgs[0].window, window, + "debugger for top-level dedicated worker should have window"); + is(dbgs[1].isChrome, false, "debugger should be for content worker"); + is(dbgs[1].parent, dbgs[0], + "debugger for child worker should have parent"); + is(dbgs[1].type, Ci.nsIWorkerDebugger.TYPE_DEDICATED); + is(dbgs[1].window, null, + "debugger for non-top-level worker should not have window"); + + promise = waitForMultiple([ + waitForUnregister((dbg) => dbg.url === CHILD_WORKER_URL), + waitForDebuggerClose(dbgs[1]), + waitForUnregister((dbg) => dbg.url === PARENT_WORKER_URL), + waitForDebuggerClose(dbgs[0]), + ]); + worker.terminate(); + yield promise; + + promise = waitForRegister(); + worker = new SharedWorker(SHARED_WORKER_URL); + let dbg = yield promise; + is(dbg.isChrome, false, "debugger should be for content worker"); + is(dbg.parent, null, + "debugger for top-level worker should not have parent"); + is(dbg.type, Ci.nsIWorkerDebugger.TYPE_SHARED, + "debugger should be for shared worker"); + is(dbg.window, null, + "debugger for non-dedicated worker should not have window"); + + let listener = { + onRegistered: function () { + ok(false, + "debugger for shared worker should not be registered twice"); + }, + }; + wdm.addListener(listener); + worker = new SharedWorker(SHARED_WORKER_URL); + + dbg.addListener({ + onClose: function () { + is(dbg.isClosed, true, "debugger should be closed"); + wdm.removeListener(listener); + dbg.removeListener(this); + SimpleTest.finish(); + } + }); + worker.port.start(); + worker.port.postMessage("close"); + }); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_webSocket_sharedWorker.html @@ -0,0 +1,32 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test for bug 1090183</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<script class="testbody" type="text/javascript"> + +SpecialPowers.pushPrefEnv({ set: [["dom.workers.sharedWorkers.enabled", true]] }, function() { + var sw = new SharedWorker('webSocket_sharedWorker.js'); + sw.port.onmessage = function(event) { + if (event.data.type == 'finish') { + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } + } +}); + +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/webSocket_sharedWorker.js @@ -0,0 +1,20 @@ +onconnect = function(evt) { + var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + + ws.onopen = function(e) { + evt.ports[0].postMessage({type: 'status', status: true, msg: 'OnOpen called' }); + ws.send("data"); + } + + ws.onclose = function(e) {} + + ws.onerror = function(e) { + evt.ports[0].postMessage({type: 'status', status: false, msg: 'onerror called!'}); + } + + ws.onmessage = function(e) { + evt.ports[0].postMessage({type: 'status', status: e.data == 'Hello world!', msg: 'Wrong data'}); + ws.close(); + evt.ports[0].postMessage({type: 'finish' }); + } +}
--- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -883,17 +883,17 @@ GetOrCreateClassObjectMap(JSContext *cx, JS::IsWeakMapObject(&desc.value().toObject())) { return &desc.value().toObject(); } // It's not there. Create and define it. JS::Rooted<JSObject*> map(cx, JS::NewWeakMapObject(cx)); if (!map || !JS_DefineProperty(cx, scope, mapName, map, JSPROP_PERMANENT | JSPROP_READONLY, - JS_PropertyStub, JS_StrictPropertyStub)) + JS_STUBGETTER, JS_STUBSETTER)) { return nullptr; } return map; } static JSObject* GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto) @@ -1032,17 +1032,17 @@ nsXBLBinding::DoInitJSClass(JSContext *c JS_SetReservedSlot(proto, 0, PRIVATE_TO_JSVAL(aProtoBinding)); // Next, enter the compartment of the property holder, wrap the proto, and // stick it on. JSAutoCompartment ac3(cx, holder); if (!JS_WrapObject(cx, &proto) || !JS_DefineProperty(cx, holder, aClassName.get(), proto, JSPROP_READONLY | JSPROP_PERMANENT, - JS_PropertyStub, JS_StrictPropertyStub)) + JS_STUBGETTER, JS_STUBSETTER)) { return NS_ERROR_OUT_OF_MEMORY; } } // Whew. We have the proto. Wrap it back into the compartment of |obj|, // splice it in, and return it. JSAutoCompartment ac4(cx, obj);
--- a/dom/xbl/nsXBLProtoImpl.cpp +++ b/dom/xbl/nsXBLProtoImpl.cpp @@ -114,17 +114,17 @@ nsXBLProtoImpl::InstallImplementation(ns // This is just a property holder, so it doesn't need any special JSClass. propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), scopeObject); NS_ENSURE_TRUE(propertyHolder, NS_ERROR_OUT_OF_MEMORY); // Define it as a property on the scopeObject, using the same name used on // the content side. bool ok = JS_DefineProperty(cx, scopeObject, className, propertyHolder, JSPROP_PERMANENT | JSPROP_READONLY, - JS_PropertyStub, JS_StrictPropertyStub); + JS_STUBGETTER, JS_STUBSETTER); NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); } else { propertyHolder = targetClassObject; } // Walk our member list and install each one in turn on the XBL scope object. if (propertyHolderIsNew) { for (nsXBLProtoImplMember* curr = mMembers;
--- a/dom/xbl/nsXBLProtoImplField.cpp +++ b/dom/xbl/nsXBLProtoImplField.cpp @@ -362,18 +362,18 @@ nsXBLProtoImplField::InstallAccessors(JS // them there. JSAutoCompartment ac2(aCx, aTargetClassObject); if (!JS_WrapObject(aCx, &get) || !JS_WrapObject(aCx, &set)) { return NS_ERROR_OUT_OF_MEMORY; } if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id, JS::UndefinedHandleValue, AccessorAttributes(), - JS_DATA_TO_FUNC_PTR(JSPropertyOp, get.get()), - JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, set.get()))) { + JS_DATA_TO_FUNC_PTR(JSNative, get.get()), + JS_DATA_TO_FUNC_PTR(JSNative, set.get()))) { return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } nsresult nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
--- a/dom/xbl/nsXBLProtoImplProperty.cpp +++ b/dom/xbl/nsXBLProtoImplProperty.cpp @@ -145,18 +145,18 @@ nsXBLProtoImplProperty::InstallMember(JS if (!(setter = ::JS_CloneFunctionObject(aCx, setter, globalObject))) return NS_ERROR_OUT_OF_MEMORY; } nsDependentString name(mName); if (!::JS_DefineUCProperty(aCx, aTargetClassObject, static_cast<const char16_t*>(mName), name.Length(), JS::UndefinedHandleValue, mJSAttributes, - JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get()), - JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get()))) + JS_DATA_TO_FUNC_PTR(JSNative, getter.get()), + JS_DATA_TO_FUNC_PTR(JSNative, setter.get()))) return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } nsresult nsXBLProtoImplProperty::CompileMember(AutoJSAPI& jsapi, const nsCString& aClassStr, JS::Handle<JSObject*> aClassObject)
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.h +++ b/dom/xslt/xslt/txMozillaXSLTProcessor.h @@ -151,17 +151,17 @@ public: { return (mFlags & DISABLE_ALL_LOADS) != 0; } static nsresult Startup(); static void Shutdown(); private: - txMozillaXSLTProcessor(nsISupports* aOwner); + explicit txMozillaXSLTProcessor(nsISupports* aOwner); /** * Default destructor for txMozillaXSLTProcessor */ ~txMozillaXSLTProcessor(); nsresult DoTransform(); void notifyError(); nsresult ensureStylesheet();
--- a/editor/txmgr/tests/moz.build +++ b/editor/txmgr/tests/moz.build @@ -1,18 +1,11 @@ # -*- 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/. -CppUnitTests([ +GeckoCppUnitTests([ 'TestTXMgr', ]) FAIL_ON_WARNINGS = True - -USE_LIBS += [ - 'mozalloc', - 'nspr', - 'xpcomglue_s', - 'xul', -]
--- a/embedding/tests/winEmbed/moz.build +++ b/embedding/tests/winEmbed/moz.build @@ -1,26 +1,24 @@ # -*- 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/. -Program('winEmbed') +GeckoProgram('winEmbed') SOURCES += [ 'WebBrowserChrome.cpp', 'WindowCreator.cpp', 'winEmbed.cpp', ] XPI_NAME = 'winembed' -DEFINES['XPCOM_GLUE'] = True - RESFILE = 'winEmbed.res' if CONFIG['GNU_CC']: # Get rid of console window LDFLAGS += ['-mwindows'] else: # Control the default heap size. # This is the heap returned by GetProcessHeap(). @@ -31,17 +29,16 @@ else: # # Set it to 256k. See bug 127069. LDFLAGS += ['/HEAP:0x40000'] DISABLE_STL_WRAPPING = True USE_LIBS += [ 'profdirserviceprovidersa_s', - 'xpcomglue', ] OS_LIBS += [ 'ole32', 'comdlg32', 'shell32', 'version', ]
--- a/gfx/2d/DrawTargetD2D1.cpp +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -1206,44 +1206,50 @@ DrawTargetD2D1::CreateBrushForPattern(co if (!pat->mSurface) { gfxDebug() << "No source surface specified for surface pattern"; return CreateTransparentBlackBrush(); } D2D1_RECT_F samplingBounds; Matrix mat = pat->mMatrix; - if (!pat->mSamplingRect.IsEmpty()) { + + bool useSamplingRect = false; + if (!pat->mSamplingRect.IsEmpty() && + (pat->mSurface->GetType() == SurfaceType::D2D1_1_IMAGE)) { samplingBounds = D2DRect(pat->mSamplingRect); mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y); + } else if (!pat->mSamplingRect.IsEmpty()) { + // We will do a partial upload of the sampling restricted area from GetImageForSurface. + samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.width, pat->mSamplingRect.height); } else { samplingBounds = D2D1::RectF(0, 0, Float(pat->mSurface->GetSize().width), Float(pat->mSurface->GetSize().height)); } RefPtr<ID2D1ImageBrush> imageBrush; - RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode); + RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr); mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds, D2DExtend(pat->mExtendMode), D2DExtend(pat->mExtendMode), D2DInterpolationMode(pat->mFilter)), D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), byRef(imageBrush)); return imageBrush.forget(); } gfxWarning() << "Invalid pattern type detected."; return CreateTransparentBlackBrush(); } TemporaryRef<ID2D1Image> DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, - ExtendMode aExtendMode) + ExtendMode aExtendMode, const IntRect* aSourceRect) { RefPtr<ID2D1Image> image; switch (aSurface->GetType()) { case SurfaceType::D2D1_1_IMAGE: { SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface); image = surf->GetImage(); @@ -1253,17 +1259,17 @@ DrawTargetD2D1::GetImageForSurface(Sourc default: { RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface(); if (!dataSurf) { gfxWarning() << "Invalid surface type."; return nullptr; } return CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode, - aSourceTransform, mDC); + aSourceTransform, mDC, aSourceRect); } break; } return image.forget(); } TemporaryRef<SourceSurface>
--- a/gfx/2d/DrawTargetD2D1.h +++ b/gfx/2d/DrawTargetD2D1.h @@ -124,21 +124,21 @@ public: virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; } bool Init(const IntSize &aSize, SurfaceFormat aFormat); bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat); uint32_t GetByteSize() const; TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, - ExtendMode aExtendMode); + ExtendMode aExtendMode, const IntRect* aSourceRect = nullptr); TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, ExtendMode aExtendMode) { Matrix mat; - return GetImageForSurface(aSurface, mat, aExtendMode); + return GetImageForSurface(aSurface, mat, aExtendMode, nullptr); } static ID2D1Factory1 *factory(); static void CleanupD2D(); static IDWriteFactory *GetDWriteFactory(); operator std::string() const { std::stringstream stream;
--- a/gfx/2d/PathHelpers.cpp +++ b/gfx/2d/PathHelpers.cpp @@ -6,20 +6,38 @@ #include "PathHelpers.h" namespace mozilla { namespace gfx { UserDataKey sDisablePixelSnapping; void +AppendRectToPath(PathBuilder* aPathBuilder, + const Rect& aRect, + bool aDrawClockwise) +{ + if (aDrawClockwise) { + aPathBuilder->MoveTo(aRect.TopLeft()); + aPathBuilder->LineTo(aRect.TopRight()); + aPathBuilder->LineTo(aRect.BottomRight()); + aPathBuilder->LineTo(aRect.BottomLeft()); + } else { + aPathBuilder->MoveTo(aRect.TopRight()); + aPathBuilder->LineTo(aRect.TopLeft()); + aPathBuilder->LineTo(aRect.BottomLeft()); + aPathBuilder->LineTo(aRect.BottomRight()); + } + aPathBuilder->Close(); +} + +void AppendRoundedRectToPath(PathBuilder* aPathBuilder, const Rect& aRect, - // paren's needed due to operator precedence: - const Size(& aCornerRadii)[4], + const RectCornerRadii& aRadii, bool aDrawClockwise) { // For CW drawing, this looks like: // // ...******0** 1 C // **** // *** 2 // ** @@ -101,70 +119,67 @@ AppendRoundedRectToPath(PathBuilder* aPa twoFloats *cornerMults = aDrawClockwise ? cwCornerMults : ccwCornerMults; Point cornerCoords[] = { aRect.TopLeft(), aRect.TopRight(), aRect.BottomRight(), aRect.BottomLeft() }; Point pc, p0, p1, p2, p3; - // The indexes of the corners: - const int kTopLeft = 0, kTopRight = 1; - if (aDrawClockwise) { - aPathBuilder->MoveTo(Point(aRect.X() + aCornerRadii[kTopLeft].width, + aPathBuilder->MoveTo(Point(aRect.X() + aRadii[RectCorner::TopLeft].width, aRect.Y())); } else { - aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aCornerRadii[kTopRight].width, + aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aRadii[RectCorner::TopRight].width, aRect.Y())); } for (int i = 0; i < 4; ++i) { // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) int c = aDrawClockwise ? ((i+1) % 4) : ((4-i) % 4); // i+2 and i+3 respectively. These are used to index into the corner // multiplier table, and were deduced by calculating out the long form // of each corner and finding a pattern in the signs and values. int i2 = (i+2) % 4; int i3 = (i+3) % 4; pc = cornerCoords[c]; - if (aCornerRadii[c].width > 0.0 && aCornerRadii[c].height > 0.0) { - p0.x = pc.x + cornerMults[i].a * aCornerRadii[c].width; - p0.y = pc.y + cornerMults[i].b * aCornerRadii[c].height; + if (aRadii[c].width > 0.0 && aRadii[c].height > 0.0) { + p0.x = pc.x + cornerMults[i].a * aRadii[c].width; + p0.y = pc.y + cornerMults[i].b * aRadii[c].height; - p3.x = pc.x + cornerMults[i3].a * aCornerRadii[c].width; - p3.y = pc.y + cornerMults[i3].b * aCornerRadii[c].height; + p3.x = pc.x + cornerMults[i3].a * aRadii[c].width; + p3.y = pc.y + cornerMults[i3].b * aRadii[c].height; - p1.x = p0.x + alpha * cornerMults[i2].a * aCornerRadii[c].width; - p1.y = p0.y + alpha * cornerMults[i2].b * aCornerRadii[c].height; + p1.x = p0.x + alpha * cornerMults[i2].a * aRadii[c].width; + p1.y = p0.y + alpha * cornerMults[i2].b * aRadii[c].height; - p2.x = p3.x - alpha * cornerMults[i3].a * aCornerRadii[c].width; - p2.y = p3.y - alpha * cornerMults[i3].b * aCornerRadii[c].height; + p2.x = p3.x - alpha * cornerMults[i3].a * aRadii[c].width; + p2.y = p3.y - alpha * cornerMults[i3].b * aRadii[c].height; aPathBuilder->LineTo(p0); aPathBuilder->BezierTo(p1, p2, p3); } else { aPathBuilder->LineTo(pc); } } aPathBuilder->Close(); } void AppendEllipseToPath(PathBuilder* aPathBuilder, const Point& aCenter, const Size& aDimensions) { - Size halfDim = aDimensions / 2.0; + Size halfDim = aDimensions / 2.f; Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions); - Size radii[] = { halfDim, halfDim, halfDim, halfDim }; + RectCornerRadii radii(halfDim.width, halfDim.height); AppendRoundedRectToPath(aPathBuilder, rect, radii); } bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2, const DrawTarget& aDrawTarget) {
--- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -3,16 +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/. */ #ifndef MOZILLA_GFX_PATHHELPERS_H_ #define MOZILLA_GFX_PATHHELPERS_H_ #include "2D.h" #include "mozilla/Constants.h" +#include "mozilla/TypedEnum.h" #include "UserData.h" namespace mozilla { namespace gfx { template <typename T> void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius, float aStartAngle, float aEndAngle, bool aAntiClockwise) @@ -125,39 +126,136 @@ void EllipseToBezier(T* aSink, const Poi // sin(x+pi/2) == cos(x) Float tmp = cosStartAngle; cosStartAngle = -sinStartAngle; sinStartAngle = tmp; } } /** + * Appends a path represending a rectangle to the path being built by + * aPathBuilder. + * + * aRect The rectangle to append. + * aDrawClockwise If set to true, the path will start at the left of the top + * left edge and draw clockwise. If set to false the path will + * start at the right of the top left edge and draw counter- + * clockwise. + */ +GFX2D_API void AppendRectToPath(PathBuilder* aPathBuilder, + const Rect& aRect, + bool aDrawClockwise = true); + +inline TemporaryRef<Path> MakePathForRect(const DrawTarget& aDrawTarget, + const Rect& aRect, + bool aDrawClockwise = true) +{ + RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(); + AppendRectToPath(builder, aRect, aDrawClockwise); + return builder->Finish(); +} + +// We can't use MOZ_BEGIN_ENUM_CLASS here because that prevents the enum +// values from being used for indexing. Wrapping the enum in a struct does at +// least gives us name scoping. +struct RectCorner { + enum { + // This order is important since AppendRoundedRectToPath and other code + // depends on it! + TopLeft = 0, + TopRight = 1, + BottomRight = 2, + BottomLeft = 3, + Count = 4 + }; +}; + +struct RectCornerRadii { + Size radii[RectCorner::Count]; + + RectCornerRadii() {} + + explicit RectCornerRadii(Float radius) { + for (int i = 0; i < RectCorner::Count; i++) { + radii[i].SizeTo(radius, radius); + } + } + + explicit RectCornerRadii(Float radiusX, Float radiusY) { + for (int i = 0; i < RectCorner::Count; i++) { + radii[i].SizeTo(radiusX, radiusY); + } + } + + RectCornerRadii(Float tl, Float tr, Float br, Float bl) { + radii[RectCorner::TopLeft].SizeTo(tl, tl); + radii[RectCorner::TopRight].SizeTo(tr, tr); + radii[RectCorner::BottomRight].SizeTo(br, br); + radii[RectCorner::BottomLeft].SizeTo(bl, bl); + } + + RectCornerRadii(const Size& tl, const Size& tr, + const Size& br, const Size& bl) { + radii[RectCorner::TopLeft] = tl; + radii[RectCorner::TopRight] = tr; + radii[RectCorner::BottomRight] = br; + radii[RectCorner::BottomLeft] = bl; + } + + const Size& operator[](size_t aCorner) const { + return radii[aCorner]; + } + + Size& operator[](size_t aCorner) { + return radii[aCorner]; + } + + void Scale(Float aXScale, Float aYScale) { + for (int i = 0; i < RectCorner::Count; i++) { + radii[i].Scale(aXScale, aYScale); + } + } + + const Size TopLeft() const { return radii[RectCorner::TopLeft]; } + Size& TopLeft() { return radii[RectCorner::TopLeft]; } + + const Size TopRight() const { return radii[RectCorner::TopRight]; } + Size& TopRight() { return radii[RectCorner::TopRight]; } + + const Size BottomRight() const { return radii[RectCorner::BottomRight]; } + Size& BottomRight() { return radii[RectCorner::BottomRight]; } + + const Size BottomLeft() const { return radii[RectCorner::BottomLeft]; } + Size& BottomLeft() { return radii[RectCorner::BottomLeft]; } +}; + +/** * Appends a path represending a rounded rectangle to the path being built by * aPathBuilder. * * aRect The rectangle to append. * aCornerRadii Contains the radii of the top-left, top-right, bottom-right * and bottom-left corners, in that order. * aDrawClockwise If set to true, the path will start at the left of the top * left edge and draw clockwise. If set to false the path will * start at the right of the top left edge and draw counter- * clockwise. */ GFX2D_API void AppendRoundedRectToPath(PathBuilder* aPathBuilder, const Rect& aRect, - const Size(& aCornerRadii)[4], + const RectCornerRadii& aRadii, bool aDrawClockwise = true); inline TemporaryRef<Path> MakePathForRoundedRect(const DrawTarget& aDrawTarget, const Rect& aRect, - const Size(& aCornerRadii)[4], + const RectCornerRadii& aRadii, bool aDrawClockwise = true) { RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(); - AppendRoundedRectToPath(builder, aRect, aCornerRadii, aDrawClockwise); + AppendRoundedRectToPath(builder, aRect, aRadii, aDrawClockwise); return builder->Finish(); } /** * Appends a path represending an ellipse to the path being built by * aPathBuilder. * * The ellipse extends aDimensions.width / 2.0 in the horizontal direction
--- a/gfx/angle/src/libEGL/moz.build +++ b/gfx/angle/src/libEGL/moz.build @@ -51,12 +51,12 @@ DEFINES['EGLAPI'] = "" # ANGLE uses the STL, so we can't use our derpy STL wrappers. DISABLE_STL_WRAPPING = True LOCAL_INCLUDES += [ '../../include', '../../src' ] USE_LIBS += [ 'libGLESv2' ] -SharedLibrary('libEGL') +GeckoSharedLibrary('libEGL', linkage=None) RCFILE = SRCDIR + '/libEGL.rc' DEFFILE = SRCDIR + '/libEGL.def'
--- a/gfx/angle/src/libGLESv2/formatutils.cpp +++ b/gfx/angle/src/libGLESv2/formatutils.cpp @@ -449,17 +449,17 @@ static InternalFormatInfoMap BuildIntern // Depth stencil formats // | Internal format | | D |S | X | Format | Type | Component type | Supported | Renderable | Filterable | map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT16, DepthStencilFormat(16, 0, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_UNSIGNED_NORMALIZED, RequireES<2>, RequireES<2>, RequireESOrExt<3, &Extensions::depthTextures>))); map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT24, DepthStencilFormat(24, 0, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_UNSIGNED_NORMALIZED, RequireES<3>, RequireES<3>, RequireESOrExt<3, &Extensions::depthTextures>))); map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32F, DepthStencilFormat(32, 0, 0, GL_DEPTH_COMPONENT, GL_FLOAT, GL_FLOAT, RequireES<3>, RequireES<3>, RequireESOrExt<3, &Extensions::depthTextures>))); map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32_OES, DepthStencilFormat(32, 0, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::depthTextures>, RequireExt<&Extensions::depthTextures>, AlwaysSupported ))); map.insert(InternalFormatInfoPair(GL_DEPTH24_STENCIL8, DepthStencilFormat(24, 8, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_UNSIGNED_NORMALIZED, RequireESOrExt<3, &Extensions::depthTextures>, RequireESOrExtOrExt<3, &Extensions::depthTextures, &Extensions::packedDepthStencil>, AlwaysSupported ))); map.insert(InternalFormatInfoPair(GL_DEPTH32F_STENCIL8, DepthStencilFormat(32, 8, 24, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, GL_FLOAT, RequireES<3>, RequireES<3>, AlwaysSupported ))); - map.insert(InternalFormatInfoPair(GL_STENCIL_INDEX8, DepthStencilFormat( 0, 8, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_BYTE, GL_UNSIGNED_INT, RequireES<2>, RequireES<2>, NeverSupported ))); + map.insert(InternalFormatInfoPair(GL_STENCIL_INDEX8, DepthStencilFormat( 0, 8, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE, RequireES<2>, RequireES<2>, NeverSupported ))); // Luminance alpha formats // | Internal format | | L | A | Format | Type | Component type | Supported | Renderable | Filterable | map.insert(InternalFormatInfoPair(GL_ALPHA8_EXT, LUMAFormat( 0, 8, GL_ALPHA, GL_UNSIGNED_BYTE, GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::textureStorage>, NeverSupported, AlwaysSupported))); map.insert(InternalFormatInfoPair(GL_LUMINANCE8_EXT, LUMAFormat( 8, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::textureStorage>, NeverSupported, AlwaysSupported))); map.insert(InternalFormatInfoPair(GL_ALPHA32F_EXT, LUMAFormat( 0, 32, GL_ALPHA, GL_FLOAT, GL_FLOAT, RequireExtAndExt<&Extensions::textureStorage, &Extensions::textureFloat>, NeverSupported, AlwaysSupported))); map.insert(InternalFormatInfoPair(GL_LUMINANCE32F_EXT, LUMAFormat(32, 0, GL_LUMINANCE, GL_FLOAT, GL_FLOAT, RequireExtAndExt<&Extensions::textureStorage, &Extensions::textureFloat>, NeverSupported, AlwaysSupported))); map.insert(InternalFormatInfoPair(GL_ALPHA16F_EXT, LUMAFormat( 0, 16, GL_ALPHA, GL_HALF_FLOAT, GL_FLOAT, RequireExtAndExt<&Extensions::textureStorage, &Extensions::textureHalfFloat>, NeverSupported, AlwaysSupported)));
--- a/gfx/angle/src/libGLESv2/moz.build +++ b/gfx/angle/src/libGLESv2/moz.build @@ -218,15 +218,15 @@ LOCAL_INCLUDES += [ '../../include', '.. if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: OS_LIBS += [ 'd3d9', 'dxguid' ] else: EXTRA_DSO_LDOPTS += [ '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']), ] -SharedLibrary('libGLESv2') +GeckoSharedLibrary('libGLESv2', linkage=None) RCFILE = SRCDIR + '/libGLESv2.rc' DEFFILE = SRCDIR + '/libGLESv2.def' SOURCES['renderer/d3d/HLSLCompiler.cpp'].flags += ['-DANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES=\'{ TEXT("d3dcompiler_47.dll"), TEXT("d3dcompiler_46.dll"), TEXT("d3dcompiler_43.dll") }\'']
--- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -157,52 +157,49 @@ static const char *sExtensionNames[] = { "GL_OES_texture_half_float", "GL_OES_texture_half_float_linear", "GL_OES_texture_npot", "GL_OES_vertex_array_object", nullptr }; static bool -ParseGLVersion(GLContext* gl, unsigned int* version) +ParseGLVersion(GLContext* gl, uint32_t* out_version) { - GLenum error = gl->fGetError(); - if (error != LOCAL_GL_NO_ERROR) { + if (gl->fGetError() != LOCAL_GL_NO_ERROR) { MOZ_ASSERT(false, "An OpenGL error has been triggered before."); return false; } /** * B2G emulator bug work around: The emulator implements OpenGL ES 2.0 on * OpenGL 3.2. The bug is that GetIntegerv(LOCAL_GL_{MAJOR,MINOR}_VERSION) * returns OpenGL 3.2 instead of generating an error. */ - if (!gl->IsGLES()) - { + if (!gl->IsGLES()) { /** * OpenGL 3.1 and OpenGL ES 3.0 both introduce GL_{MAJOR,MINOR}_VERSION * with GetIntegerv. So we first try those constants even though we - * might not have an OpenGL context supporting them, has this is a + * might not have an OpenGL context supporting them, as this is a * better way than parsing GL_VERSION. */ GLint majorVersion = 0; GLint minorVersion = 0; - gl->fGetIntegerv(LOCAL_GL_MAJOR_VERSION, &majorVersion); - gl->fGetIntegerv(LOCAL_GL_MINOR_VERSION, &minorVersion); + const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAJOR_VERSION, + &majorVersion) && + gl->GetPotentialInteger(LOCAL_GL_MINOR_VERSION, + &minorVersion)); // If it's not an OpenGL (ES) 3.0 context, we will have an error - error = gl->fGetError(); - while (gl->fGetError() != LOCAL_GL_NO_ERROR); - - if (error == LOCAL_GL_NO_ERROR && + if (ok && majorVersion > 0 && minorVersion >= 0) { - *version = majorVersion * 100 + minorVersion * 10; + *out_version = majorVersion * 100 + minorVersion * 10; return true; } } /** * We were not able to use GL_{MAJOR,MINOR}_VERSION, so we parse * GL_VERSION. * @@ -227,79 +224,77 @@ ParseGLVersion(GLContext* gl, unsigned i * one or more digits. * * * Note: * We don't care about release_number. */ const char* versionString = (const char*)gl->fGetString(LOCAL_GL_VERSION); - error = gl->fGetError(); - if (error != LOCAL_GL_NO_ERROR) { + if (gl->fGetError() != LOCAL_GL_NO_ERROR) { MOZ_ASSERT(false, "glGetString(GL_VERSION) has generated an error"); return false; } else if (!versionString) { MOZ_ASSERT(false, "glGetString(GL_VERSION) has returned 0"); return false; } const char kGLESVersionPrefix[] = "OpenGL ES "; if (strncmp(versionString, kGLESVersionPrefix, strlen(kGLESVersionPrefix)) == 0) { versionString += strlen(kGLESVersionPrefix); } const char* itr = versionString; char* end = nullptr; - int majorVersion = (int)strtol(itr, &end, 10); + auto majorVersion = strtol(itr, &end, 10); if (!end) { MOZ_ASSERT(false, "Failed to parse the GL major version number."); return false; } else if (*end != '.') { MOZ_ASSERT(false, "Failed to parse GL's major-minor version number separator."); return false; } // we skip the '.' between the major and the minor version itr = end + 1; end = nullptr; - int minorVersion = (int)strtol(itr, &end, 10); + auto minorVersion = strtol(itr, &end, 10); if (!end) { MOZ_ASSERT(false, "Failed to parse GL's minor version number."); return false; } if (majorVersion <= 0 || majorVersion >= 100) { MOZ_ASSERT(false, "Invalid major version."); return false; } else if (minorVersion < 0 || minorVersion >= 10) { MOZ_ASSERT(false, "Invalid minor version."); return false; } - *version = (unsigned int)(majorVersion * 100 + minorVersion * 10); + *out_version = (uint32_t)majorVersion * 100 + (uint32_t)minorVersion * 10; return true; } GLContext::GLContext(const SurfaceCaps& caps, GLContext* sharedContext, bool isOffscreen) : mInitialized(false), mIsOffscreen(isOffscreen), mContextLost(false), mVersion(0), mProfile(ContextProfile::Unknown), mVendor(GLVendor::Other), mRenderer(GLRenderer::Other), mHasRobustness(false), -#ifdef MOZ_GL_DEBUG - mIsInLocalErrorCheck(false), -#endif + mTopError(LOCAL_GL_NO_ERROR), + mLocalErrorScope(nullptr), mSharedContext(sharedContext), mCaps(caps), mScreen(nullptr), mLockedSurface(nullptr), mMaxTextureSize(0), mMaxCubeMapTextureSize(0), mMaxTextureImageSize(0), mMaxRenderbufferSize(0), @@ -502,18 +497,17 @@ GLContext::InitWithPrefix(const char *pr END_SYMBOLS }; mInitialized = LoadSymbols(&symbols[0], trygl, prefix); MakeCurrent(); if (mInitialized) { - unsigned int version = 0; - + uint32_t version = 0; ParseGLVersion(this, &version); #ifdef MOZ_GL_DEBUG printf_stderr("OpenGL version detected: %u\n", version); printf_stderr("OpenGL vendor: %s\n", fGetString(LOCAL_GL_VENDOR)); printf_stderr("OpenGL renderer: %s\n", fGetString(LOCAL_GL_RENDERER)); #endif @@ -1586,17 +1580,17 @@ GLContext::DebugCallback(GLenum source, case LOCAL_GL_DEBUG_SEVERITY_NOTIFICATION: sevStr = NS_LITERAL_CSTRING("SEVERITY_NOTIFICATION"); break; default: sevStr = nsPrintfCString("<severity 0x%04x>", severity); break; } - printf_stderr("[KHR_debug: 0x%" PRIxPTR "] ID %u: %s %s %s:\n %s", + printf_stderr("[KHR_debug: 0x%" PRIxPTR "] ID %u: %s, %s, %s:\n %s\n", (uintptr_t)this, id, sourceStr.BeginReading(), typeStr.BeginReading(), sevStr.BeginReading(), message); } @@ -2396,30 +2390,31 @@ GLContext::CleanDirtyScreen() BeforeGLReadCall(); // no-op; we just want to make sure the Read FBO is updated if it needs to be AfterGLReadCall(); } void GLContext::EmptyTexGarbageBin() { - TexGarbageBin()->EmptyGarbage(); + TexGarbageBin()->EmptyGarbage(); } bool -GLContext::IsOffscreenSizeAllowed(const IntSize& aSize) const { - int32_t biggerDimension = std::max(aSize.width, aSize.height); - int32_t maxAllowed = std::min(mMaxRenderbufferSize, mMaxTextureSize); - return biggerDimension <= maxAllowed; +GLContext::IsOffscreenSizeAllowed(const IntSize& aSize) const +{ + int32_t biggerDimension = std::max(aSize.width, aSize.height); + int32_t maxAllowed = std::min(mMaxRenderbufferSize, mMaxTextureSize); + return biggerDimension <= maxAllowed; } bool GLContext::IsOwningThreadCurrent() { - return PlatformThread::CurrentId() == mOwningThreadId; + return PlatformThread::CurrentId() == mOwningThreadId; } GLBlitHelper* GLContext::BlitHelper() { if (!mBlitHelper) { mBlitHelper = MakeUnique<GLBlitHelper>(this); }
--- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -271,17 +271,17 @@ public: return profile == mProfile; } /** * Return the version of the context. * Example : * If this a OpenGL 2.1, that will return 210 */ - inline unsigned int Version() const { + inline uint32_t Version() const { return mVersion; } const char* VersionString() const { return mVersionString.get(); } GLVendor Vendor() const { @@ -313,17 +313,17 @@ protected: bool mIsOffscreen; bool mIsGlobalSharedContext; bool mContextLost; /** * mVersion store the OpenGL's version, multiplied by 100. For example, if * the context is an OpenGL 2.1 context, mVersion value will be 210. */ - unsigned int mVersion; + uint32_t mVersion; nsCString mVersionString; ContextProfile mProfile; GLVendor mVendor; GLRenderer mRenderer; inline void SetProfileVersion(ContextProfile profile, unsigned int version) { MOZ_ASSERT(!mInitialized, "SetProfileVersion can only be called before initialization!"); @@ -575,123 +575,106 @@ public: return "GL_TABLE_TOO_LARGE"; case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; default: return ""; } } - /** \returns the first GL error, and guarantees that all GL error flags are cleared, - * i.e. that a subsequent GetError call will return NO_ERROR - */ - GLenum GetAndClearError() { - // the first error is what we want to return - GLenum error = fGetError(); - - if (error) { - // clear all pending errors - while(fGetError()) {} - } - - return error; - } - private: - GLenum raw_fGetError() { + GLenum mTopError; + + GLenum RawGetError() { return mSymbols.fGetError(); } - std::queue<GLenum> mGLErrorQueue; + GLenum RawGetErrorAndClear() { + GLenum err = RawGetError(); + + if (err) + while (RawGetError()) {} + + return err; + } public: + GLenum FlushErrors() { + GLenum err = RawGetErrorAndClear(); + if (!mTopError) + mTopError = err; + return err; + } + + // We smash all errors together, so you never have to loop on this. We + // guarantee that immediately after this call, there are no errors left. GLenum fGetError() { - if (!mGLErrorQueue.empty()) { - GLenum err = mGLErrorQueue.front(); - mGLErrorQueue.pop(); - return err; - } - - return GetUnpushedError(); - } - -private: - GLenum GetUnpushedError() { - return raw_fGetError(); - } - - void ClearUnpushedErrors() { - while (GetUnpushedError()) { - // Discard errors. - } - } - - GLenum GetAndClearUnpushedErrors() { - GLenum err = GetUnpushedError(); - if (err) { - ClearUnpushedErrors(); - } + FlushErrors(); + + GLenum err = mTopError; + mTopError = LOCAL_GL_NO_ERROR; return err; } - void PushError(GLenum err) { - mGLErrorQueue.push(err); - } - - void GetAndPushAllErrors() { - while (true) { - GLenum err = GetUnpushedError(); - if (!err) - break; - - PushError(err); - } - } - //////////////////////////////////// // Use this safer option. + class LocalErrorScope; + private: -#ifdef MOZ_GL_DEBUG - bool mIsInLocalErrorCheck; -#endif + LocalErrorScope* mLocalErrorScope; public: - class ScopedLocalErrorCheck { - GLContext* const mGL; + class LocalErrorScope { + GLContext& mGL; + GLenum mOldTop; bool mHasBeenChecked; public: - explicit ScopedLocalErrorCheck(GLContext* gl) + explicit LocalErrorScope(GLContext& gl) : mGL(gl) , mHasBeenChecked(false) { -#ifdef MOZ_GL_DEBUG - MOZ_ASSERT(!mGL->mIsInLocalErrorCheck); - mGL->mIsInLocalErrorCheck = true; -#endif - mGL->GetAndPushAllErrors(); + MOZ_ASSERT(!mGL.mLocalErrorScope); + mGL.mLocalErrorScope = this; + + mGL.FlushErrors(); + + mOldTop = mGL.mTopError; + mGL.mTopError = LOCAL_GL_NO_ERROR; } - GLenum GetLocalError() { -#ifdef MOZ_GL_DEBUG - MOZ_ASSERT(mGL->mIsInLocalErrorCheck); - mGL->mIsInLocalErrorCheck = false; -#endif - + GLenum GetError() { MOZ_ASSERT(!mHasBeenChecked); mHasBeenChecked = true; - return mGL->GetAndClearUnpushedErrors(); + return mGL.fGetError(); } - ~ScopedLocalErrorCheck() { + ~LocalErrorScope() { MOZ_ASSERT(mHasBeenChecked); + + MOZ_ASSERT(mGL.fGetError() == LOCAL_GL_NO_ERROR); + + mGL.mTopError = mOldTop; + + MOZ_ASSERT(mGL.mLocalErrorScope == this); + mGL.mLocalErrorScope = nullptr; } }; + bool GetPotentialInteger(GLenum pname, GLint* param) { + LocalErrorScope localError(*this); + + fGetIntegerv(pname, param); + + GLenum err = localError.GetError(); + MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_INVALID_ENUM); + return err == LOCAL_GL_NO_ERROR; + } + private: static void GLAPIENTRY StaticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const GLvoid* userParam); @@ -717,53 +700,57 @@ private: # define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__ # elif defined(_MSC_VER) # define MOZ_FUNCTION_NAME __FUNCTION__ # else # define MOZ_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name. # endif #endif - void BeforeGLCall(const char* glFunction) { + void BeforeGLCall(const char* funcName) { MOZ_ASSERT(IsCurrent()); + if (DebugMode()) { - GLContext *currentGLContext = nullptr; - - currentGLContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS); + FlushErrors(); if (DebugMode() & DebugTrace) - printf_stderr("[gl:%p] > %s\n", this, glFunction); - if (this != currentGLContext) { - printf_stderr("Fatal: %s called on non-current context %p. " - "The current context for this thread is %p.\n", - glFunction, this, currentGLContext); - NS_ABORT(); + printf_stderr("[gl:%p] > %s\n", this, funcName); + + GLContext* tlsContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS); + if (this != tlsContext) { + printf_stderr("Fatal: %s called on non-current context %p. The" + " current context for this thread is %p.\n", + funcName, this, tlsContext); + MOZ_CRASH("GLContext is not current."); } } } - void AfterGLCall(const char* glFunction) { + void AfterGLCall(const char* funcName) { if (DebugMode()) { // calling fFinish() immediately after every GL call makes sure that if this GL command crashes, // the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces // tend to be meaningless mSymbols.fFinish(); - GLenum err = GetUnpushedError(); - PushError(err); - - if (DebugMode() & DebugTrace) - printf_stderr("[gl:%p] < %s [0x%04x]\n", this, glFunction, err); - - if (err != LOCAL_GL_NO_ERROR) { - printf_stderr("GL ERROR: %s generated GL error %s(0x%04x)\n", - glFunction, - GLErrorToString(err), - err); + GLenum err = FlushErrors(); + + if (DebugMode() & DebugTrace) { + printf_stderr("[gl:%p] < %s [%s (0x%04x)]\n", this, funcName, + GLErrorToString(err), err); + } + + if (err != LOCAL_GL_NO_ERROR && + !mLocalErrorScope) + { + printf_stderr("[gl:%p] %s: Generated unexpected %s error." + " (0x%04x)\n", this, funcName, + GLErrorToString(err), err); + if (DebugMode() & DebugAbortOnError) - NS_ABORT(); + MOZ_CRASH("MOZ_GL_DEBUG_ABORT_ON_ERROR"); } } } GLContext *TrackingContext() { GLContext *tip = this; while (tip->mSharedContext) @@ -3475,32 +3462,17 @@ public: } protected: // Note that it does -not- clear the resized buffers. bool CreateScreenBuffer(const gfx::IntSize& size, const SurfaceCaps& caps) { if (!IsOffscreenSizeAllowed(size)) return false; - SurfaceCaps tryCaps = caps; - if (tryCaps.antialias) { - // AA path - if (CreateScreenBufferImpl(size, tryCaps)) - return true; - - NS_WARNING("CreateScreenBuffer failed to initialize an AA context! Falling back to no AA..."); - tryCaps.antialias = false; - } - MOZ_ASSERT(!tryCaps.antialias); - - if (CreateScreenBufferImpl(size, tryCaps)) - return true; - - NS_WARNING("CreateScreenBuffer failed to initialize non-AA context!"); - return false; + return CreateScreenBufferImpl(size, caps); } bool CreateScreenBufferImpl(const gfx::IntSize& size, const SurfaceCaps& caps); public: bool ResizeScreenBuffer(const gfx::IntSize& size); @@ -3637,16 +3609,19 @@ protected: : mMaxTextureSize; return width <= maxSize && height <= maxSize; } return true; } public: + GLsizei MaxSamples() const { + return mMaxSamples; + } void fViewport(GLint x, GLint y, GLsizei width, GLsizei height) { if (mViewportRect[0] == x && mViewportRect[1] == y && mViewportRect[2] == width && mViewportRect[3] == height) { return;
--- a/gfx/gl/GLScreenBuffer.cpp +++ b/gfx/gl/GLScreenBuffer.cpp @@ -567,18 +567,22 @@ DrawBuffer::Create(GLContext* const gl, if (!caps.color) { MOZ_ASSERT(!caps.alpha && !caps.depth && !caps.stencil); // Nothing is needed. return true; } - if (caps.antialias && formats.samples == 0) - return false; // Can't create it + if (caps.antialias) { + if (formats.samples == 0) + return false; // Can't create it. + + MOZ_ASSERT(formats.samples <= gl->MaxSamples()); + } GLuint colorMSRB = 0; GLuint depthRB = 0; GLuint stencilRB = 0; GLuint* pColorMSRB = caps.antialias ? &colorMSRB : nullptr; GLuint* pDepthRB = caps.depth ? &depthRB : nullptr; GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr; @@ -595,29 +599,30 @@ DrawBuffer::Create(GLContext* const gl, } else { if (!formats.depth) pDepthRB = nullptr; if (!formats.stencil) pStencilRB = nullptr; } - GLContext::ScopedLocalErrorCheck localError(gl); + GLContext::LocalErrorScope localError(*gl); CreateRenderbuffersForOffscreen(gl, formats, size, caps.antialias, pColorMSRB, pDepthRB, pStencilRB); GLuint fb = 0; gl->fGenFramebuffers(1, &fb); gl->AttachBuffersToFB(0, colorMSRB, depthRB, stencilRB, fb); UniquePtr<DrawBuffer> ret( new DrawBuffer(gl, size, fb, colorMSRB, depthRB, stencilRB) ); - GLenum err = localError.GetLocalError(); + GLenum err = localError.GetError(); + MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY); if (err || !gl->IsFramebufferComplete(fb)) return false; *out_buffer = Move(ret); return true; } DrawBuffer::~DrawBuffer() @@ -654,17 +659,17 @@ ReadBuffer::Create(GLContext* gl, } GLuint depthRB = 0; GLuint stencilRB = 0; GLuint* pDepthRB = caps.depth ? &depthRB : nullptr; GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr; - GLContext::ScopedLocalErrorCheck localError(gl); + GLContext::LocalErrorScope localError(*gl); CreateRenderbuffersForOffscreen(gl, formats, surf->mSize, caps.antialias, nullptr, pDepthRB, pStencilRB); GLuint colorTex = 0; GLuint colorRB = 0; GLenum target = 0; @@ -684,17 +689,18 @@ ReadBuffer::Create(GLContext* gl, GLuint fb = 0; gl->fGenFramebuffers(1, &fb); gl->AttachBuffersToFB(colorTex, colorRB, depthRB, stencilRB, fb, target); gl->mFBOMapping[fb] = surf; UniquePtr<ReadBuffer> ret( new ReadBuffer(gl, fb, depthRB, stencilRB, surf) ); - GLenum err = localError.GetLocalError(); + GLenum err = localError.GetError(); + MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY); if (err || !gl->IsFramebufferComplete(fb)) { ret = nullptr; } return Move(ret); } ReadBuffer::~ReadBuffer()
--- a/gfx/gl/SharedSurfaceGL.cpp +++ b/gfx/gl/SharedSurfaceGL.cpp @@ -21,22 +21,24 @@ using gfx::SurfaceFormat; SharedSurface_Basic::Create(GLContext* gl, const GLFormats& formats, const IntSize& size, bool hasAlpha) { UniquePtr<SharedSurface_Basic> ret; gl->MakeCurrent(); - GLContext::ScopedLocalErrorCheck localError(gl); + GLContext::LocalErrorScope localError(*gl); GLuint tex = CreateTexture(gl, formats.color_texInternalFormat, formats.color_texFormat, formats.color_texType, size); - GLenum err = localError.GetLocalError(); + + GLenum err = localError.GetError(); + MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY); if (err) { gl->fDeleteTextures(1, &tex); return Move(ret); } SurfaceFormat format = SurfaceFormat::B8G8R8X8; switch (formats.color_texInternalFormat) { case LOCAL_GL_RGB: @@ -116,27 +118,28 @@ SharedSurface_GLTexture::Create(GLContex GLuint tex = texture; bool ownsTex = false; UniquePtr<SharedSurface_GLTexture> ret; if (!tex) { - GLContext::ScopedLocalErrorCheck localError(prodGL); + GLContext::LocalErrorScope localError(*prodGL); - tex = CreateTextureForOffscreen(prodGL, formats, size); + tex = CreateTextureForOffscreen(prodGL, formats, size); - GLenum err = localError.GetLocalError(); - if (err) { - prodGL->fDeleteTextures(1, &tex); - return Move(ret); - } + GLenum err = localError.GetError(); + MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY); + if (err) { + prodGL->fDeleteTextures(1, &tex); + return Move(ret); + } - ownsTex = true; + ownsTex = true; } ret.reset( new SharedSurface_GLTexture(prodGL, consGL, size, hasAlpha, tex, ownsTex) ); return Move(ret); } SharedSurface_GLTexture::~SharedSurface_GLTexture()
--- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -917,35 +917,42 @@ CompositorD3D11::EnsureSize() mSize = rect.Size(); } void CompositorD3D11::VerifyBufferSize() { DXGI_SWAP_CHAIN_DESC swapDesc; - mSwapChain->GetDesc(&swapDesc); + HRESULT hr; + + hr = mSwapChain->GetDesc(&swapDesc); + if (Failed(hr)) { + return; + } if ((swapDesc.BufferDesc.Width == mSize.width && swapDesc.BufferDesc.Height == mSize.height) || mSize.width == 0 || mSize.height == 0) { return; } mDefaultRT = nullptr; if (IsRunningInWindowsMetro()) { - mSwapChain->ResizeBuffers(2, mSize.width, mSize.height, - DXGI_FORMAT_B8G8R8A8_UNORM, - 0); + hr = mSwapChain->ResizeBuffers(2, mSize.width, mSize.height, + DXGI_FORMAT_B8G8R8A8_UNORM, + 0); + HandleError(hr); mDisableSequenceForNextFrame = true; } else { - mSwapChain->ResizeBuffers(1, mSize.width, mSize.height, - DXGI_FORMAT_B8G8R8A8_UNORM, - 0); + hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height, + DXGI_FORMAT_B8G8R8A8_UNORM, + 0); + HandleError(hr); } } void CompositorD3D11::UpdateRenderTarget() { EnsureSize(); VerifyBufferSize(); @@ -1071,50 +1078,64 @@ CompositorD3D11::SetSamplerForFilter(Fil mContext->PSSetSamplers(0, 1, &sampler); } void CompositorD3D11::PaintToTarget() { nsRefPtr<ID3D11Texture2D> backBuf; + HRESULT hr; - mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment()); + hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment()); + if (Failed(hr)) { + return; + } D3D11_TEXTURE2D_DESC bbDesc; backBuf->GetDesc(&bbDesc); CD3D11_TEXTURE2D_DESC softDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height); softDesc.MipLevels = 1; softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; softDesc.Usage = D3D11_USAGE_STAGING; softDesc.BindFlags = 0; nsRefPtr<ID3D11Texture2D> readTexture; - HRESULT hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture)); + hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture)); + if (Failed(hr)) { + return; + } mContext->CopyResource(readTexture, backBuf); D3D11_MAPPED_SUBRESOURCE map; - mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map); + hr = mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map); + if (Failed(hr)) { + return; + } RefPtr<DataSourceSurface> sourceSurface = Factory::CreateWrappingDataSourceSurface((uint8_t*)map.pData, map.RowPitch, IntSize(bbDesc.Width, bbDesc.Height), SurfaceFormat::B8G8R8A8); mTarget->CopySurface(sourceSurface, IntRect(0, 0, bbDesc.Width, bbDesc.Height), IntPoint(-mTargetBounds.x, -mTargetBounds.y)); + mTarget->Flush(); mContext->Unmap(readTexture, 0); } void CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity) { + if (SUCCEEDED(hr)) { + return; + } // XXX - It would be nice to use gfxCriticalError, but it needs to // be made to work off the main thread first. MOZ_ASSERT(aSeverity != DebugAssert); if (aSeverity == Critical) { MOZ_CRASH("Unrecoverable D3D11 error"); } @@ -1130,23 +1151,21 @@ CompositorD3D11::HandleError(HRESULT hr, if (aSeverity == Recoverable) { NS_WARNING("Encountered a recoverable D3D11 error"); } } bool CompositorD3D11::Failed(HRESULT hr, Severity aSeverity) { - if (FAILED(hr)) { - HandleError(hr, aSeverity); - return true; - } - return false; + HandleError(hr, aSeverity); + return FAILED(hr); } bool CompositorD3D11::Succeeded(HRESULT hr, Severity aSeverity) { - return !Failed(hr, aSeverity); + HandleError(hr, aSeverity); + return SUCCEEDED(hr); } } }
--- a/gfx/layers/d3d11/CompositorD3D11.h +++ b/gfx/layers/d3d11/CompositorD3D11.h @@ -144,17 +144,17 @@ public: private: enum Severity { Recoverable, DebugAssert, Critical, }; - void HandleError(HRESULT hr, Severity aSeverity); + void HandleError(HRESULT hr, Severity aSeverity = DebugAssert); bool Failed(HRESULT hr, Severity aSeverity = DebugAssert); bool Succeeded(HRESULT hr, Severity aSeverity = DebugAssert); // ensure mSize is up to date with respect to mWidget void EnsureSize(); void VerifyBufferSize(); void UpdateRenderTarget(); bool CreateShaders();
--- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -597,16 +597,17 @@ CompositorParent::ResumeComposition() __android_log_print(ANDROID_LOG_INFO, "CompositorParent", "Unable to renew compositor surface; remaining in paused state"); #endif lock.NotifyAll(); return; } mPaused = false; + mLastCompose = TimeStamp::Now(); CompositeToTarget(nullptr);