diff --git a/accessible/src/html/HTMLTableAccessible.cpp b/accessible/src/html/HTMLTableAccessible.cpp
index 4322a58d7e84..f0b8b76d6930 100644
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -38,6 +38,7 @@
#include "nsTableOuterFrame.h"
using namespace mozilla;
+using namespace mozilla::dom;
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
@@ -869,15 +870,10 @@ HTMLTableAccessible::Description(nsString& aDescription)
bool
HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty)
{
- nsCOMPtr tableElt(do_QueryInterface(mContent));
- NS_ENSURE_TRUE(tableElt, false);
+ nsCOMPtr elements =
+ mContent->AsElement()->GetElementsByTagName(aTagName);
- nsCOMPtr nodeList;
- tableElt->GetElementsByTagName(aTagName, getter_AddRefs(nodeList));
- NS_ENSURE_TRUE(nodeList, false);
-
- nsCOMPtr foundItem;
- nodeList->Item(0, getter_AddRefs(foundItem));
+ Element* foundItem = elements->Item(0);
if (!foundItem)
return false;
@@ -886,11 +882,10 @@ HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty)
// Make sure that the item we found has contents and either has multiple
// children or the found item is not a whitespace-only text node.
- nsCOMPtr foundItemContent = do_QueryInterface(foundItem);
- if (foundItemContent->GetChildCount() > 1)
+ if (foundItem->GetChildCount() > 1)
return true; // Treat multiple child nodes as non-empty
- nsIContent *innerItemContent = foundItemContent->GetFirstChild();
+ nsIContent *innerItemContent = foundItem->GetFirstChild();
if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace())
return true;
@@ -901,8 +896,7 @@ HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty)
// caption element only. On another hand we create accessible object for
// the first entry of caption element (see
// HTMLTableAccessible::CacheChildren).
- nodeList->Item(1, getter_AddRefs(foundItem));
- return !!foundItem;
+ return !!elements->Item(1);
}
bool
diff --git a/browser/base/content/test/social/browser_social_status.js b/browser/base/content/test/social/browser_social_status.js
index 3e59e2dc0d55..7eaf99d36d45 100644
--- a/browser/base/content/test/social/browser_social_status.js
+++ b/browser/base/content/test/social/browser_social_status.js
@@ -112,6 +112,11 @@ var tests = {
iconURL: "chrome://browser/skin/Info.png",
counter: 1
};
+
+ // Disable the transition
+ let panel = document.getElementById("social-notification-panel");
+ panel.setAttribute("animate", "false");
+
// click on panel to open and wait for visibility
let provider = Social._getProviderFromOrigin(manifest2.origin);
let id = SocialStatus._toolbarHelper.idFromOrigin(manifest2.origin);
@@ -131,8 +136,8 @@ var tests = {
case "got-social-panel-visibility":
ok(true, "got the panel message " + e.data.result);
if (e.data.result == "shown") {
- let panel = document.getElementById("social-notification-panel");
panel.hidePopup();
+ panel.removeAttribute("animate");
} else {
port.postMessage({topic: "test-ambient-notification", data: icon});
port.close();
diff --git a/browser/base/content/test/social/head.js b/browser/base/content/test/social/head.js
index 53cc7e25c419..61a1105abc7f 100644
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -63,11 +63,14 @@ function checkProviderPrefsEmpty(isError) {
}
function defaultFinishChecks() {
+ PopupNotifications.transitionsEnabled = true;
checkProviderPrefsEmpty(true);
finish();
}
function runSocialTestWithProvider(manifest, callback, finishcallback) {
+ PopupNotifications.transitionsEnabled = false;
+
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
let manifests = Array.isArray(manifest) ? manifest : [manifest];
@@ -158,6 +161,8 @@ function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) {
let providersAtStart = Social.providers.length;
info("runSocialTests: start test run with " + providersAtStart + " providers");
+ PopupNotifications.transitionsEnabled = false;
+
if (cbPreTest === undefined) {
cbPreTest = function(cb) {cb()};
}
diff --git a/browser/components/customizableui/content/panelUI.inc.xul b/browser/components/customizableui/content/panelUI.inc.xul
index 1d1740ee5ee7..38e446355087 100644
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -7,7 +7,6 @@
type="arrow"
hidden="true"
flip="slide"
- animate="false"
position="bottomcenter topright"
noautofocus="true">
diff --git a/config/config.mk b/config/config.mk
index a9fa56a95f2c..be861dc7470a 100644
--- a/config/config.mk
+++ b/config/config.mk
@@ -54,6 +54,7 @@ _MOZBUILD_EXTERNAL_VARIABLES := \
JAR_MANIFEST \
JAVA_JAR_TARGETS \
JS_MODULES_PATH \
+ LD_VERSION_SCRIPT \
LIBRARY_NAME \
MODULE \
MSVC_ENABLE_PGO \
diff --git a/config/rules.mk b/config/rules.mk
index c9cb9f2a0fe6..b958cccefe71 100644
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -510,6 +510,10 @@ ifeq ($(OS_ARCH),Linux)
ifdef IS_COMPONENT
EXTRA_DSO_LDOPTS += -Wl,-Bsymbolic
endif
+ifdef LD_VERSION_SCRIPT
+EXTRA_DSO_LDOPTS += -Wl,--version-script,$(LD_VERSION_SCRIPT)
+EXTRA_DEPS += $(LD_VERSION_SCRIPT)
+endif
endif
#
@@ -1020,6 +1024,10 @@ $(filter %.s,$(CPPSRCS:%.cc=%.s)): %.s: %.cc $(call mkdir_deps,$(MDDEPDIR))
$(REPORT_BUILD)
$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+$(filter %.s,$(CPPSRCS:%.cxx=%.s)): %.s: %.cpp $(call mkdir_deps,$(MDDEPDIR))
+ $(REPORT_BUILD)
+ $(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+
$(filter %.s,$(CSRCS:%.c=%.s)): %.s: %.c $(call mkdir_deps,$(MDDEPDIR))
$(REPORT_BUILD)
$(CC) -S $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
@@ -1035,6 +1043,7 @@ ifneq (,$(filter %.i,$(MAKECMDGOALS)))
_group_srcs = $(sort $(patsubst %.$1,%.i,$(filter %.$1,$2 $(notdir $2))))
_PREPROCESSED_CPP_FILES := $(call _group_srcs,cpp,$(CPPSRCS))
_PREPROCESSED_CC_FILES := $(call _group_srcs,cc,$(CPPSRCS))
+_PREPROCESSED_CXX_FILES := $(call _group_srcs,cxx,$(CPPSRCS))
_PREPROCESSED_C_FILES := $(call _group_srcs,c,$(CSRCS))
_PREPROCESSED_CMM_FILES := $(call _group_srcs,mm,$(CMMSRCS))
@@ -1044,7 +1053,7 @@ VPATH += $(addprefix $(srcdir)/,$(sort $(dir $(CPPSRCS) $(CSRCS) $(CMMSRCS))))
# Make preprocessed files PHONY so they are always executed, since they are
# manual targets and we don't necessarily write to $@.
-.PHONY: $(_PREPROCESSED_CPP_FILES) $(_PREPROCESSED_CC_FILES) $(_PREPROCESSED_C_FILES) $(_PREPROCESSED_CMM_FILES)
+.PHONY: $(_PREPROCESSED_CPP_FILES) $(_PREPROCESSED_CC_FILES) $(_PREPROCESSED_CXX_FILES) $(_PREPROCESSED_C_FILES) $(_PREPROCESSED_CMM_FILES)
$(_PREPROCESSED_CPP_FILES): %.i: %.cpp $(call mkdir_deps,$(MDDEPDIR))
$(REPORT_BUILD)
@@ -1056,6 +1065,11 @@ $(_PREPROCESSED_CC_FILES): %.i: %.cc $(call mkdir_deps,$(MDDEPDIR))
$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+$(_PREPROCESSED_CXX_FILES): %.i: %.cxx $(call mkdir_deps,$(MDDEPDIR))
+ $(REPORT_BUILD)
+ $(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
+ $(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+
$(_PREPROCESSED_C_FILES): %.i: %.c $(call mkdir_deps,$(MDDEPDIR))
$(REPORT_BUILD)
$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
@@ -1074,7 +1088,7 @@ PP_UNIFIED ?= 1
# infinite loop if the filename doesn't exist in the unified source files.
ifndef PP_REINVOKE
-MATCH_cpp = \(cpp\|cc\)
+MATCH_cpp = \(cpp\|cc|cxx\)
UPPER_c = C
UPPER_cpp = CPP
UPPER_mm = CMM
diff --git a/configure.in b/configure.in
index 063133ab8d2c..262eadc2d608 100644
--- a/configure.in
+++ b/configure.in
@@ -2928,7 +2928,7 @@ dnl Checks for library functions.
dnl ========================================================
AC_PROG_GCC_TRADITIONAL
AC_FUNC_MEMCMP
-AC_CHECK_FUNCS(stat64 lstat64 truncate64 statvfs64 statvfs statfs64 statfs getpagesize localtime_r)
+AC_CHECK_FUNCS(stat64 lstat64 truncate64 statvfs64 statvfs statfs64 statfs getpagesize localtime_r arc4random arc4random_buf)
dnl check for clock_gettime(), the CLOCK_MONOTONIC clock
AC_CACHE_CHECK(for clock_gettime(CLOCK_MONOTONIC),
@@ -3589,7 +3589,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
_USE_SYSTEM_NSS=1 )
if test -n "$_USE_SYSTEM_NSS"; then
- AM_PATH_NSS(3.16, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
+ AM_PATH_NSS(3.16.1, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
fi
if test -n "$MOZ_NATIVE_NSS"; then
@@ -8803,6 +8803,7 @@ AC_SUBST(CPU_ARCH)
AC_SUBST(INTEL_ARCHITECTURE)
AC_SUBST(HAVE_TOOLCHAIN_SUPPORT_MSSSE3)
AC_SUBST(HAVE_TOOLCHAIN_SUPPORT_MSSE4_1)
+AC_SUBST(GCC_USE_GNU_LD)
AC_SUBST(MOZ_CHROME_FILE_FORMAT)
diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h
index d2f225dae8f6..bc6c79b0bb79 100644
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -1350,6 +1350,8 @@ private:
// Set if the element has a parser insertion mode other than "in body",
// per the HTML5 "Parse state" section.
ElementHasWeirdParserInsertionMode,
+ // Parser sets this flag if it has notified about the node.
+ ParserHasNotified,
// Guard value
BooleanFlagCount
};
@@ -1490,6 +1492,8 @@ public:
bool IsScopedStyleRoot() { return GetBoolFlag(ElementIsScopedStyleRoot); }
bool HasRelevantHoverRules() const { return GetBoolFlag(NodeHasRelevantHoverRules); }
void SetHasRelevantHoverRules() { SetBoolFlag(NodeHasRelevantHoverRules); }
+ void SetParserHasNotified() { SetBoolFlag(ParserHasNotified); };
+ bool HasParserNotified() { return GetBoolFlag(ParserHasNotified); }
protected:
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
void SetInDocument() { SetBoolFlag(IsInDocument); }
diff --git a/content/base/src/WebSocket.cpp b/content/base/src/WebSocket.cpp
index a6e06f7234c2..44d554306aec 100644
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -559,7 +559,7 @@ WebSocket::Constructor(const GlobalObject& aGlobal,
}
nsRefPtr webSocket = new WebSocket(ownerWindow);
- nsresult rv = webSocket->Init(aGlobal.GetContext(), principal,
+ nsresult rv = webSocket->Init(aGlobal.Context(), principal,
aUrl, protocolArray);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
diff --git a/content/canvas/src/ImageData.cpp b/content/canvas/src/ImageData.cpp
index ebb10219a3d1..5c04a9231c15 100644
--- a/content/canvas/src/ImageData.cpp
+++ b/content/canvas/src/ImageData.cpp
@@ -52,8 +52,8 @@ ImageData::Constructor(const GlobalObject& aGlobal,
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return nullptr;
}
- js::AssertSameCompartment(aGlobal.GetContext(), aGlobal.Get());
- JSObject* data = Uint8ClampedArray::Create(aGlobal.GetContext(),
+ js::AssertSameCompartment(aGlobal.Context(), aGlobal.Get());
+ JSObject* data = Uint8ClampedArray::Create(aGlobal.Context(),
length.value());
if (!data) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
diff --git a/content/html/content/src/HTMLOutputElement.cpp b/content/html/content/src/HTMLOutputElement.cpp
index 037236dab2eb..4c6fbc6f1610 100644
--- a/content/html/content/src/HTMLOutputElement.cpp
+++ b/content/html/content/src/HTMLOutputElement.cpp
@@ -13,14 +13,16 @@
#include "nsDOMSettableTokenList.h"
#include "nsFormSubmission.h"
-NS_IMPL_NS_NEW_HTML_ELEMENT(Output)
+NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Output)
namespace mozilla {
namespace dom {
-HTMLOutputElement::HTMLOutputElement(already_AddRefed& aNodeInfo)
+HTMLOutputElement::HTMLOutputElement(already_AddRefed& aNodeInfo,
+ FromParser aFromParser)
: nsGenericHTMLFormElement(aNodeInfo)
, mValueModeFlag(eModeDefault)
+ , mIsDoneAddingChildren(!aFromParser)
{
AddMutationObserver(this);
@@ -93,6 +95,12 @@ HTMLOutputElement::ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute,
aValue, aResult);
}
+void
+HTMLOutputElement::DoneAddingChildren(bool aHaveNotified)
+{
+ mIsDoneAddingChildren = true;
+}
+
EventStates
HTMLOutputElement::IntrinsicState() const
{
@@ -170,7 +178,7 @@ HTMLOutputElement::HtmlFor()
void HTMLOutputElement::DescendantsChanged()
{
- if (mValueModeFlag == eModeDefault) {
+ if (mIsDoneAddingChildren && mValueModeFlag == eModeDefault) {
if (!nsContentUtils::GetNodeTextContent(this, true, mDefaultValue)) {
NS_RUNTIMEABORT("OOM");
}
diff --git a/content/html/content/src/HTMLOutputElement.h b/content/html/content/src/HTMLOutputElement.h
index 11041f9c4b4b..cf2617d0644e 100644
--- a/content/html/content/src/HTMLOutputElement.h
+++ b/content/html/content/src/HTMLOutputElement.h
@@ -21,7 +21,8 @@ class HTMLOutputElement MOZ_FINAL : public nsGenericHTMLFormElement,
public:
using nsIConstraintValidation::GetValidationMessage;
- HTMLOutputElement(already_AddRefed& aNodeInfo);
+ HTMLOutputElement(already_AddRefed& aNodeInfo,
+ FromParser aFromParser = NOT_FROM_PARSER);
virtual ~HTMLOutputElement();
// nsISupports
@@ -39,6 +40,8 @@ public:
bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute,
const nsAString& aValue, nsAttrValue& aResult) MOZ_OVERRIDE;
+ virtual void DoneAddingChildren(bool aHaveNotified) MOZ_OVERRIDE;
+
EventStates IntrinsicState() const MOZ_OVERRIDE;
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
@@ -101,6 +104,7 @@ protected:
};
ValueModeFlag mValueModeFlag;
+ bool mIsDoneAddingChildren;
nsString mDefaultValue;
nsRefPtr mTokenList;
};
diff --git a/content/media/MediaDecoderStateMachine.cpp b/content/media/MediaDecoderStateMachine.cpp
index f448a9fb713b..665e48c485fc 100644
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -1966,6 +1966,25 @@ void MediaDecoderStateMachine::DecodeSeek()
video = mReader->FindStartTime(nextSampleStartTime);
}
+ if (seekTime > mediaTime &&
+ nextSampleStartTime < mediaTime &&
+ mSeekTarget.mType == SeekTarget::PrevSyncPoint) {
+ // We are doing a fastSeek, but we ended up *before* the previous
+ // playback position. This is surprising UX, so switch to an accurate
+ // seek and decode to the seek target. This is not conformant to the
+ // spec, fastSeek should always be fast, but until we get the time to
+ // change all Readers to seek to the keyframe after the currentTime
+ // in this case, we'll just decode forward. Bug 1026330.
+ ResetPlayback();
+ {
+ ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
+ res = mReader->DecodeToTarget(seekTime);
+ if (NS_SUCCEEDED(res)) {
+ video = mReader->FindStartTime(nextSampleStartTime);
+ }
+ }
+ }
+
// Setup timestamp state.
if (seekTime == mEndTime) {
newCurrentTime = mAudioStartTime = seekTime;
diff --git a/content/media/eme/MediaKeyMessageEvent.cpp b/content/media/eme/MediaKeyMessageEvent.cpp
index 8af59724a12b..d5e2f4ebbab6 100644
--- a/content/media/eme/MediaKeyMessageEvent.cpp
+++ b/content/media/eme/MediaKeyMessageEvent.cpp
@@ -86,9 +86,9 @@ MediaKeyMessageEvent::Constructor(const GlobalObject& aGlobal,
if (aEventInitDict.mMessage.WasPassed()) {
const auto& a = aEventInitDict.mMessage.Value();
a.ComputeLengthAndData();
- e->mMessage = Uint8Array::Create(aGlobal.GetContext(), owner, a.Length(), a.Data());
+ e->mMessage = Uint8Array::Create(aGlobal.Context(), owner, a.Length(), a.Data());
} else {
- e->mMessage = Uint8Array::Create(aGlobal.GetContext(), owner, 0, nullptr);
+ e->mMessage = Uint8Array::Create(aGlobal.Context(), owner, 0, nullptr);
}
if (!e->mMessage) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
diff --git a/content/media/eme/MediaKeyNeededEvent.cpp b/content/media/eme/MediaKeyNeededEvent.cpp
index 9305e8a90d4e..715e35a1d3b3 100644
--- a/content/media/eme/MediaKeyNeededEvent.cpp
+++ b/content/media/eme/MediaKeyNeededEvent.cpp
@@ -78,7 +78,7 @@ MediaKeyNeededEvent::Constructor(const GlobalObject& aGlobal,
!aEventInitDict.mInitData.Value().IsNull()) {
const auto& a = aEventInitDict.mInitData.Value().Value();
a.ComputeLengthAndData();
- e->mInitData = Uint8Array::Create(aGlobal.GetContext(), owner, a.Length(), a.Data());
+ e->mInitData = Uint8Array::Create(aGlobal.Context(), owner, a.Length(), a.Data());
if (!e->mInitData) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return nullptr;
diff --git a/content/media/test/crashtests/0-timescale.html b/content/media/test/crashtests/0-timescale.html
new file mode 100644
index 000000000000..72cb843d6e4f
--- /dev/null
+++ b/content/media/test/crashtests/0-timescale.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/content/media/test/crashtests/0-timescale.mp4 b/content/media/test/crashtests/0-timescale.mp4
new file mode 100644
index 000000000000..32df5dc5a559
Binary files /dev/null and b/content/media/test/crashtests/0-timescale.mp4 differ
diff --git a/content/media/test/crashtests/crashtests.list b/content/media/test/crashtests/crashtests.list
index 4585bc4aa285..18492ffcf826 100644
--- a/content/media/test/crashtests/crashtests.list
+++ b/content/media/test/crashtests/crashtests.list
@@ -1,3 +1,4 @@
+skip-if(B2G) load 0-timescale.html
skip-if(B2G) load 459439-1.html # bug 888557
load 466607-1.html
load 466945-1.html
diff --git a/content/media/test/mochitest.ini b/content/media/test/mochitest.ini
index a75c74487d7e..37219e5ea3fb 100644
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -347,6 +347,7 @@ skip-if = buildapp == 'b2g' # bug 1021676
[test_error_in_video_document.html]
[test_error_on_404.html]
[test_fastSeek.html]
+[test_fastSeek-forwards.html]
[test_info_leak.html]
[test_invalid_reject.html]
[test_load.html]
diff --git a/content/media/test/test_fastSeek-forwards.html b/content/media/test/test_fastSeek-forwards.html
new file mode 100644
index 000000000000..a7258ddf71c5
--- /dev/null
+++ b/content/media/test/test_fastSeek-forwards.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+ Test for Bug 1022913
+
+
+
+
+
+
+Mozilla Bug 1022913
+
+
+
+
+
+
+
+
diff --git a/db/sqlite3/src/Makefile.in b/db/sqlite3/src/Makefile.in
index 4b5041cd2fcd..12364e94025f 100644
--- a/db/sqlite3/src/Makefile.in
+++ b/db/sqlite3/src/Makefile.in
@@ -5,6 +5,8 @@
LIB_IS_C_ONLY = 1
+include $(topsrcdir)/config/config.mk
+
ifeq ($(OS_ARCH),WINNT)
DEFFILE = $(CURDIR)/sqlite-processed.def
@@ -20,10 +22,25 @@ sqlite-version.h: sqlite-version.py sqlite3.h
# We have to preprocess our def file because we need different symbols in debug
# builds exposed that are not built in non-debug builds.
$(DEFFILE): sqlite.def
- @$(call py_action,preprocessor,$(DEFINES) \
+ @$(call py_action,preprocessor,$(DEFINES) $(XULPPFLAGS) \
$(srcdir)/sqlite.def -o $(DEFFILE))
export:: sqlite-version.h
+else
+ifndef MOZ_FOLD_LIBS
+ifdef GCC_USE_GNU_LD
+
+GARBAGE += \
+ $(LD_VERSION_SCRIPT) \
+ $(NULL)
+
+# Convert to the format we need for ld.
+$(LD_VERSION_SCRIPT): $(srcdir)/sqlite.def
+ @$(call py_action,convert_def_file, \
+ $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) -o $@ $^)
+
+endif
+endif
endif
ifeq (Darwin,$(OS_TARGET))
diff --git a/db/sqlite3/src/moz.build b/db/sqlite3/src/moz.build
index f899190436ea..14c79b48f067 100644
--- a/db/sqlite3/src/moz.build
+++ b/db/sqlite3/src/moz.build
@@ -74,6 +74,11 @@ if CONFIG['OS_ARCH'] == 'WINNT':
RCFILE = 'sqlite.rc'
RESFILE = 'sqlite.res'
+if CONFIG['OS_ARCH'] == 'Linux' and \
+ not CONFIG['MOZ_FOLD_LIBS'] and \
+ CONFIG['GCC_USE_GNU_LD']:
+ LD_VERSION_SCRIPT = 'sqlite-processed.def'
+
# Suppress warnings in third-party code.
if CONFIG['GNU_CC']:
CFLAGS += [
diff --git a/db/sqlite3/src/sqlite.def b/db/sqlite3/src/sqlite.def
index 5eeb52257ae1..c9f741a5796f 100644
--- a/db/sqlite3/src/sqlite.def
+++ b/db/sqlite3/src/sqlite.def
@@ -126,6 +126,9 @@ EXPORTS
sqlite3_step
sqlite3_stmt_readonly
sqlite3_stmt_status
+#ifdef XP_UNIX
+ sqlite3_temp_directory
+#endif
sqlite3_thread_cleanup
sqlite3_total_changes
sqlite3_trace
@@ -151,7 +154,7 @@ EXPORTS
sqlite3_vfs_unregister
sqlite3_vfs_register
sqlite3_vmprintf
-#ifdef SQLITE_DEBUG
+#ifdef DEBUG
sqlite3_mutex_held
sqlite3_mutex_notheld
#endif
diff --git a/dom/activities/src/Activity.h b/dom/activities/src/Activity.h
index 520f19b467bc..9a36bbce7db0 100644
--- a/dom/activities/src/Activity.h
+++ b/dom/activities/src/Activity.h
@@ -25,7 +25,6 @@ public:
static already_AddRefed
Constructor(const GlobalObject& aOwner,
- JSContext* aCx,
const ActivityOptions& aOptions,
ErrorResult& aRv)
{
@@ -36,7 +35,7 @@ public:
}
nsRefPtr activity = new Activity(window);
- aRv = activity->Initialize(window, aCx, aOptions);
+ aRv = activity->Initialize(window, aOwner.Context(), aOptions);
return activity.forget();
}
diff --git a/dom/base/Console.cpp b/dom/base/Console.cpp
index 7bae4c87b126..c70b62faa32c 100644
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -910,7 +910,7 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
ErrorResult rv;
nsRefPtr performance = win->GetPerformance(rv);
- if (rv.Failed()) {
+ if (rv.Failed() || !performance) {
return;
}
diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h
index c16c444d3414..51b6fa980b59 100644
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -70,7 +70,7 @@ public:
// The context that this returns is not guaranteed to be in the compartment of
// the object returned from Get(), in fact it's generally in the caller's
// compartment.
- JSContext* GetContext() const
+ JSContext* Context() const
{
return mCx;
}
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
index c423ab7f3a90..5135216f3ad4 100644
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1835,6 +1835,7 @@ GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)
mCx(aCx),
mGlobalObject(nullptr)
{
+ MOZ_ASSERT(mCx);
JS::Rooted obj(aCx, aObject);
if (js::IsWrapper(obj)) {
obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
@@ -2437,7 +2438,7 @@ ConvertExceptionToPromise(JSContext* cx,
JS_ClearPendingException(cx);
ErrorResult rv;
- nsRefPtr promise = Promise::Reject(global, cx, exn, rv);
+ nsRefPtr promise = Promise::Reject(global, exn, rv);
if (rv.Failed()) {
// We just give up. Make sure to not leak memory on the
// ErrorResult, but then just put the original exception back.
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
index 0ff25a7b6b67..af6145b000e9 100644
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5726,8 +5726,9 @@ def isResultAlreadyAddRefed(extendedAttributes):
return 'resultNotAddRefed' not in extendedAttributes
-def needCx(returnType, arguments, extendedAttributes, considerTypes):
- return (considerTypes and
+def needCx(returnType, arguments, extendedAttributes, considerTypes,
+ static=False):
+ return (not static and considerTypes and
(typeNeedsCx(returnType, True) or
any(typeNeedsCx(a.type) for a in arguments)) or
'implicitJSContext' in extendedAttributes)
@@ -6057,10 +6058,11 @@ class CGPerSignatureCall(CGThing):
# For JS-implemented interfaces we do not want to base the
# needsCx decision on the types involved, just on our extended
- # attributes.
+ # attributes. Also, JSContext is not needed for the static case
+ # since GlobalObject already contains the context.
needsCx = needCx(returnType, arguments, self.extendedAttributes,
- not descriptor.interface.isJSImplemented())
- if needsCx and not (static and descriptor.workers):
+ not descriptor.interface.isJSImplemented(), static)
+ if needsCx:
argsPre.append("cx")
needsUnwrap = False
@@ -11708,7 +11710,7 @@ class CGNativeMember(ClassMethod):
args.insert(0, Argument("JS::Value", "aThisVal"))
# And jscontext bits.
if needCx(returnType, argList, self.extendedAttrs,
- self.passJSBitsAsNeeded):
+ self.passJSBitsAsNeeded, self.member.isStatic()):
args.insert(0, Argument("JSContext*", "cx"))
if needScopeObject(returnType, argList, self.extendedAttrs,
self.descriptorProvider.wrapperCache,
@@ -13647,7 +13649,7 @@ class CGEventMethod(CGNativeMember):
self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
constructorForNativeCaller = CGNativeMember.declare(self, cgClass)
self.args = list(self.originalArgs)
- if needCx(None, self.arguments(), [], True):
+ if needCx(None, self.arguments(), [], considerTypes=True, static=True):
self.args.insert(0, Argument("JSContext*", "aCx"))
self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
self.args.append(Argument('ErrorResult&', 'aRv'))
@@ -13698,7 +13700,7 @@ class CGEventMethod(CGNativeMember):
""",
arg0=self.args[0].name,
arg1=self.args[1].name)
- if needCx(None, self.arguments(), [], True):
+ if needCx(None, self.arguments(), [], considerTypes=True, static=True):
self.args.insert(0, Argument("JSContext*", "aCx"))
self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
self.args.append(Argument('ErrorResult&', 'aRv'))
diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h
index c4def67e38e2..9ad7699036a7 100644
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -151,7 +151,6 @@ public:
static
already_AddRefed Test2(const GlobalObject&,
- JSContext*,
const DictForConstructor&,
JS::Handle,
JS::Handle,
@@ -682,8 +681,7 @@ public:
// Static methods and attributes
static void StaticMethod(const GlobalObject&, bool);
- static void StaticMethodWithContext(const GlobalObject&, JSContext*,
- JS::Value);
+ static void StaticMethodWithContext(const GlobalObject&, JS::Value);
static bool StaticAttribute(const GlobalObject&);
static void SetStaticAttribute(const GlobalObject&, bool);
diff --git a/dom/browser-element/mochitest/mochitest.ini b/dom/browser-element/mochitest/mochitest.ini
index 92d44aa41fe5..0e5e97f13d65 100644
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -196,7 +196,3 @@ disabled = bug 774100
# Disabled due to focus issues (no bug that I'm aware of)
[test_browserElement_oop_KeyEvents.html]
disabled =
-# Disable due to certificate issue (no bug that I'm aware of)
-[test_browserElement_inproc_ErrorSecurity.html]
-skip-if = buildapp=='b2g'
-disabled =
diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp
index 0d8d1ea328f2..646dd88ae608 100644
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -523,6 +523,7 @@ void
Event::SetEventType(const nsAString& aEventTypeArg)
{
if (mIsMainThreadEvent) {
+ mEvent->typeString.Truncate();
mEvent->userType =
nsContentUtils::GetEventIdAndAtom(aEventTypeArg, mEvent->eventStructType,
&(mEvent->message));
diff --git a/dom/events/MessageEvent.cpp b/dom/events/MessageEvent.cpp
index 66e583658170..2d06aefb7a24 100644
--- a/dom/events/MessageEvent.cpp
+++ b/dom/events/MessageEvent.cpp
@@ -114,7 +114,7 @@ MessageEvent::GetSource(Nullable& aValue) const
/* static */ already_AddRefed
MessageEvent::Constructor(const GlobalObject& aGlobal,
- JSContext* aCx, const nsAString& aType,
+ const nsAString& aType,
const MessageEventInit& aParam,
ErrorResult& aRv)
{
diff --git a/dom/events/MessageEvent.h b/dom/events/MessageEvent.h
index fa403d622be6..2d45b4e08c3b 100644
--- a/dom/events/MessageEvent.h
+++ b/dom/events/MessageEvent.h
@@ -70,7 +70,7 @@ public:
}
static already_AddRefed
- Constructor(const GlobalObject& aGlobal, JSContext* aCx,
+ Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const MessageEventInit& aEventInit,
ErrorResult& aRv);
diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini
index 0b790b6e8675..4eeff715106e 100644
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -157,3 +157,4 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
skip-if = buildapp == 'b2g' || e10s
[test_bug985988.html]
[test_dom_storage_event.html]
+[test_bug998809.html]
diff --git a/dom/events/test/test_bug998809.html b/dom/events/test/test_bug998809.html
new file mode 100644
index 000000000000..08499d2c75af
--- /dev/null
+++ b/dom/events/test/test_bug998809.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Test for Bug 998809
+
+
+
+
+
+Mozilla Bug 998809
+
+
+
+
+
+
+
+
diff --git a/dom/indexedDB/IDBKeyRange.cpp b/dom/indexedDB/IDBKeyRange.cpp
index 370b44d15ea3..7c69077a01a6 100644
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -214,7 +214,7 @@ IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle aResult,
// static
already_AddRefed
-IDBKeyRange::Only(const GlobalObject& aGlobal, JSContext* aCx,
+IDBKeyRange::Only(const GlobalObject& aGlobal,
JS::Handle aValue, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
@@ -222,7 +222,7 @@ IDBKeyRange::Only(const GlobalObject& aGlobal, JSContext* aCx,
nsRefPtr keyRange =
new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true);
- aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Lower());
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower());
if (aRv.Failed()) {
return nullptr;
}
@@ -232,7 +232,7 @@ IDBKeyRange::Only(const GlobalObject& aGlobal, JSContext* aCx,
// static
already_AddRefed
-IDBKeyRange::LowerBound(const GlobalObject& aGlobal, JSContext* aCx,
+IDBKeyRange::LowerBound(const GlobalObject& aGlobal,
JS::Handle aValue, bool aOpen,
ErrorResult& aRv)
{
@@ -241,7 +241,7 @@ IDBKeyRange::LowerBound(const GlobalObject& aGlobal, JSContext* aCx,
nsRefPtr keyRange =
new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false);
- aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Lower());
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower());
if (aRv.Failed()) {
return nullptr;
}
@@ -251,7 +251,7 @@ IDBKeyRange::LowerBound(const GlobalObject& aGlobal, JSContext* aCx,
// static
already_AddRefed
-IDBKeyRange::UpperBound(const GlobalObject& aGlobal, JSContext* aCx,
+IDBKeyRange::UpperBound(const GlobalObject& aGlobal,
JS::Handle aValue, bool aOpen,
ErrorResult& aRv)
{
@@ -260,7 +260,7 @@ IDBKeyRange::UpperBound(const GlobalObject& aGlobal, JSContext* aCx,
nsRefPtr keyRange =
new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false);
- aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Upper());
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Upper());
if (aRv.Failed()) {
return nullptr;
}
@@ -270,7 +270,7 @@ IDBKeyRange::UpperBound(const GlobalObject& aGlobal, JSContext* aCx,
// static
already_AddRefed
-IDBKeyRange::Bound(const GlobalObject& aGlobal, JSContext* aCx,
+IDBKeyRange::Bound(const GlobalObject& aGlobal,
JS::Handle aLower, JS::Handle aUpper,
bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv)
{
@@ -279,12 +279,12 @@ IDBKeyRange::Bound(const GlobalObject& aGlobal, JSContext* aCx,
nsRefPtr keyRange =
new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false);
- aRv = GetKeyFromJSVal(aCx, aLower, keyRange->Lower());
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aLower, keyRange->Lower());
if (aRv.Failed()) {
return nullptr;
}
- aRv = GetKeyFromJSVal(aCx, aUpper, keyRange->Upper());
+ aRv = GetKeyFromJSVal(aGlobal.Context(), aUpper, keyRange->Upper());
if (aRv.Failed()) {
return nullptr;
}
diff --git a/dom/indexedDB/IDBKeyRange.h b/dom/indexedDB/IDBKeyRange.h
index 618a364c53b3..85ade2510917 100644
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -177,19 +177,19 @@ public:
}
static already_AddRefed
- Only(const GlobalObject& aGlobal, JSContext* aCx,
+ Only(const GlobalObject& aGlobal,
JS::Handle aValue, ErrorResult& aRv);
static already_AddRefed
- LowerBound(const GlobalObject& aGlobal, JSContext* aCx,
+ LowerBound(const GlobalObject& aGlobal,
JS::Handle aValue, bool aOpen, ErrorResult& aRv);
static already_AddRefed
- UpperBound(const GlobalObject& aGlobal, JSContext* aCx,
+ UpperBound(const GlobalObject& aGlobal,
JS::Handle aValue, bool aOpen, ErrorResult& aRv);
static already_AddRefed
- Bound(const GlobalObject& aGlobal, JSContext* aCx,
+ Bound(const GlobalObject& aGlobal,
JS::Handle aLower, JS::Handle aUpper,
bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv);
diff --git a/dom/nfc/MozNDEFRecord.cpp b/dom/nfc/MozNDEFRecord.cpp
index fbd87630e899..46da47b88752 100644
--- a/dom/nfc/MozNDEFRecord.cpp
+++ b/dom/nfc/MozNDEFRecord.cpp
@@ -79,7 +79,7 @@ MozNDEFRecord::Constructor(const GlobalObject& aGlobal,
return nullptr;
}
- nsRefPtr ndefrecord = new MozNDEFRecord(aGlobal.GetContext(),
+ nsRefPtr ndefrecord = new MozNDEFRecord(aGlobal.Context(),
win, aTnf, aType, aId,
aPayload);
if (!ndefrecord) {
diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp
index aff7fd2247db..117c7d029173 100644
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -593,7 +593,7 @@ Promise::CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTas
Promise::Constructor(const GlobalObject& aGlobal,
PromiseInit& aInit, ErrorResult& aRv)
{
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
nsCOMPtr global;
global = do_QueryInterface(aGlobal.GetAsSupports());
@@ -641,12 +641,12 @@ Promise::Constructor(const GlobalObject& aGlobal,
}
/* static */ already_AddRefed
-Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
+Promise::Resolve(const GlobalObject& aGlobal,
JS::Handle aValue, ErrorResult& aRv)
{
// If a Promise was passed, just return it.
if (aValue.isObject()) {
- JS::Rooted valueObj(aCx, &aValue.toObject());
+ JS::Rooted valueObj(aGlobal.Context(), &aValue.toObject());
Promise* nextPromise;
nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise);
@@ -663,7 +663,7 @@ Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
return nullptr;
}
- return Resolve(global, aCx, aValue, aRv);
+ return Resolve(global, aGlobal.Context(), aValue, aRv);
}
/* static */ already_AddRefed
@@ -677,7 +677,7 @@ Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
}
/* static */ already_AddRefed
-Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
+Promise::Reject(const GlobalObject& aGlobal,
JS::Handle aValue, ErrorResult& aRv)
{
nsCOMPtr global =
@@ -687,7 +687,7 @@ Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
return nullptr;
}
- return Reject(global, aCx, aValue, aRv);
+ return Reject(global, aGlobal.Context(), aValue, aRv);
}
/* static */ already_AddRefed
@@ -744,9 +744,9 @@ public:
: mPromise(aPromise), mCountdown(aCountdown)
{
MOZ_ASSERT(aCountdown != 0);
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
- // The only time aGlobal.GetContext() and aGlobal.Get() are not
+ // The only time aGlobal.Context() and aGlobal.Get() are not
// same-compartment is when we're called via Xrays, and in that situation we
// in fact want to create the array in the callee compartment
@@ -864,7 +864,7 @@ NS_INTERFACE_MAP_END_INHERITING(PromiseNativeHandler)
NS_IMPL_CYCLE_COLLECTION(AllResolveHandler, mCountdownHolder)
/* static */ already_AddRefed
-Promise::All(const GlobalObject& aGlobal, JSContext* aCx,
+Promise::All(const GlobalObject& aGlobal,
const Sequence& aIterable, ErrorResult& aRv)
{
nsCOMPtr global =
@@ -874,21 +874,23 @@ Promise::All(const GlobalObject& aGlobal, JSContext* aCx,
return nullptr;
}
+ JSContext* cx = aGlobal.Context();
+
if (aIterable.Length() == 0) {
- JS::Rooted empty(aCx, JS_NewArrayObject(aCx, 0));
+ JS::Rooted empty(cx, JS_NewArrayObject(cx, 0));
if (!empty) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return nullptr;
}
- JS::Rooted value(aCx, JS::ObjectValue(*empty));
- return Promise::Resolve(aGlobal, aCx, value, aRv);
+ JS::Rooted value(cx, JS::ObjectValue(*empty));
+ return Promise::Resolve(aGlobal, value, aRv);
}
nsRefPtr promise = new Promise(global);
nsRefPtr holder =
new CountdownHolder(aGlobal, promise, aIterable.Length());
- JS::Rooted obj(aCx, JS::CurrentGlobalOrNull(aCx));
+ JS::Rooted obj(cx, JS::CurrentGlobalOrNull(cx));
if (!obj) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
@@ -897,8 +899,8 @@ Promise::All(const GlobalObject& aGlobal, JSContext* aCx,
nsRefPtr rejectCb = new RejectPromiseCallback(promise, obj);
for (uint32_t i = 0; i < aIterable.Length(); ++i) {
- JS::Rooted value(aCx, aIterable.ElementAt(i));
- nsRefPtr nextPromise = Promise::Resolve(aGlobal, aCx, value, aRv);
+ JS::Rooted value(cx, aIterable.ElementAt(i));
+ nsRefPtr nextPromise = Promise::Resolve(aGlobal, value, aRv);
MOZ_ASSERT(!aRv.Failed());
@@ -916,7 +918,7 @@ Promise::All(const GlobalObject& aGlobal, JSContext* aCx,
}
/* static */ already_AddRefed
-Promise::Race(const GlobalObject& aGlobal, JSContext* aCx,
+Promise::Race(const GlobalObject& aGlobal,
const Sequence& aIterable, ErrorResult& aRv)
{
nsCOMPtr global =
@@ -926,7 +928,9 @@ Promise::Race(const GlobalObject& aGlobal, JSContext* aCx,
return nullptr;
}
- JS::Rooted obj(aCx, JS::CurrentGlobalOrNull(aCx));
+ JSContext* cx = aGlobal.Context();
+
+ JS::Rooted obj(cx, JS::CurrentGlobalOrNull(cx));
if (!obj) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
@@ -940,8 +944,8 @@ Promise::Race(const GlobalObject& aGlobal, JSContext* aCx,
nsRefPtr rejectCb = new RejectPromiseCallback(promise, obj);
for (uint32_t i = 0; i < aIterable.Length(); ++i) {
- JS::Rooted value(aCx, aIterable.ElementAt(i));
- nsRefPtr nextPromise = Promise::Resolve(aGlobal, aCx, value, aRv);
+ JS::Rooted value(cx, aIterable.ElementAt(i));
+ nsRefPtr nextPromise = Promise::Resolve(aGlobal, value, aRv);
// According to spec, Resolve can throw, but our implementation never does.
// Well it does when window isn't passed on the main thread, but that is an
// implementation detail which should never be reached since we are checking
diff --git a/dom/promise/Promise.h b/dom/promise/Promise.h
index fb64c85f3e0e..60484c0b2426 100644
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -121,7 +121,7 @@ public:
ErrorResult& aRv);
static already_AddRefed
- Resolve(const GlobalObject& aGlobal, JSContext* aCx,
+ Resolve(const GlobalObject& aGlobal,
JS::Handle aValue, ErrorResult& aRv);
static already_AddRefed
@@ -129,7 +129,7 @@ public:
JS::Handle aValue, ErrorResult& aRv);
static already_AddRefed
- Reject(const GlobalObject& aGlobal, JSContext* aCx,
+ Reject(const GlobalObject& aGlobal,
JS::Handle aValue, ErrorResult& aRv);
static already_AddRefed
@@ -144,11 +144,11 @@ public:
Catch(JSContext* aCx, AnyCallback* aRejectCallback);
static already_AddRefed
- All(const GlobalObject& aGlobal, JSContext* aCx,
+ All(const GlobalObject& aGlobal,
const Sequence& aIterable, ErrorResult& aRv);
static already_AddRefed
- Race(const GlobalObject& aGlobal, JSContext* aCx,
+ Race(const GlobalObject& aGlobal,
const Sequence& aIterable, ErrorResult& aRv);
void AppendNativeHandler(PromiseNativeHandler* aRunnable);
diff --git a/dom/push/src/PushService.jsm b/dom/push/src/PushService.jsm
index ab7a5576b67c..3b3d90dae125 100644
--- a/dom/push/src/PushService.jsm
+++ b/dom/push/src/PushService.jsm
@@ -1502,15 +1502,11 @@ this.PushService = {
},
/**
- * Get mobile network information to decide if the client is capable of being
- * woken up by UDP (which currently just means having an mcc and mnc along
- * with an IP).
+ * Returns information about MCC-MNC and the IP of the current connection.
*/
- _getNetworkState: function(callback) {
- if (typeof callback !== 'function') {
- throw new Error("No callback method. Aborting push agent !");
- }
- debug("getNetworkState()");
+ _getNetworkInformation: function() {
+ debug("getNetworkInformation()");
+
try {
if (!prefs.get("udp.wakeupEnabled")) {
debug("UDP support disabled, we do not send any carrier info");
@@ -1534,16 +1530,11 @@ this.PushService = {
let prefixLengths = {};
nm.active.getAddresses(ips, prefixLengths);
- this._getMobileNetworkId(function(netid) {
- debug("Recovered netID = " + netid);
- callback({
- mcc: iccInfo.mcc,
- mnc: iccInfo.mnc,
- ip: ips.value[0],
- netid: netid
- });
- });
- return;
+ return {
+ mcc: iccInfo.mcc,
+ mnc: iccInfo.mnc,
+ ip: ips.value[0]
+ }
}
}
} catch (e) {
@@ -1551,11 +1542,40 @@ this.PushService = {
}
debug("Running on wifi");
- callback({
+ return {
mcc: 0,
mnc: 0,
ip: undefined
- });
+ };
+ },
+
+ /**
+ * Get mobile network information to decide if the client is capable of being
+ * woken up by UDP (which currently just means having an mcc and mnc along
+ * with an IP, and optionally a netid).
+ */
+ _getNetworkState: function(callback) {
+ debug("getNetworkState()");
+
+ if (typeof callback !== 'function') {
+ throw new Error("No callback method. Aborting push agent !");
+ }
+
+ var networkInfo = this._getNetworkInformation();
+
+ if (networkInfo.ip) {
+ this._getMobileNetworkId(function(netid) {
+ debug("Recovered netID = " + netid);
+ callback({
+ mcc: networkInfo.mcc,
+ mnc: networkInfo.mnc,
+ ip: networkInfo.ip,
+ netid: netid
+ });
+ });
+ } else {
+ callback(networkInfo);
+ }
},
// utility function used to add/remove observers in init() and shutdown()
diff --git a/dom/src/notification/Notification.cpp b/dom/src/notification/Notification.cpp
index aa426569246e..f4e01de20a8c 100644
--- a/dom/src/notification/Notification.cpp
+++ b/dom/src/notification/Notification.cpp
@@ -46,7 +46,7 @@ public:
{
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aPromise);
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
JSAutoCompartment ac(cx, mGlobal);
mNotifications = JS_NewArrayObject(cx, 0);
HoldData();
diff --git a/dom/wifi/WifiCertService.cpp b/dom/wifi/WifiCertService.cpp
index c1a23d963ccc..685f2d105d9f 100644
--- a/dom/wifi/WifiCertService.cpp
+++ b/dom/wifi/WifiCertService.cpp
@@ -3,6 +3,10 @@
* 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/. */
+// XXX: This must be done prior to including cert.h (directly or indirectly).
+// CERT_AddTempCertToPerm is exposed as __CERT_AddTempCertToPerm.
+#define CERT_AddTempCertToPerm __CERT_AddTempCertToPerm
+
#include "WifiCertService.h"
#include "mozilla/ClearOnShutdown.h"
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
index 03c9083f8f00..c65784f63b04 100644
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -2123,7 +2123,7 @@ RuntimeService::CreateSharedWorkerInternal(const GlobalObject& aGlobal,
nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(window);
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
WorkerPrivate::LoadInfo loadInfo;
nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL,
diff --git a/dom/workers/URL.cpp b/dom/workers/URL.cpp
index 0a9a3f741a7c..4e38dfffbb59 100644
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -475,7 +475,7 @@ URL*
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
URL& aBase, ErrorResult& aRv)
{
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
nsRefPtr runnable =
@@ -499,7 +499,7 @@ URL*
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
const nsAString& aBase, ErrorResult& aRv)
{
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
nsRefPtr runnable =
@@ -844,7 +844,7 @@ URL::CreateObjectURL(const GlobalObject& aGlobal, JSObject* aBlob,
const mozilla::dom::objectURLOptions& aOptions,
nsString& aResult, mozilla::ErrorResult& aRv)
{
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
nsCOMPtr blob = file::GetDOMBlobFromJSObject(aBlob);
@@ -878,7 +878,7 @@ URL::CreateObjectURL(const GlobalObject& aGlobal, JSObject& aBlob,
void
URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl)
{
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
nsRefPtr runnable =
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index cc17effe5c2f..0dfd43e89788 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3629,7 +3629,7 @@ WorkerPrivate::Constructor(const GlobalObject& aGlobal,
const nsACString& aSharedWorkerName,
LoadInfo* aLoadInfo, ErrorResult& aRv)
{
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
return Constructor(cx, aScriptURL, aIsChromeWorker, aWorkerType,
aSharedWorkerName, aLoadInfo, aRv);
}
@@ -5771,7 +5771,7 @@ WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial)
ErrorResult rv;
nsRefPtr event =
- MessageEvent::Constructor(globalObject, aCx,
+ MessageEvent::Constructor(globalObject,
NS_LITERAL_STRING("connect"), init, rv);
event->SetTrusted(true);
diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp
index d8b54e42f011..0fdb8fcd2c51 100644
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -1627,7 +1627,7 @@ XMLHttpRequest::Constructor(const GlobalObject& aGlobal,
const MozXMLHttpRequestParameters& aParams,
ErrorResult& aRv)
{
- JSContext* cx = aGlobal.GetContext();
+ JSContext* cx = aGlobal.Context();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
MOZ_ASSERT(workerPrivate);
diff --git a/dom/workers/XMLHttpRequest.h b/dom/workers/XMLHttpRequest.h
index e5ccfce0b501..bb8390dd22a8 100644
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -88,7 +88,7 @@ public:
{
// Pretend like someone passed null, so we can pick up the default values
MozXMLHttpRequestParameters params;
- if (!params.Init(aGlobal.GetContext(), JS::NullHandleValue)) {
+ if (!params.Init(aGlobal.Context(), JS::NullHandleValue)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
diff --git a/gfx/angle/README.mozilla b/gfx/angle/README.mozilla
index acb2338c19d6..717430bd692a 100644
--- a/gfx/angle/README.mozilla
+++ b/gfx/angle/README.mozilla
@@ -60,6 +60,9 @@ In this order:
angle-d3dcc47.patch:
Tell ANGLE about d3dcompiler_47.dll from WinSDK 8.1.
+ angle-fix-issue-651.patch:
+ Fixes crash in TSymbolTableLevel::~TSymbolTableLevel with GCC 4.9
+
In addition to these patches, the Makefile.in and moz.build build files are ours,
they're not present in upsteam ANGLE. Therefore, changes made to the build files
should not be stored in the local .patch files.
diff --git a/gfx/angle/angle-fix-issue-651.patch b/gfx/angle/angle-fix-issue-651.patch
new file mode 100644
index 000000000000..efd37adf663a
--- /dev/null
+++ b/gfx/angle/angle-fix-issue-651.patch
@@ -0,0 +1,27 @@
+Fix for https://code.google.com/p/angleproject/issues/detail?id=651
+
+See https://bugzilla.mozilla.org/show_bug.cgi?id=1025576 for details.
+
+diff --git a/gfx/angle/src/compiler/SymbolTable.cpp b/gfx/angle/src/compiler/SymbolTable.cpp
+--- a/gfx/angle/src/compiler/SymbolTable.cpp
++++ b/gfx/angle/src/compiler/SymbolTable.cpp
+@@ -166,17 +166,18 @@ TFunction::~TFunction()
+ }
+
+ //
+ // Symbol table levels are a map of pointers to symbols that have to be deleted.
+ //
+ TSymbolTableLevel::~TSymbolTableLevel()
+ {
+ for (tLevel::iterator it = level.begin(); it != level.end(); ++it)
+- delete (*it).second;
++ if ((*it).first == (*it).second->getMangledName())
++ delete (*it).second;
+ }
+
+ //
+ // Change all function entries in the table with the non-mangled name
+ // to be related to the provided built-in operation. This is a low
+ // performance operation, and only intended for symbol tables that
+ // live across a large number of compiles.
+ //
diff --git a/gfx/angle/src/compiler/SymbolTable.cpp b/gfx/angle/src/compiler/SymbolTable.cpp
index 51180aff663d..42e8998c5ee3 100644
--- a/gfx/angle/src/compiler/SymbolTable.cpp
+++ b/gfx/angle/src/compiler/SymbolTable.cpp
@@ -171,7 +171,8 @@ TFunction::~TFunction()
TSymbolTableLevel::~TSymbolTableLevel()
{
for (tLevel::iterator it = level.begin(); it != level.end(); ++it)
- delete (*it).second;
+ if ((*it).first == (*it).second->getMangledName())
+ delete (*it).second;
}
//
diff --git a/gfx/gl/GLTextureImage.cpp b/gfx/gl/GLTextureImage.cpp
index 721781c2e35c..368969b758b2 100644
--- a/gfx/gl/GLTextureImage.cpp
+++ b/gfx/gl/GLTextureImage.cpp
@@ -9,6 +9,7 @@
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "gfx2DGlue.h"
+#include "gfxImageSurface.h"
#include "ScopedGLHelpers.h"
#include "GLUploadHelpers.h"
diff --git a/gfx/layers/GrallocImages.cpp b/gfx/layers/GrallocImages.cpp
index a5a01c4b441f..d06cbc8889ee 100644
--- a/gfx/layers/GrallocImages.cpp
+++ b/gfx/layers/GrallocImages.cpp
@@ -220,6 +220,10 @@ GrallocImage::GetAsSourceSurface()
RefPtr surface
= gfx::Factory::CreateDataSourceSurface(GetSize(), gfx::SurfaceFormat::R5G6B5);
+ if (!surface) {
+ NS_WARNING("Failed to create SourceSurface.");
+ return nullptr;
+ }
uint32_t width = GetSize().width;
uint32_t height = GetSize().height;
diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp
index 13e59c5d29fc..b5ac61414dab 100644
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -575,6 +575,10 @@ PlanarYCbCrImage::GetAsSourceSurface()
}
RefPtr surface = gfx::Factory::CreateDataSourceSurface(size, format);
+ if (!surface) {
+ NS_WARNING("Failed to create SourceSurface.");
+ return nullptr;
+ }
gfx::ConvertYCbCrToRGB(mData, format, size, surface->GetData(), surface->Stride());
@@ -590,6 +594,10 @@ RemoteBitmapImage::GetAsSourceSurface()
? gfx::SurfaceFormat::B8G8R8X8
: gfx::SurfaceFormat::B8G8R8A8;
RefPtr newSurf = gfx::Factory::CreateDataSourceSurface(mSize, fmt);
+ if (!newSurf) {
+ NS_WARNING("Failed to create SourceSurface.");
+ return nullptr;
+ }
for (int y = 0; y < mSize.height; y++) {
memcpy(newSurf->GetData() + newSurf->Stride() * y,
diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp
index 0182999d4248..265212d6f6b1 100644
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -17,6 +17,7 @@
#include "gfxPlatform.h" // for gfxPlatform
#include "gfxUtils.h" // for gfxUtils, etc
#include "gfx2DGlue.h"
+#include "gfxImageSurface.h"
#include "mozilla/DebugOnly.h" // for DebugOnly
#include "mozilla/Telemetry.h" // for Accumulate
#include "mozilla/gfx/2D.h" // for DrawTarget
diff --git a/gfx/layers/YCbCrImageDataSerializer.cpp b/gfx/layers/YCbCrImageDataSerializer.cpp
index e16db18bae88..bd43e76b6a3a 100644
--- a/gfx/layers/YCbCrImageDataSerializer.cpp
+++ b/gfx/layers/YCbCrImageDataSerializer.cpp
@@ -276,6 +276,10 @@ YCbCrImageDataDeserializer::ToDataSourceSurface()
{
RefPtr result =
Factory::CreateDataSourceSurface(GetYSize(), gfx::SurfaceFormat::B8G8R8X8);
+ if (!result) {
+ NS_WARNING("Failed to create SourceSurface.");
+ return nullptr;
+ }
DataSourceSurface::MappedSurface map;
result->Map(DataSourceSurface::MapType::WRITE, &map);
diff --git a/gfx/layers/apz/util/ActiveElementManager.cpp b/gfx/layers/apz/util/ActiveElementManager.cpp
index 76a27e229c81..7cf5b970172b 100644
--- a/gfx/layers/apz/util/ActiveElementManager.cpp
+++ b/gfx/layers/apz/util/ActiveElementManager.cpp
@@ -8,11 +8,9 @@
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "inIDOMUtils.h"
-#include "nsIDOMDocument.h"
-#include "nsIDOMElement.h"
-#include "nsIDOMEventTarget.h"
#include "base/message_loop.h"
#include "base/task.h"
+#include "mozilla/dom/Element.h"
#define AEM_LOG(...)
// #define AEM_LOG(...) printf_stderr("AEM: " __VA_ARGS__)
@@ -40,7 +38,7 @@ ActiveElementManager::ActiveElementManager()
ActiveElementManager::~ActiveElementManager() {}
void
-ActiveElementManager::SetTargetElement(nsIDOMEventTarget* aTarget)
+ActiveElementManager::SetTargetElement(dom::EventTarget* aTarget)
{
if (mTarget) {
// Multiple fingers on screen (since HandleTouchEnd clears mTarget).
@@ -126,11 +124,12 @@ ActiveElementManager::HandleTouchEnd(bool aWasClick)
}
void
-ActiveElementManager::SetActive(nsIDOMElement* aTarget)
+ActiveElementManager::SetActive(dom::Element* aTarget)
{
AEM_LOG("Setting active %p\n", aTarget);
if (mDomUtils) {
- mDomUtils->SetContentState(aTarget, NS_EVENT_STATE_ACTIVE.GetInternalValue());
+ nsCOMPtr target = do_QueryInterface(aTarget);
+ mDomUtils->SetContentState(target, NS_EVENT_STATE_ACTIVE.GetInternalValue());
}
}
@@ -141,15 +140,10 @@ ActiveElementManager::ResetActive()
// Clear the :active flag from mTarget by setting it on the document root.
if (mTarget) {
- nsCOMPtr doc;
- mTarget->GetOwnerDocument(getter_AddRefs(doc));
- if (doc) {
- nsCOMPtr root;
- doc->GetDocumentElement(getter_AddRefs(root));
- if (root) {
- AEM_LOG("Found root %p, making active\n", root.get());
- SetActive(root);
- }
+ dom::Element* root = mTarget->OwnerDoc()->GetDocumentElement();
+ if (root) {
+ AEM_LOG("Found root %p, making active\n", root.get());
+ SetActive(root);
}
}
}
@@ -162,7 +156,7 @@ ActiveElementManager::ResetTouchBlockState()
}
void
-ActiveElementManager::SetActiveTask(nsIDOMElement* aTarget)
+ActiveElementManager::SetActiveTask(dom::Element* aTarget)
{
AEM_LOG("mSetActiveTask %p running\n", mSetActiveTask);
diff --git a/gfx/layers/apz/util/ActiveElementManager.h b/gfx/layers/apz/util/ActiveElementManager.h
index a67d7b3124b9..e703927d5bd8 100644
--- a/gfx/layers/apz/util/ActiveElementManager.h
+++ b/gfx/layers/apz/util/ActiveElementManager.h
@@ -10,11 +10,14 @@
#include "nsISupportsImpl.h"
class inIDOMUtils;
-class nsIDOMEventTarget;
-class nsIDOMElement;
class CancelableTask;
namespace mozilla {
+namespace dom {
+class Element;
+class EventTarget;
+}
+
namespace layers {
/**
@@ -35,7 +38,7 @@ public:
* HandleTouchStart().
* |aTarget| may be nullptr.
*/
- void SetTargetElement(nsIDOMEventTarget* aTarget);
+ void SetTargetElement(dom::EventTarget* aTarget);
/**
* Handle a touch-start event.
* @param aCanBePan whether the touch can be a pan
@@ -55,7 +58,7 @@ private:
/**
* The target of the first touch point in the current touch block.
*/
- nsCOMPtr mTarget;
+ nsCOMPtr mTarget;
/**
* Whether the current touch block can be a pan. Set in HandleTouchStart().
*/
@@ -73,10 +76,10 @@ private:
// Helpers
void TriggerElementActivation();
- void SetActive(nsIDOMElement* aTarget);
+ void SetActive(dom::Element* aTarget);
void ResetActive();
void ResetTouchBlockState();
- void SetActiveTask(nsIDOMElement* aTarget);
+ void SetActiveTask(dom::Element* aTarget);
void CancelTask();
};
diff --git a/gfx/layers/composite/TextRenderer.cpp b/gfx/layers/composite/TextRenderer.cpp
index 97553398e12c..3f7fd4ec911c 100644
--- a/gfx/layers/composite/TextRenderer.cpp
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -85,6 +85,9 @@ TextRenderer::RenderText(const string& aText, const IntPoint& aOrigin,
// Create a surface to draw our glyphs to.
RefPtr textSurf =
Factory::CreateDataSourceSurface(IntSize(maxWidth, numLines * sCellHeight), sTextureFormat);
+ if (!textSurf) {
+ return;
+ }
DataSourceSurface::MappedSurface map;
textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map);
@@ -144,6 +147,10 @@ TextRenderer::EnsureInitialized()
}
mGlyphBitmaps = Factory::CreateDataSourceSurface(IntSize(sTextureWidth, sTextureHeight), sTextureFormat);
+ if (!mGlyphBitmaps) {
+ NS_WARNING("Failed to create SourceSurface.");
+ return;
+ }
mGlyphBitmaps->Map(DataSourceSurface::MapType::READ_WRITE, &mMap);
diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp
index 7657bff023b2..b7828ee5e4d6 100644
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -684,7 +684,9 @@ CompositorParent::ForceComposeToTarget(DrawTarget* aTarget, const nsIntRect* aRe
bool
CompositorParent::CanComposite()
{
- return !(mPaused || !mLayerManager || !mLayerManager->GetRoot());
+ return mLayerManager &&
+ mLayerManager->GetRoot() &&
+ !mPaused;
}
// Go down the composite layer tree, setting properties to match their
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
index 6eb693edfcd1..f6e4b23b3946 100644
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1455,6 +1455,10 @@ CompositorOGL::CopyToTarget(DrawTarget* aTarget, const nsIntPoint& aTopLeft, con
RefPtr source =
Factory::CreateDataSourceSurface(rect.Size(), gfx::SurfaceFormat::B8G8R8A8);
+ if (!source) {
+ NS_WARNING("Failed to create SourceSurface.");
+ return;
+ }
ReadPixelsIntoDataSurface(mGLContext, source);
diff --git a/gfx/src/nsColor.h b/gfx/src/nsColor.h
index a93fabcba0be..ca6603fc32fa 100644
--- a/gfx/src/nsColor.h
+++ b/gfx/src/nsColor.h
@@ -29,6 +29,10 @@ typedef uint32_t nscolor;
#define NS_RGBA(_r,_g,_b,_a) \
((nscolor) (((_a) << 24) | ((_b)<<16) | ((_g)<<8) | (_r)))
+// Make a color out of a gfxRGBA.
+#define NS_RGBA_FROM_GFXRGBA(gfxColor) \
+ ((nscolor) (gfxColor.Packed()))
+
// Extract color components from nscolor
#define NS_GET_R(_rgba) ((uint8_t) ((_rgba) & 0xff))
#define NS_GET_G(_rgba) ((uint8_t) (((_rgba) >> 8) & 0xff))
diff --git a/image/public/imgIContainer.idl b/image/public/imgIContainer.idl
index 2ee0ce4a4aae..f8b7b4be9712 100644
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -7,12 +7,10 @@
#include "nsISupports.idl"
%{C++
-#include "gfxImageSurface.h"
#include "gfxContext.h"
#include "gfxMatrix.h"
#include "gfxRect.h"
#include "GraphicsFilter.h"
-#include "gfxASurface.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/RefPtr.h"
#include "nsRect.h"
@@ -41,9 +39,6 @@ class Orientation;
%}
-[ptr] native gfxImageSurface(gfxImageSurface);
-[ptr] native gfxASurface(gfxASurface);
-native gfxImageFormat(gfxASurface::gfxImageFormat);
[ptr] native gfxContext(gfxContext);
[ref] native gfxMatrix(gfxMatrix);
[ref] native gfxRect(gfxRect);
@@ -176,8 +171,7 @@ interface imgIContainer : nsISupports
/**
* Get a surface for the given frame. This may be a platform-native,
* optimized surface, so you cannot inspect its pixel data. If you
- * need that, use gfxASurface::GetAsReadableARGB32ImageSurface or
- * gfxASurface::CopyToARGB32ImageSurface.
+ * need that, use SourceSurface::GetDataSurface.
*
* @param aWhichFrame Frame specifier of the FRAME_* variety.
* @param aFlags Flags of the FLAG_* variety
diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp
index fb91a677e649..6d19e49ca270 100644
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -211,13 +211,16 @@ public:
bool success = false;
if (!dstLocked) {
+ // We need to hold a lock onto the RasterImage object itself so that
+ // it (and its associated imgFrames) aren't marked as discardable.
+ bool imgLocked = NS_SUCCEEDED(image->LockImage());
bool srcLocked = NS_SUCCEEDED(srcFrame->LockImageData());
srcSurface = srcFrame->GetSurface();
dstLocked = NS_SUCCEEDED(dstFrame->LockImageData());
dstSurface = dstFrame->GetSurface();
- success = srcLocked && dstLocked && srcSurface && dstSurface;
+ success = imgLocked && srcLocked && dstLocked && srcSurface && dstSurface;
if (success) {
srcData = srcFrame->GetImageData();
@@ -253,6 +256,7 @@ public:
if (DiscardingEnabled())
dstFrame->SetDiscardable();
success = NS_SUCCEEDED(dstFrame->UnlockImageData());
+ success = success && NS_SUCCEEDED(image->UnlockImage());
dstLocked = false;
srcData = nullptr;
@@ -346,9 +350,9 @@ public:
ScaleRequest* request = mScaleRequest;
if (!request->stopped) {
- request->done = mozilla::gfx::Scale(request->srcData, request->srcRect.width, request->srcRect.height, request->srcStride,
- request->dstData, request->dstSize.width, request->dstSize.height, request->dstStride,
- request->srcFormat);
+ request->done = gfx::Scale(request->srcData, request->srcRect.width, request->srcRect.height, request->srcStride,
+ request->dstData, request->dstSize.width, request->dstSize.height, request->dstStride,
+ request->srcFormat);
} else {
request->done = false;
}
@@ -535,7 +539,7 @@ RasterImage::Init(const char* aMimeType,
//******************************************************************************
// [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
NS_IMETHODIMP_(void)
-RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
+RasterImage::RequestRefresh(const TimeStamp& aTime)
{
if (HadRecentRefresh(aTime)) {
return;
@@ -1568,7 +1572,7 @@ RasterImage::ResetAnimation()
//******************************************************************************
// [notxpcom] void setAnimationStartTime ([const] in TimeStamp aTime);
NS_IMETHODIMP_(void)
-RasterImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
+RasterImage::SetAnimationStartTime(const TimeStamp& aTime)
{
if (mError || mAnimationMode == kDontAnimMode || mAnimating || !mAnim)
return;
@@ -3232,7 +3236,7 @@ RasterImage::DecodePool::DecodePool()
}
#endif
- nsCOMPtr obsSvc = mozilla::services::GetObserverService();
+ nsCOMPtr obsSvc = services::GetObserverService();
if (obsSvc) {
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
}
diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h
index a96d4b2ea620..bfbc8c94b947 100644
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -176,8 +176,8 @@ public:
/* The total number of frames in this image. */
uint32_t GetNumFrames() const;
- virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const;
- virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const;
+ virtual size_t HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const;
+ virtual size_t HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const;
virtual size_t NonHeapSizeOfDecoded() const;
virtual size_t OutOfProcessSizeOfDecoded() const;
@@ -497,7 +497,7 @@ private:
// mThreadPoolMutex protects mThreadPool. For all RasterImages R,
// R::mDecodingMonitor must be acquired before mThreadPoolMutex
// if both are acquired; the other order may cause deadlock.
- mozilla::Mutex mThreadPoolMutex;
+ Mutex mThreadPoolMutex;
nsCOMPtr mThreadPool;
};
@@ -577,7 +577,7 @@ private:
uint32_t GetCurrentImgFrameIndex() const;
size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
- mozilla::MallocSizeOf aMallocSizeOf) const;
+ MallocSizeOf aMallocSizeOf) const;
void EnsureAnimExists();
@@ -660,10 +660,10 @@ private: // data
int mRequestedSampleSize;
// Cached value for GetImageContainer.
- nsRefPtr mImageContainer;
+ nsRefPtr mImageContainer;
// If not cached in mImageContainer, this might have our image container
- WeakPtr mImageContainerCache;
+ WeakPtr mImageContainerCache;
#ifdef DEBUG
uint32_t mFramesNotified;
@@ -673,7 +673,7 @@ private: // data
// at once, and hence need to be locked by mDecodingMonitor.
// BEGIN LOCKED MEMBER VARIABLES
- mozilla::ReentrantMonitor mDecodingMonitor;
+ ReentrantMonitor mDecodingMonitor;
FallibleTArray mSourceData;
diff --git a/image/src/SurfaceCache.cpp b/image/src/SurfaceCache.cpp
index 93aa8eea2449..d89925b66779 100644
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -17,7 +17,6 @@
#include "mozilla/StaticPtr.h"
#include "nsIMemoryReporter.h"
#include "gfx2DGlue.h"
-#include "gfxASurface.h"
#include "gfxPattern.h" // Workaround for flaw in bug 921753 part 2.
#include "gfxDrawable.h"
#include "gfxPlatform.h"
diff --git a/image/src/imgFrame.cpp b/image/src/imgFrame.cpp
index 19e1745cc73b..72c786b358f3 100644
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -16,7 +16,6 @@
static bool gDisableOptimize = false;
-#include "cairo.h"
#include "GeckoProfiler.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
diff --git a/image/src/imgFrame.h b/image/src/imgFrame.h
index cd8a96cff0ee..cd88d69b6b28 100644
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -25,7 +25,7 @@ public:
imgFrame();
~imgFrame();
- nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, mozilla::gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
+ nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
nsresult Optimize();
bool Draw(gfxContext *aContext, GraphicsFilter aFilter,
@@ -36,7 +36,7 @@ public:
nsresult ImageUpdated(const nsIntRect &aUpdateRect);
nsIntRect GetRect() const;
- mozilla::gfx::SurfaceFormat GetFormat() const;
+ SurfaceFormat GetFormat() const;
bool GetNeedsBackground() const;
uint32_t GetImageBytesPerRow() const;
uint32_t GetImageDataLength() const;
diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h
index 5924d79c13dd..c58d62d029b3 100644
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -10,8 +10,6 @@
#include "mozilla/NullPtr.h"
#include "js/HeapAPI.h"
-#include "js/RootingAPI.h"
-#include "js/Value.h"
namespace js {
namespace gc {
@@ -432,47 +430,6 @@ class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoAssertOnGC
explicit AutoCheckCannotGC(JSRuntime *rt) : AutoAssertOnGC(rt) {}
};
-class JS_PUBLIC_API(ObjectPtr)
-{
- Heap value;
-
- public:
- ObjectPtr() : value(nullptr) {}
-
- explicit ObjectPtr(JSObject *obj) : value(obj) {}
-
- /* Always call finalize before the destructor. */
- ~ObjectPtr() { MOZ_ASSERT(!value); }
-
- void finalize(JSRuntime *rt) {
- if (IsIncrementalBarrierNeeded(rt))
- IncrementalObjectBarrier(value);
- value = nullptr;
- }
-
- void init(JSObject *obj) { value = obj; }
-
- JSObject *get() const { return value; }
-
- void writeBarrierPre(JSRuntime *rt) {
- IncrementalObjectBarrier(value);
- }
-
- bool isAboutToBeFinalized();
-
- ObjectPtr &operator=(JSObject *obj) {
- IncrementalObjectBarrier(value);
- value = obj;
- return *this;
- }
-
- void trace(JSTracer *trc, const char *name);
-
- JSObject &operator*() const { return *value; }
- JSObject *operator->() const { return value; }
- operator JSObject *() const { return value; }
-};
-
/*
* Unsets the gray bit for anything reachable from |thing|. |kind| should not be
* JSTRACE_SHAPE. |thing| should be non-null.
@@ -507,13 +464,6 @@ ExposeGCThingToActiveJS(void *thing, JSGCTraceKind kind)
UnmarkGrayGCThingRecursively(thing, kind);
}
-static MOZ_ALWAYS_INLINE void
-ExposeValueToActiveJS(const Value &v)
-{
- if (v.isMarkable())
- ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind());
-}
-
static MOZ_ALWAYS_INLINE void
ExposeObjectToActiveJS(JSObject *obj)
{
diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h
index 383e4e790f63..e2f5977f6b81 100644
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -349,7 +349,6 @@ struct RuntimeSizes
macro(_, _, contexts) \
macro(_, _, dtoa) \
macro(_, _, temporary) \
- macro(_, _, regexpData) \
macro(_, _, interpreterStack) \
macro(_, _, mathCache) \
macro(_, _, sourceDataCache) \
diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h
index 47396394ba0f..b392848ee98a 100644
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -15,6 +15,7 @@
#include "jspubtd.h"
+#include "js/GCAPI.h"
#include "js/HeapAPI.h"
#include "js/TypeDecls.h"
#include "js/Utility.h"
@@ -1165,6 +1166,47 @@ class PersistentRooted : private mozilla::LinkedListElement
T ptr;
};
+class JS_PUBLIC_API(ObjectPtr)
+{
+ Heap value;
+
+ public:
+ ObjectPtr() : value(nullptr) {}
+
+ explicit ObjectPtr(JSObject *obj) : value(obj) {}
+
+ /* Always call finalize before the destructor. */
+ ~ObjectPtr() { MOZ_ASSERT(!value); }
+
+ void finalize(JSRuntime *rt) {
+ if (IsIncrementalBarrierNeeded(rt))
+ IncrementalObjectBarrier(value);
+ value = nullptr;
+ }
+
+ void init(JSObject *obj) { value = obj; }
+
+ JSObject *get() const { return value; }
+
+ void writeBarrierPre(JSRuntime *rt) {
+ IncrementalObjectBarrier(value);
+ }
+
+ bool isAboutToBeFinalized();
+
+ ObjectPtr &operator=(JSObject *obj) {
+ IncrementalObjectBarrier(value);
+ value = obj;
+ return *this;
+ }
+
+ void trace(JSTracer *trc, const char *name);
+
+ JSObject &operator*() const { return *value; }
+ JSObject *operator->() const { return value; }
+ operator JSObject *() const { return value; }
+};
+
} /* namespace JS */
namespace js {
diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h
new file mode 100644
index 000000000000..d2acc2e24289
--- /dev/null
+++ b/js/public/UbiNode.h
@@ -0,0 +1,462 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 js_UbiNode_h
+#define js_UbiNode_h
+
+#include "mozilla/Alignment.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+
+#include "jspubtd.h"
+
+#include "js/GCAPI.h"
+#include "js/HashTable.h"
+#include "js/TypeDecls.h"
+
+// JS::ubi::Node
+//
+// JS::ubi::Node is a pointer-like type designed for internal use by heap
+// analysis tools. A ubi::Node can refer to:
+//
+// - a JS value, like a string or object;
+// - an internal SpiderMonkey structure, like a shape or a scope chain object
+// - an instance of some embedding-provided type: in Firefox, an XPCOM
+// object, or an internal DOM node class instance
+//
+// A ubi::Node instance provides metadata about its referent, and can
+// enumerate its referent's outgoing edges, so you can implement heap analysis
+// algorithms that walk the graph - finding paths between objects, or
+// computing heap dominator trees, say - using ubi::Node, while remaining
+// ignorant of the details of the types you're operating on.
+//
+// Of course, when it comes to presenting the results in a developer-facing
+// tool, you'll need to stop being ignorant of those details, because you have
+// to discuss the ubi::Nodes' referents with the developer. Here, ubi::Node
+// can hand you dynamically checked, properly typed pointers to the original
+// objects via the as method, or generate descriptions of the referent
+// itself.
+//
+// ubi::Node instances are lightweight (two-word) value types. Instances:
+// - compare equal if and only if they refer to the same object;
+// - have hash values that respect their equality relation; and
+// - have serializations that are only equal if the ubi::Nodes are equal.
+//
+// A ubi::Node is only valid for as long as its referent is alive; if its
+// referent goes away, the ubi::Node becomes a dangling pointer. A ubi::Node
+// that refers to a GC-managed object is not automatically a GC root; if the
+// GC frees or relocates its referent, the ubi::Node becomes invalid. A
+// ubi::Node that refers to a reference-counted object does not bump the
+// reference count.
+//
+// ubi::Node values require no supporting data structures, making them
+// feasible for use in memory-constrained devices --- ideally, the memory
+// requirements of the algorithm which uses them will be the limiting factor,
+// not the demands of ubi::Node itself.
+//
+// One can construct a ubi::Node value given a pointer to a type that ubi::Node
+// supports. In the other direction, one can convert a ubi::Node back to a
+// pointer; these downcasts are checked dynamically. In particular, one can
+// convert a 'JSRuntime *' to a ubi::Node, yielding a node with an outgoing edge
+// for every root registered with the runtime; starting from this, one can walk
+// the entire heap. (Of course, one could also start traversal at any other kind
+// of type to which one has a pointer.)
+//
+//
+// Extending ubi::Node To Handle Your Embedding's Types
+//
+// To add support for a new ubi::Node referent type R, you must define a
+// specialization of the ubi::Concrete template, ubi::Concrete, which
+// inherits from ubi::Base. ubi::Node itself uses the specialization for
+// compile-time information (i.e. the checked conversions between R * and
+// ubi::Node), and the inheritance for run-time dispatching.
+//
+//
+// ubi::Node Exposes Implementation Details
+//
+// In many cases, a JavaScript developer's view of their data differs
+// substantially from its actual implementation. For example, while the
+// ECMAScript specification describes objects as maps from property names to
+// sets of attributes (like ECMAScript's [[Value]]), in practice many objects
+// have only a pointer to a shape, shared with other similar objects, and
+// indexed slots that contain the [[Value]] attributes. As another example, a
+// string produced by concatenating two other strings may sometimes be
+// represented by a "rope", a structure that points to the two original
+// strings.
+//
+
+// We intend to use ubi::Node to write tools that report memory usage, so it's
+// important that ubi::Node accurately portray how much memory nodes consume.
+// Thus, for example, when data that apparently belongs to multiple nodes is
+// in fact shared in a common structure, ubi::Node's graph uses a separate
+// node for that shared structure, and presents edges to it from the data's
+// apparent owners. For example, ubi::Node exposes SpiderMonkey objects'
+// shapes and base shapes, and exposes rope string and substring structure,
+// because these optimizations become visible when a tool reports how much
+// memory a structure consumes.
+//
+// However, fine granularity is not a goal. When a particular object is the
+// exclusive owner of a separate block of memory, ubi::Node may present the
+// object and its block as a single node, and add their sizes together when
+// reporting the node's size, as there is no meaningful loss of data in this
+// case. Thus, for example, a ubi::Node referring to a JavaScript object, when
+// asked for the object's size in bytes, includes the object's slot and
+// element arrays' sizes in the total. There is no separate ubi::Node value
+// representing the slot and element arrays, since they are owned exclusively
+// by the object.
+//
+//
+// Presenting Analysis Results To JavaScript Developers
+//
+// If an analysis provides its results in terms of ubi::Node values, a user
+// interface presenting those results will generally need to clean them up
+// before they can be understood by JavaScript developers. For example,
+// JavaScript developers should not need to understand shapes, only JavaScript
+// objects. Similarly, they should not need to understand the distinction
+// between DOM nodes and the JavaScript shadow objects that represent them.
+//
+//
+// Rooting Restrictions
+//
+// At present there is no way to root ubi::Node instances, so instances can't be
+// live across any operation that might GC. Analyses using ubi::Node must either
+// run to completion and convert their results to some other rootable type, or
+// save their intermediate state in some rooted structure if they must GC before
+// they complete. (For algorithms like path-finding and dominator tree
+// computation, we implement the algorithm avoiding any operation that could
+// cause a GC --- and use AutoCheckCannotGC to verify this.)
+//
+// If this restriction prevents us from implementing interesting tools, we may
+// teach the GC how to root ubi::Nodes, fix up hash tables that use them as
+// keys, etc.
+
+
+// Forward declarations of SpiderMonkey's ubi::Node reference types.
+namespace js {
+class LazyScript;
+class Shape;
+class BaseShape;
+namespace jit {
+class JitCode;
+}
+namespace types {
+struct TypeObject;
+}
+}
+
+
+namespace JS {
+namespace ubi {
+
+class Edge;
+class EdgeRange;
+
+// The base class implemented by each ubi::Node referent type. Subclasses must
+// not add data members to this class.
+class Base {
+ friend class Node;
+
+ // For performance's sake, we'd prefer to avoid a virtual destructor; and
+ // an empty constructor seems consistent with the 'lightweight value type'
+ // visible behavior we're trying to achieve. But if the destructor isn't
+ // virtual, and a subclass overrides it, the subclass's destructor will be
+ // ignored. Is there a way to make the compiler catch that error?
+
+ protected:
+ // Space for the actual pointer. Concrete subclasses should define a
+ // properly typed 'get' member function to access this.
+ void *ptr;
+
+ Base(void *ptr) : ptr(ptr) { }
+
+ public:
+ bool operator==(const Base &rhs) const {
+ // Some compilers will indeed place objects of different types at
+ // the same address, so technically, we should include the vtable
+ // in this comparison. But it seems unlikely to cause problems in
+ // practice.
+ return ptr == rhs.ptr;
+ }
+ bool operator!=(const Base &rhs) const { return !(*this == rhs); }
+
+ // Return a human-readable name for the referent's type. The result should
+ // be statically allocated. (You can use MOZ_UTF16("strings") for this.)
+ //
+ // This must always return Concrete::concreteTypeName; we use that
+ // pointer as a tag for this particular referent type.
+ virtual const jschar *typeName() const = 0;
+
+ // Return the size of this node, in bytes. Include any structures that this
+ // node owns exclusively that are not exposed as their own ubi::Nodes.
+ virtual size_t size() const = 0;
+
+ // Return an EdgeRange that initially contains all the referent's outgoing
+ // edges. The EdgeRange should be freed with 'js_delete'. (You could use
+ // ScopedDJSeletePtr to manage it.) On OOM, report an exception
+ // on |cx| and return nullptr.
+ virtual EdgeRange *edges(JSContext *cx) const = 0;
+
+ private:
+ Base(const Base &rhs) MOZ_DELETE;
+ Base &operator=(const Base &rhs) MOZ_DELETE;
+};
+
+// A traits template with a specialization for each referent type that
+// ubi::Node supports. The specialization must be the concrete subclass of
+// Base that represents a pointer to the referent type. It must also
+// include the members described here.
+template
+struct Concrete {
+ // The specific jschar array returned by Concrete::typeName.
+ static const jschar concreteTypeName[];
+
+ // Construct an instance of this concrete class in |storage| referring
+ // to |referent|. Implementations typically use a placement 'new'.
+ //
+ // In some cases, |referent| will contain dynamic type information that
+ // identifies it a some more specific subclass of |Referent|. For example,
+ // when |Referent| is |JSObject|, then |referent->getClass()| could tell us
+ // that it's actually a JSFunction. Similarly, if |Referent| is
+ // |nsISupports|, we would like a ubi::Node that knows its final
+ // implementation type.
+ //
+ // So, we delegate the actual construction to this specialization, which
+ // knows Referent's details.
+ static void construct(void *storage, Referent *referent);
+};
+
+// A container for a Base instance; all members simply forward to the contained instance.
+// This container allows us to pass ubi::Node instances by value.
+class Node {
+ // Storage in which we allocate Base subclasses.
+ mozilla::AlignedStorage2 storage;
+ Base *base() { return storage.addr(); }
+ const Base *base() const { return storage.addr(); }
+
+ template
+ void construct(T *ptr) {
+ static_assert(sizeof(Concrete) == sizeof(*base()),
+ "ubi::Base specializations must be the same size as ubi::Base");
+ Concrete::construct(base(), ptr);
+ }
+
+ typedef void (Node::* ConvertibleToBool)();
+ void nonNull() {}
+
+ public:
+ Node() { construct(nullptr); }
+
+ template
+ Node(T *ptr) {
+ construct(ptr);
+ }
+ template
+ Node &operator=(T *ptr) {
+ construct(ptr);
+ return *this;
+ }
+
+ // We can construct and assign from rooted forms of pointers.
+ template
+ Node(const Rooted &root) {
+ construct(root.get());
+ }
+ template
+ Node &operator=(const Rooted &root) {
+ construct(root.get());
+ return *this;
+ }
+
+ // Constructors accepting SpiderMonkey's other generic-pointer-ish types.
+ Node(JS::Value value);
+ Node(JSGCTraceKind kind, void *ptr);
+
+ // copy construction and copy assignment just use memcpy, since we know
+ // instances contain nothing but a vtable pointer and a data pointer.
+ //
+ // To be completely correct, concrete classes could provide a virtual
+ // 'construct' member function, which we could invoke on rhs to construct an
+ // instance in our storage. But this is good enough; there's no need to jump
+ // through vtables for copying and assignment that are just going to move
+ // two words around. The compiler knows how to optimize memcpy.
+ Node(const Node &rhs) {
+ memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
+ }
+
+ Node &operator=(const Node &rhs) {
+ memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
+ return *this;
+ }
+
+ bool operator==(const Node &rhs) const { return *base() == *rhs.base(); }
+ bool operator!=(const Node &rhs) const { return *base() != *rhs.base(); }
+
+ operator ConvertibleToBool() const {
+ return base()->ptr ? &Node::nonNull : 0;
+ }
+
+ template
+ bool is() const {
+ return base()->typeName() == Concrete::concreteTypeName;
+ }
+
+ template
+ T *as() const {
+ MOZ_ASSERT(is());
+ return static_cast(base()->ptr);
+ }
+
+ template
+ T *asOrNull() const {
+ return is() ? static_cast(base()->ptr) : nullptr;
+ }
+
+ // If this node refers to something that can be represented as a
+ // JavaScript value that is safe to expose to JavaScript code, return that
+ // value. Otherwise return UndefinedValue(). JSStrings and some (but not
+ // all!) JSObjects can be exposed.
+ JS::Value exposeToJS() const;
+
+ const jschar *typeName() const { return base()->typeName(); }
+ size_t size() const { return base()->size(); }
+ EdgeRange *edges(JSContext *cx) const { return base()->edges(cx); }
+
+ // A hash policy for ubi::Nodes.
+ // This simply uses the stock PointerHasher on the ubi::Node's pointer.
+ // We specialize DefaultHasher below to make this the default.
+ class HashPolicy {
+ typedef js::PointerHasher::value> PtrHash;
+
+ public:
+ typedef Node Lookup;
+
+ static js::HashNumber hash(const Lookup &l) { return PtrHash::hash(l.base()->ptr); }
+ static bool match(const Node &k, const Lookup &l) { return k == l; }
+ static void rekey(Node &k, const Node &newKey) { k = newKey; }
+ };
+};
+
+
+// Edge is the abstract base class representing an outgoing edge of a node.
+// Edges are owned by EdgeRanges, and need not have assignment operators or copy
+// constructors.
+//
+// Each Edge class should inherit from this base class, overriding as
+// appropriate.
+class Edge {
+ protected:
+ Edge() : name(nullptr), referent() { }
+ virtual ~Edge() { }
+
+ public:
+ // This edge's name.
+ //
+ // The storage is owned by this Edge, and will be freed when this Edge is
+ // destructed.
+ //
+ // (In real life we'll want a better representation for names, to avoid
+ // creating tons of strings when the names follow a pattern; and we'll need
+ // to think about lifetimes carefully to ensure traversal stays cheap.)
+ const jschar *name;
+
+ // This edge's referent.
+ Node referent;
+
+ private:
+ Edge(const Edge &) MOZ_DELETE;
+ Edge &operator=(const Edge &) MOZ_DELETE;
+};
+
+
+// EdgeRange is an abstract base class for iterating over a node's outgoing
+// edges. (This is modeled after js::HashTable::Range.)
+//
+// Concrete instances of this class need not be as lightweight as Node itself,
+// since they're usually only instantiated while iterating over a particular
+// object's edges. For example, a dumb implementation for JS Cells might use
+// JS_TraceChildren to to get the outgoing edges, and then store them in an
+// array internal to the EdgeRange.
+class EdgeRange {
+ protected:
+ // The current front edge of this range, or nullptr if this range is empty.
+ Edge *front_;
+
+ EdgeRange() : front_(nullptr) { }
+
+ public:
+ virtual ~EdgeRange() { };
+
+ // True if there are no more edges in this range.
+ bool empty() const { return !front_; }
+
+ // The front edge of this range. This is owned by the EdgeRange, and is
+ // only guaranteed to live until the next call to popFront, or until
+ // the EdgeRange is destructed.
+ const Edge &front() { return *front_; }
+
+ // Remove the front edge from this range. This should only be called if
+ // !empty().
+ virtual void popFront() = 0;
+
+ private:
+ EdgeRange(const EdgeRange &) MOZ_DELETE;
+ EdgeRange &operator=(const EdgeRange &) MOZ_DELETE;
+};
+
+
+// Concrete classes for ubi::Node referent types.
+
+// A reusable ubi::Concrete specialization base class for types supported by
+// JS_TraceChildren.
+template
+class TracerConcrete : public Base {
+ const jschar *typeName() const MOZ_OVERRIDE { return concreteTypeName; }
+ size_t size() const MOZ_OVERRIDE { return 0; } // not implemented yet; bug 1011300
+ EdgeRange *edges(JSContext *) const MOZ_OVERRIDE;
+
+ TracerConcrete(Referent *ptr) : Base(ptr) { }
+
+ public:
+ static const jschar concreteTypeName[];
+ static void construct(void *storage, Referent *ptr) { new (storage) TracerConcrete(ptr); };
+};
+
+template<> struct Concrete : TracerConcrete { };
+template<> struct Concrete : TracerConcrete { };
+template<> struct Concrete : TracerConcrete { };
+template<> struct Concrete : TracerConcrete { };
+template<> struct Concrete : TracerConcrete { };
+template<> struct Concrete : TracerConcrete { };
+template<> struct Concrete : TracerConcrete { };
+template<> struct Concrete : TracerConcrete { };
+
+// The ubi::Node null pointer. Any attempt to operate on a null ubi::Node asserts.
+template<>
+class Concrete : public Base {
+ const jschar *typeName() const MOZ_OVERRIDE;
+ size_t size() const MOZ_OVERRIDE;
+ EdgeRange *edges(JSContext *cx) const MOZ_OVERRIDE;
+
+ Concrete(void *ptr) : Base(ptr) { }
+
+ public:
+ static void construct(void *storage, void *ptr) { new (storage) Concrete(ptr); }
+ static const jschar concreteTypeName[];
+};
+
+
+} // namespace ubi
+} // namespace JS
+
+namespace js {
+
+// Make ubi::Node::HashPolicy the default hash policy for ubi::Node.
+template<> struct DefaultHasher : JS::ubi::Node::HashPolicy { };
+
+} // namespace js
+
+#endif // js_UbiNode_h
diff --git a/js/public/UbiNodeTraverse.h b/js/public/UbiNodeTraverse.h
new file mode 100644
index 000000000000..6072cbc489c2
--- /dev/null
+++ b/js/public/UbiNodeTraverse.h
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 js_UbiNodeTraverse_h
+#define js_UbiNodeTraverse_h
+
+#include "js/UbiNode.h"
+#include "js/Utility.h"
+#include "js/Vector.h"
+
+namespace JS {
+namespace ubi {
+
+// A breadth-first traversal template for graphs of ubi::Nodes.
+//
+// No GC may occur while an instance of this template is live.
+//
+// The provided Handler type should have two members:
+//
+// typename NodeData;
+//
+// The value type of |BreadthFirst::visited|, the HashMap of
+// ubi::Nodes that have been visited so far. Since the algorithm needs a
+// hash table like this for its own use anyway, it is simple to let
+// Handler store its own metadata about each node in the same table.
+//
+// For example, if you want to find a shortest path to each node from any
+// traversal starting point, your |NodeData| type could record the first
+// edge to reach each node, and the node from which it originates. Then,
+// when the traversal is complete, you can walk backwards from any node
+// to some starting point, and the path recorded will be a shortest path.
+//
+// This type must have a default constructor. If this type owns any other
+// resources, move constructors and assignment operators are probably a
+// good idea, too.
+//
+// bool operator() (BreadthFirst &traversal,
+// Node origin, const Edge &edge,
+// Handler::NodeData *referentData, bool first);
+//
+// The visitor function, called to report that we have traversed
+// |edge| from |origin|. This is called once for each edge we traverse.
+// As this is a breadth-first search, any prior calls to the visitor function
+// were for origin nodes not further from the start nodes than |origin|.
+//
+// |traversal| is this traversal object, passed along for convenience.
+//
+// |referentData| is a pointer to the value of the entry in
+// |traversal.visited| for |edge.referent|; the visitor function can
+// store whatever metadata it likes about |edge.referent| there.
+//
+// |first| is true if this is the first time we have visited an edge
+// leading to |edge.referent|. This could be stored in NodeData, but
+// the algorithm knows whether it has just created the entry in
+// |traversal.visited|, so it passes it along for convenience.
+//
+// The visitor function may call |traversal.stop()| if it doesn't want
+// to visit any more nodes.
+//
+// The visitor function may consult |traversal.visited| for information
+// about other nodes, but it should not add or remove entries.
+//
+// The visitor function should return true on success, or false if an
+// error occurs. A false return value terminates the traversal
+// immediately, and causes BreadthFirst::traverse to return
+// false.
+template
+struct BreadthFirst {
+
+ // Construct a breadth-first traversal object that reports the nodes it
+ // reaches to |handler|. The traversal object reports OOM on |cx|, and
+ // asserts that no GC happens in |cx|'s runtime during its lifetime.
+ //
+ // We do nothing with noGC, other than require it to exist, with a lifetime
+ // that encloses our own.
+ BreadthFirst(JSContext *cx, Handler &handler, const JS::AutoCheckCannotGC &noGC)
+ : cx(cx), visited(cx), handler(handler), pending(cx),
+ traversalBegun(false), stopRequested(false)
+ { }
+
+ // Initialize this traversal object. Return false on OOM.
+ bool init() { return visited.init(); }
+
+ // Add |node| as a starting point for the traversal. You may add
+ // as many starting points as you like. Return false on OOM.
+ bool addStart(Node node) { return pending.append(node); }
+
+ // Traverse the graph in breadth-first order, starting at the given
+ // start nodes, applying |handler::operator()| for each edge traversed
+ // as described above.
+ //
+ // This should be called only once per instance of this class.
+ //
+ // Return false on OOM or error return from |handler::operator()|.
+ bool traverse()
+ {
+ MOZ_ASSERT(!traversalBegun);
+ traversalBegun = true;
+
+ // While there are pending nodes, visit them, until we've found a path to the target.
+ while (!pending.empty()) {
+ Node origin = pending.front();
+ pending.popFront();
+
+ // Get a range containing all origin's outgoing edges.
+ js::ScopedJSDeletePtr range(origin.edges(cx));
+ if (!range)
+ return false;
+
+ // Traverse each edge.
+ for (; !range->empty(); range->popFront()) {
+ MOZ_ASSERT(!stopRequested);
+
+ const Edge &edge = range->front();
+ typename NodeMap::AddPtr a = visited.lookupForAdd(edge.referent);
+ bool first = !a;
+
+ if (first) {
+ // This is the first time we've reached |edge.referent|.
+ // Create an entry for it in |visited|, and arrange to
+ // traverse its outgoing edges later.
+ if (!visited.add(a, edge.referent, typename Handler::NodeData()) ||
+ !pending.append(edge.referent)) {
+ return false;
+ }
+ }
+
+ MOZ_ASSERT(a);
+
+ // Report this edge to the visitor function.
+ if (!handler(*this, origin, edge, &a->value(), first))
+ return false;
+
+ if (stopRequested)
+ return true;
+ }
+ }
+
+ return true;
+ }
+
+ // Stop traversal, and return true from |traverse| without visiting any
+ // more nodes. Only |handler::operator()| should call this function; it
+ // may do so to stop the traversal early, without returning false and
+ // then making |traverse|'s caller disambiguate that result from a real
+ // error.
+ void stop() { stopRequested = true; }
+
+ // The context with which we were constructed.
+ JSContext *cx;
+
+ // A map associating each node N that we have reached with a
+ // Handler::NodeData, for |handler|'s use. This is public, so that
+ // |handler| can access it to see the traversal thus far.
+ typedef js::HashMap NodeMap;
+ NodeMap visited;
+
+ private:
+ // Our handler object.
+ Handler &handler;
+
+ // A queue template. Appending and popping the front are constant time.
+ // Wasted space is never more than some recent actual population plus the
+ // current population.
+ template
+ class Queue {
+ js::Vector head, tail;
+ size_t frontIndex;
+ public:
+ Queue(JSContext *cx) : head(cx), tail(cx), frontIndex(0) { }
+ bool empty() { return frontIndex >= head.length(); }
+ T &front() {
+ MOZ_ASSERT(!empty());
+ return head[frontIndex];
+ }
+ void popFront() {
+ MOZ_ASSERT(!empty());
+ frontIndex++;
+ if (frontIndex >= head.length()) {
+ head.clearAndFree();
+ head.swap(tail);
+ frontIndex = 0;
+ }
+ }
+ bool append(const T &elt) {
+ return frontIndex == 0 ? head.append(elt) : tail.append(elt);
+ }
+ };
+
+ // A queue of nodes that we have reached, but whose outgoing edges we
+ // have not yet traversed. Nodes reachable in fewer edges are enqueued
+ // earlier.
+ Queue pending;
+
+ // True if our traverse function has been called.
+ bool traversalBegun;
+
+ // True if we've been asked to stop the traversal.
+ bool stopRequested;
+};
+
+} // namespace ubi
+} // namespace JS
+
+#endif // js_UbiNodeTraverse.h
diff --git a/js/public/Value.h b/js/public/Value.h
index 1952f7951141..7b0fa0914e07 100644
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -19,6 +19,7 @@
#include "jstypes.h"
#include "js/Anchor.h"
+#include "js/GCAPI.h"
#include "js/RootingAPI.h"
#include "js/Utility.h"
@@ -1288,6 +1289,13 @@ IsOptimizedPlaceholderMagicValue(const Value &v)
return false;
}
+static MOZ_ALWAYS_INLINE void
+ExposeValueToActiveJS(const Value &v)
+{
+ if (v.isMarkable())
+ ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind());
+}
+
/************************************************************************/
static inline Value
diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp
index 021d43a2b700..1b7296c02daa 100644
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -10,10 +10,7 @@
#include "jscntxt.h"
-#ifndef JS_YARR
#include "irregexp/RegExpParser.h"
-#endif
-
#include "vm/RegExpStatics.h"
#include "vm/StringBuffer.h"
@@ -94,28 +91,12 @@ js::CreateRegExpMatchResult(JSContext *cx, HandleString input, const MatchPairs
static RegExpRunStatus
ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re,
HandleLinearString input, const jschar *chars, size_t length,
- size_t *lastIndex, MatchConduit &matches)
+ size_t *lastIndex, MatchPairs &matches)
{
- RegExpRunStatus status;
-
- /* Switch between MatchOnly and IncludeSubpatterns modes. */
- if (matches.isPair) {
-#ifdef JS_YARR
- size_t lastIndex_orig = *lastIndex;
- /* Only one MatchPair slot provided: execute short-circuiting regexp. */
- status = re.executeMatchOnly(cx, chars, length, lastIndex, *matches.u.pair);
- if (status == RegExpRunStatus_Success && res)
- res->updateLazily(cx, input, &re, lastIndex_orig);
-#else
- MOZ_CRASH();
-#endif
- } else {
- /* Vector of MatchPairs provided: execute full regexp. */
- status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs);
- if (status == RegExpRunStatus_Success && res) {
- if (!res->updateFromMatchPairs(cx, input, *matches.u.pairs))
- return RegExpRunStatus_Error;
- }
+ RegExpRunStatus status = re.execute(cx, chars, length, lastIndex, matches);
+ if (status == RegExpRunStatus_Success && res) {
+ if (!res->updateFromMatchPairs(cx, input, matches))
+ return RegExpRunStatus_Error;
}
return status;
}
@@ -131,10 +112,9 @@ js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
return false;
ScopedMatchPairs matches(&cx->tempLifoAlloc());
- MatchConduit conduit(&matches);
RegExpRunStatus status =
- ExecuteRegExpImpl(cx, res, *shared, input_, chars, length, lastIndex, conduit);
+ ExecuteRegExpImpl(cx, res, *shared, input_, chars, length, lastIndex, matches);
if (status == RegExpRunStatus_Error)
return false;
@@ -308,16 +288,11 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
if (!escapedSourceStr)
return false;
-#ifdef JS_YARR
- if (!RegExpShared::checkSyntax(cx, nullptr, escapedSourceStr))
- return false;
-#else // JS_YARR
CompileOptions options(cx);
frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), escapedSourceStr))
return false;
-#endif // JS_YARR
RegExpStatics *res = cx->global()->getRegExpStatics(cx);
if (!res)
@@ -557,7 +532,7 @@ js_InitRegExpClass(JSContext *cx, HandleObject obj)
RegExpRunStatus
js::ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string,
- MatchConduit &matches, RegExpStaticsUpdate staticsUpdate)
+ MatchPairs &matches, RegExpStaticsUpdate staticsUpdate)
{
/* Step 1 (b) was performed by CallNonGenericMethod. */
Rooted reobj(cx, ®exp->as());
@@ -633,7 +608,7 @@ js::ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string,
/* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */
static RegExpRunStatus
-ExecuteRegExp(JSContext *cx, CallArgs args, MatchConduit &matches)
+ExecuteRegExp(JSContext *cx, CallArgs args, MatchPairs &matches)
{
/* Step 1 (a) was performed by CallNonGenericMethod. */
RootedObject regexp(cx, &args.thisv().toObject());
@@ -653,9 +628,8 @@ regexp_exec_impl(JSContext *cx, HandleObject regexp, HandleString string,
{
/* Execute regular expression and gather matches. */
ScopedMatchPairs matches(&cx->tempLifoAlloc());
- MatchConduit conduit(&matches);
- RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, staticsUpdate);
+ RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, matches, staticsUpdate);
if (status == RegExpRunStatus_Error)
return false;
@@ -711,14 +685,8 @@ js::regexp_exec_no_statics(JSContext *cx, unsigned argc, Value *vp)
static bool
regexp_test_impl(JSContext *cx, CallArgs args)
{
-#ifdef JS_YARR
- MatchPair match;
- MatchConduit conduit(&match);
-#else
ScopedMatchPairs matches(&cx->tempLifoAlloc());
- MatchConduit conduit(&matches);
-#endif
- RegExpRunStatus status = ExecuteRegExp(cx, args, conduit);
+ RegExpRunStatus status = ExecuteRegExp(cx, args, matches);
args.rval().setBoolean(status == RegExpRunStatus_Success);
return status != RegExpRunStatus_Error;
}
@@ -727,14 +695,8 @@ regexp_test_impl(JSContext *cx, CallArgs args)
bool
js::regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, bool *result)
{
-#ifdef JS_YARR
- MatchPair match;
- MatchConduit conduit(&match);
-#else
ScopedMatchPairs matches(&cx->tempLifoAlloc());
- MatchConduit conduit(&matches);
-#endif
- RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, conduit, UpdateRegExpStatics);
+ RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, matches, UpdateRegExpStatics);
*result = (status == RegExpRunStatus_Success);
return status != RegExpRunStatus_Error;
}
@@ -757,14 +719,8 @@ js::regexp_test_no_statics(JSContext *cx, unsigned argc, Value *vp)
RootedObject regexp(cx, &args[0].toObject());
RootedString string(cx, args[1].toString());
-#ifdef JS_YARR
- MatchPair match;
- MatchConduit conduit(&match);
-#else
ScopedMatchPairs matches(&cx->tempLifoAlloc());
- MatchConduit conduit(&matches);
-#endif
- RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, DontUpdateRegExpStatics);
+ RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, matches, DontUpdateRegExpStatics);
args.rval().setBoolean(status == RegExpRunStatus_Success);
return status != RegExpRunStatus_Error;
}
diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h
index 6d2cbac9ab09..fe194b9a1045 100644
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -19,15 +19,13 @@ js_InitRegExpClass(JSContext *cx, js::HandleObject obj);
namespace js {
-class MatchConduit;
-
// Whether RegExp statics should be updated with the input and results of a
// regular expression execution.
enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
RegExpRunStatus
ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string,
- MatchConduit &matches, RegExpStaticsUpdate staticsUpdate);
+ MatchPairs &matches, RegExpStaticsUpdate staticsUpdate);
/*
* Legacy behavior of ExecuteRegExp(), which is baked into the JSAPI.
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index e61f83e49c8d..e3c8866e58c4 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -6,6 +6,9 @@
#include "builtin/TestingFunctions.h"
+#include "mozilla/Move.h"
+#include "mozilla/Scoped.h"
+
#include "jsapi.h"
#include "jscntxt.h"
#include "jsfriendapi.h"
@@ -18,7 +21,11 @@
#include "jit/AsmJS.h"
#include "jit/AsmJSLink.h"
+#include "js/HashTable.h"
#include "js/StructuredClone.h"
+#include "js/UbiNode.h"
+#include "js/UbiNodeTraverse.h"
+#include "js/Vector.h"
#include "vm/ForkJoin.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
@@ -33,6 +40,8 @@ using namespace js;
using namespace JS;
using mozilla::ArrayLength;
+using mozilla::Move;
+using mozilla::ScopedFreePtr;
// If fuzzingSafe is set, remove functionality that could cause problems with
// fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
@@ -1651,6 +1660,214 @@ ReportLargeAllocationFailure(JSContext *cx, unsigned argc, jsval *vp)
return true;
}
+namespace heaptools {
+
+// An edge to a node from its predecessor in a path through the graph.
+class BackEdge {
+ // The node from which this edge starts.
+ JS::ubi::Node predecessor_;
+
+ // The name of this edge. We own this storage.
+ ScopedFreePtr name_;
+
+ public:
+ BackEdge() : name_(nullptr) { }
+ // Construct an initialized back edge. Take ownership of |name|.
+ BackEdge(JS::ubi::Node predecessor, jschar *name)
+ : predecessor_(predecessor), name_(name) { }
+ BackEdge(BackEdge &&rhs) : predecessor_(rhs.predecessor_), name_(rhs.name_.forget()) { }
+ BackEdge &operator=(BackEdge &&rhs) {
+ MOZ_ASSERT(&rhs != this);
+ this->~BackEdge();
+ new(this) BackEdge(Move(rhs));
+ return *this;
+ }
+
+ jschar *forgetName() { return name_.forget(); }
+ JS::ubi::Node predecessor() const { return predecessor_; }
+
+ private:
+ // No copy constructor or copying assignment.
+ BackEdge(const BackEdge &) MOZ_DELETE;
+ BackEdge &operator=(const BackEdge &) MOZ_DELETE;
+};
+
+// A path-finding handler class for use with JS::ubi::BreadthFirst.
+struct FindPathHandler {
+ typedef BackEdge NodeData;
+ typedef JS::ubi::BreadthFirst Traversal;
+
+ FindPathHandler(JS::ubi::Node start, JS::ubi::Node target,
+ AutoValueVector &nodes, Vector > &edges)
+ : start(start), target(target), foundPath(false),
+ nodes(nodes), edges(edges) { }
+
+ bool
+ operator()(Traversal &traversal, JS::ubi::Node origin, const JS::ubi::Edge &edge,
+ BackEdge *backEdge, bool first)
+ {
+ // We take care of each node the first time we visit it, so there's
+ // nothing to be done on subsequent visits.
+ if (!first)
+ return true;
+
+ // Record how we reached this node. This is the last edge on a
+ // shortest path to this node.
+ jschar *edgeName = js_strdup(traversal.cx, edge.name);
+ if (!edgeName)
+ return false;
+ *backEdge = mozilla::Move(BackEdge(origin, edgeName));
+
+ // Have we reached our final target node?
+ if (edge.referent == target) {
+ // Record the path that got us here, which must be a shortest path.
+ if (!recordPath(traversal))
+ return false;
+ foundPath = true;
+ traversal.stop();
+ }
+
+ return true;
+ }
+
+ // We've found a path to our target. Walk the backlinks to produce the
+ // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
+ // rooted, so it can hold the path's nodes as we leave the scope of
+ // the AutoCheckCannotGC.
+ bool recordPath(Traversal &traversal) {
+ JS::ubi::Node here = target;
+
+ do {
+ Traversal::NodeMap::Ptr p = traversal.visited.lookup(here);
+ MOZ_ASSERT(p);
+ JS::ubi::Node predecessor = p->value().predecessor();
+ if (!nodes.append(predecessor.exposeToJS()) ||
+ !edges.append(p->value().forgetName()))
+ return false;
+ here = predecessor;
+ } while (here != start);
+
+ return true;
+ }
+
+ // The node we're starting from.
+ JS::ubi::Node start;
+
+ // The node we're looking for.
+ JS::ubi::Node target;
+
+ // True if we found a path to target, false if we didn't.
+ bool foundPath;
+
+ // The nodes and edges of the path --- should we find one. The path is
+ // stored in reverse order, because that's how it's easiest for us to
+ // construct it:
+ // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
+ // - edges[0] is the name of the edge from nodes[0] to the target.
+ // - The last node, nodes[n-1], is the start node.
+ AutoValueVector &nodes;
+ Vector > &edges;
+};
+
+} // namespace heaptools
+
+static bool
+FindPath(JSContext *cx, unsigned argc, jsval *vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (argc < 2) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
+ "findPath", "1", "");
+ return false;
+ }
+
+ // We don't ToString non-objects given as 'start' or 'target'. We can't
+ // see edges to non-string primitive values, and it doesn't make much
+ // sense to ask for paths to or from a freshly allocated string, so
+ // if a non-string primitive appears here it's probably a mistake.
+ if (!args[0].isObject() && !args[0].isString()) {
+ js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
+ JSDVG_SEARCH_STACK, args[0], JS::NullPtr(),
+ "neither an object nor a string", NULL);
+ return false;
+ }
+
+ if (!args[1].isObject() && !args[1].isString()) {
+ js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
+ JSDVG_SEARCH_STACK, args[0], JS::NullPtr(),
+ "neither an object nor a string", NULL);
+ return false;
+ }
+
+ AutoValueVector nodes(cx);
+ Vector > edges(cx);
+
+ {
+ // We can't tolerate the GC moving things around while we're searching
+ // the heap. Check that nothing we do causes a GC.
+ JS::AutoCheckCannotGC autoCannotGC;
+
+ JS::ubi::Node start(args[0]), target(args[1]);
+
+ heaptools::FindPathHandler handler(start, target, nodes, edges);
+ heaptools::FindPathHandler::Traversal traversal(cx, handler, autoCannotGC);
+ if (!traversal.init() || !traversal.addStart(start))
+ return false;
+
+ if (!traversal.traverse())
+ return false;
+
+ if (!handler.foundPath) {
+ // We didn't find any paths from the start to the target.
+ args.rval().setUndefined();
+ return true;
+ }
+ }
+
+ // |nodes| and |edges| contain the path from |start| to |target|, reversed.
+ // Construct a JavaScript array describing the path from the start to the
+ // target. Each element has the form:
+ //
+ // { node: