diff --git a/accessible/tests/mochitest/a11y.ini b/accessible/tests/mochitest/a11y.ini index aace285e83..c571254bba 100644 --- a/accessible/tests/mochitest/a11y.ini +++ b/accessible/tests/mochitest/a11y.ini @@ -6,25 +6,8 @@ support-files = letters.gif moz.png longdesc_src.html - actions.js - attributes.js - autocomplete.js - browser.js - common.js - events.js - grid.js - layout.js - name.js - pivot.js - relations.js - role.js - selectable.js - states.js - table.js - value.js - text.js + *.js treeview.css - treeview.js [test_aria_token_attrs.html] [test_bug420863.html] diff --git a/accessible/tests/mochitest/actions/a11y.ini b/accessible/tests/mochitest/actions/a11y.ini index 9750b29f43..ca01f41040 100644 --- a/accessible/tests/mochitest/actions/a11y.ini +++ b/accessible/tests/mochitest/actions/a11y.ini @@ -1,4 +1,7 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/dom/media/test/bug461281.ogg [test_anchors.html] [test_aria.html] diff --git a/accessible/tests/mochitest/attributes/a11y.ini b/accessible/tests/mochitest/attributes/a11y.ini index de355ced1d..ad53ecd279 100644 --- a/accessible/tests/mochitest/attributes/a11y.ini +++ b/accessible/tests/mochitest/attributes/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_obj.html] [test_obj_css.html] diff --git a/accessible/tests/mochitest/bounds/a11y.ini b/accessible/tests/mochitest/bounds/a11y.ini index 643293df22..d60bd46a50 100644 --- a/accessible/tests/mochitest/bounds/a11y.ini +++ b/accessible/tests/mochitest/bounds/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_list.html] [test_select.html] diff --git a/accessible/tests/mochitest/editabletext/a11y.ini b/accessible/tests/mochitest/editabletext/a11y.ini index 333438fdf6..68466fdf23 100644 --- a/accessible/tests/mochitest/editabletext/a11y.ini +++ b/accessible/tests/mochitest/editabletext/a11y.ini @@ -1,5 +1,7 @@ [DEFAULT] -support-files = editabletext.js +support-files = + editabletext.js + !/accessible/tests/mochitest/*.js [test_1.html] [test_2.html] diff --git a/accessible/tests/mochitest/elm/a11y.ini b/accessible/tests/mochitest/elm/a11y.ini index ecbf075e52..76fb4402be 100644 --- a/accessible/tests/mochitest/elm/a11y.ini +++ b/accessible/tests/mochitest/elm/a11y.ini @@ -1,4 +1,8 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/moz.png + !/dom/media/test/bug461281.ogg [test_HTMLSpec.html] skip-if = buildapp == 'mulet' diff --git a/accessible/tests/mochitest/events/a11y.ini b/accessible/tests/mochitest/events/a11y.ini index 6b549c854a..d1c7eaf542 100644 --- a/accessible/tests/mochitest/events/a11y.ini +++ b/accessible/tests/mochitest/events/a11y.ini @@ -3,6 +3,8 @@ support-files = docload_wnd.html focus.html scroll.html + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif [test_aria_alert.html] [test_aria_menu.html] diff --git a/accessible/tests/mochitest/focus/a11y.ini b/accessible/tests/mochitest/focus/a11y.ini index f77cb2ae88..48d5e6654c 100644 --- a/accessible/tests/mochitest/focus/a11y.ini +++ b/accessible/tests/mochitest/focus/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_focusedChild.html] skip-if = (os == 'win' && (os_version == '6.2' || os_version == '6.3')) # bug 845134 diff --git a/accessible/tests/mochitest/hittest/a11y.ini b/accessible/tests/mochitest/hittest/a11y.ini index 33202fda5c..13bca54de2 100644 --- a/accessible/tests/mochitest/hittest/a11y.ini +++ b/accessible/tests/mochitest/hittest/a11y.ini @@ -1,5 +1,7 @@ [DEFAULT] support-files = zoom_tree.xul + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif [test_browser.html] [test_canvas_hitregion.html] diff --git a/accessible/tests/mochitest/hyperlink/a11y.ini b/accessible/tests/mochitest/hyperlink/a11y.ini index 85d274dc8e..f37dca8e12 100644 --- a/accessible/tests/mochitest/hyperlink/a11y.ini +++ b/accessible/tests/mochitest/hyperlink/a11y.ini @@ -1,5 +1,7 @@ [DEFAULT] support-files = hyperlink.js + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif [test_general.html] [test_general.xul] diff --git a/accessible/tests/mochitest/hypertext/a11y.ini b/accessible/tests/mochitest/hypertext/a11y.ini index 9f221c03d6..27f878f743 100644 --- a/accessible/tests/mochitest/hypertext/a11y.ini +++ b/accessible/tests/mochitest/hypertext/a11y.ini @@ -1,4 +1,7 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif [test_general.html] [test_update.html] diff --git a/accessible/tests/mochitest/jsat/a11y.ini b/accessible/tests/mochitest/jsat/a11y.ini index 78143b2a0c..4171fefcc4 100644 --- a/accessible/tests/mochitest/jsat/a11y.ini +++ b/accessible/tests/mochitest/jsat/a11y.ini @@ -7,6 +7,8 @@ support-files = doc_traversal.html doc_content_integration.html doc_content_text.html + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/moz.png [test_alive.html] [test_content_integration.html] diff --git a/accessible/tests/mochitest/name/a11y.ini b/accessible/tests/mochitest/name/a11y.ini index e5147ed2d7..edfda49ba5 100644 --- a/accessible/tests/mochitest/name/a11y.ini +++ b/accessible/tests/mochitest/name/a11y.ini @@ -4,6 +4,8 @@ support-files = general.xbl markup.js markuprules.xml + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/moz.png [test_browserui.xul] [test_counterstyle.html] diff --git a/accessible/tests/mochitest/pivot/a11y.ini b/accessible/tests/mochitest/pivot/a11y.ini index b1fec993fe..8add460947 100644 --- a/accessible/tests/mochitest/pivot/a11y.ini +++ b/accessible/tests/mochitest/pivot/a11y.ini @@ -2,6 +2,7 @@ support-files = doc_virtualcursor.html doc_virtualcursor_text.html + !/accessible/tests/mochitest/*.js [test_virtualcursor.html] [test_virtualcursor_text.html] diff --git a/accessible/tests/mochitest/relations/a11y.ini b/accessible/tests/mochitest/relations/a11y.ini index 7c5cc25b90..a2da0cf2ef 100644 --- a/accessible/tests/mochitest/relations/a11y.ini +++ b/accessible/tests/mochitest/relations/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_bindings.xhtml] [test_embeds.xul] diff --git a/accessible/tests/mochitest/role/a11y.ini b/accessible/tests/mochitest/role/a11y.ini index 7162a087c0..295fd34e4b 100644 --- a/accessible/tests/mochitest/role/a11y.ini +++ b/accessible/tests/mochitest/role/a11y.ini @@ -1,4 +1,7 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/moz.png [test_aria.html] [test_aria.xul] diff --git a/accessible/tests/mochitest/scroll/a11y.ini b/accessible/tests/mochitest/scroll/a11y.ini index e6005c7d0e..e2e9dfd48b 100644 --- a/accessible/tests/mochitest/scroll/a11y.ini +++ b/accessible/tests/mochitest/scroll/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_zoom.html] [test_zoom_text.html] diff --git a/accessible/tests/mochitest/selectable/a11y.ini b/accessible/tests/mochitest/selectable/a11y.ini index 9292871bd2..36e2e10b84 100644 --- a/accessible/tests/mochitest/selectable/a11y.ini +++ b/accessible/tests/mochitest/selectable/a11y.ini @@ -1,4 +1,8 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/treeview.css + !/accessible/tests/mochitest/treeview.js [test_aria.html] [test_listbox.xul] diff --git a/accessible/tests/mochitest/states/a11y.ini b/accessible/tests/mochitest/states/a11y.ini index 631ae38288..d080237371 100644 --- a/accessible/tests/mochitest/states/a11y.ini +++ b/accessible/tests/mochitest/states/a11y.ini @@ -5,6 +5,10 @@ support-files = z_frames_checkbox.html z_frames_textbox.html z_frames_update.html + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/dumbfile.xpi + !/accessible/tests/mochitest/formimage.png + !/accessible/tests/mochitest/treeview.css [test_aria.html] [test_aria.xul] diff --git a/accessible/tests/mochitest/table/a11y.ini b/accessible/tests/mochitest/table/a11y.ini index 1f6090c9f3..ba8e6ba98a 100644 --- a/accessible/tests/mochitest/table/a11y.ini +++ b/accessible/tests/mochitest/table/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_css_tables.html] [test_headers_ariagrid.html] diff --git a/accessible/tests/mochitest/text/a11y.ini b/accessible/tests/mochitest/text/a11y.ini index 5461e3cc12..cc80391555 100644 --- a/accessible/tests/mochitest/text/a11y.ini +++ b/accessible/tests/mochitest/text/a11y.ini @@ -1,5 +1,6 @@ [DEFAULT] support-files = doc.html + !/accessible/tests/mochitest/*.js [test_atcaretoffset.html] [test_charboundary.html] diff --git a/accessible/tests/mochitest/textattrs/a11y.ini b/accessible/tests/mochitest/textattrs/a11y.ini index 8315e027a6..046bd57e81 100644 --- a/accessible/tests/mochitest/textattrs/a11y.ini +++ b/accessible/tests/mochitest/textattrs/a11y.ini @@ -1,4 +1,7 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/moz.png [test_general.html] [test_invalid.html] diff --git a/accessible/tests/mochitest/textcaret/a11y.ini b/accessible/tests/mochitest/textcaret/a11y.ini index 5d147bd421..22d09751db 100644 --- a/accessible/tests/mochitest/textcaret/a11y.ini +++ b/accessible/tests/mochitest/textcaret/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_browserui.xul] [test_general.html] diff --git a/accessible/tests/mochitest/textrange/a11y.ini b/accessible/tests/mochitest/textrange/a11y.ini index cc32505311..4e610c61f3 100644 --- a/accessible/tests/mochitest/textrange/a11y.ini +++ b/accessible/tests/mochitest/textrange/a11y.ini @@ -1,4 +1,7 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/moz.png [test_general.html] [test_selection.html] diff --git a/accessible/tests/mochitest/textselection/a11y.ini b/accessible/tests/mochitest/textselection/a11y.ini index 69692ca7a6..6581af56db 100644 --- a/accessible/tests/mochitest/textselection/a11y.ini +++ b/accessible/tests/mochitest/textselection/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_general.html] [test_userinput.html] diff --git a/accessible/tests/mochitest/tree/a11y.ini b/accessible/tests/mochitest/tree/a11y.ini index 9a9be091ec..9ec1c71607 100644 --- a/accessible/tests/mochitest/tree/a11y.ini +++ b/accessible/tests/mochitest/tree/a11y.ini @@ -2,6 +2,12 @@ support-files = dockids.html wnd.xul + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/formimage.png + !/accessible/tests/mochitest/letters.gif + !/accessible/tests/mochitest/moz.png + !/accessible/tests/mochitest/tree/wnd.xul + !/dom/media/test/bug461281.ogg [test_applicationacc.xul] skip-if = true # Bug 561508 diff --git a/accessible/tests/mochitest/treeupdate/a11y.ini b/accessible/tests/mochitest/treeupdate/a11y.ini index bdb1b95701..5c304c0359 100644 --- a/accessible/tests/mochitest/treeupdate/a11y.ini +++ b/accessible/tests/mochitest/treeupdate/a11y.ini @@ -1,4 +1,8 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif + !/accessible/tests/mochitest/moz.png [test_ariadialog.html] [test_ariaowns.html] diff --git a/accessible/tests/mochitest/value/a11y.ini b/accessible/tests/mochitest/value/a11y.ini index 2bf0b7f9e9..b93d9b1800 100644 --- a/accessible/tests/mochitest/value/a11y.ini +++ b/accessible/tests/mochitest/value/a11y.ini @@ -1,4 +1,6 @@ [DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js [test_general.html] [test_number.html] diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in index 77a06ec23b..265dce0ae8 100644 --- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -159,8 +159,12 @@ endif endif +# Builds using the hybrid FasterMake/RecursiveMake backend will +# fail to produce a langpack. See bug 1255096. libs:: +ifeq (,$(filter FasterMake+RecursiveMake,$(BUILD_BACKENDS))) $(MAKE) -C $(DEPTH)/browser/locales langpack +endif ifeq (WINNT,$(OS_ARCH)) PKGCOMP_FIND_OPTS = diff --git a/build/mozconfig.cache b/build/mozconfig.cache index 69be996008..c793feb1be 100644 --- a/build/mozconfig.cache +++ b/build/mozconfig.cache @@ -5,7 +5,7 @@ # Setup for build cache # Avoid duplication if the file happens to be included twice. -if test -z "$bucket"; then +if test -z "$bucket" -a -z "$NO_CACHE"; then read branch platform master < /dev/null) diff --git a/caps/tests/mochitest/chrome.ini b/caps/tests/mochitest/chrome.ini index 818638f2da..2d1d24ec38 100644 --- a/caps/tests/mochitest/chrome.ini +++ b/caps/tests/mochitest/chrome.ini @@ -2,6 +2,7 @@ skip-if = buildapp == 'b2g' || os == 'android' support-files = file_disableScript.html + !/caps/tests/mochitest/file_disableScript.html [test_bug995943.xul] [test_addonMayLoad.html] diff --git a/caps/tests/mochitest/mochitest.ini b/caps/tests/mochitest/mochitest.ini index 2925995ec8..577f54408d 100644 --- a/caps/tests/mochitest/mochitest.ini +++ b/caps/tests/mochitest/mochitest.ini @@ -2,6 +2,7 @@ support-files = file_data.txt file_disableScript.html + !/js/xpconnect/tests/mochitest/file_empty.html [test_app_principal_equality.html] [test_bug246699.html] diff --git a/chrome/test/unit_ipc/xpcshell.ini b/chrome/test/unit_ipc/xpcshell.ini index 5fed166035..f7ec0fe9ab 100644 --- a/chrome/test/unit_ipc/xpcshell.ini +++ b/chrome/test/unit_ipc/xpcshell.ini @@ -2,5 +2,9 @@ head = tail = skip-if = toolkit == 'android' || toolkit == 'gonk' +support-files = + !/chrome/test/unit/data/** + !/chrome/test/unit/test_resolve_uris.js + !/chrome/test/unit/head_crtestutils.js [test_resolve_uris_ipc.js] diff --git a/config/rules.mk b/config/rules.mk index 8547d74742..0012d55d8d 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -897,19 +897,19 @@ $(OBJS) $(HOST_OBJS) $(PROGOBJS) $(HOST_PROGOBJS): $(GLOBAL_DEPS) # Rules for building native targets must come first because of the host_ prefix $(HOST_COBJS): $(REPORT_BUILD) - $(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + $(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) $(HOST_CPPOBJS): $(REPORT_BUILD) - $(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CXXFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + $(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CXXFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) $(HOST_CMOBJS): $(REPORT_BUILD) - $(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CFLAGS) $(HOST_CMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + $(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CFLAGS) $(HOST_CMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) $(HOST_CMMOBJS): $(REPORT_BUILD) - $(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CXXFLAGS) $(HOST_CMMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + $(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CXXFLAGS) $(HOST_CMMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) $(COBJS): $(REPORT_BUILD) diff --git a/configure.in b/configure.in index 4ae68a00bb..658bb58bd6 100644 --- a/configure.in +++ b/configure.in @@ -2051,13 +2051,7 @@ ia64*-hpux*) ;; *-*linux*) - # Note: both GNU_CC and INTEL_CC are set when using Intel's C compiler. - # Similarly for GNU_CXX and INTEL_CXX. - if test "$INTEL_CC" -o "$INTEL_CXX"; then - # -Os has been broken on Intel's C/C++ compilers for quite a - # while; Intel recommends against using it. - MOZ_OPTIMIZE_FLAGS="-O2" - elif test "$GNU_CC" -o "$GNU_CXX"; then + if test "$GNU_CC" -o "$GNU_CXX"; then MOZ_PGO_OPTIMIZE_FLAGS="-O3" MOZ_OPTIMIZE_FLAGS="-O2" if test -z "$CLANG_CC"; then @@ -7607,13 +7601,6 @@ fi CFLAGS="$_SAVE_CFLAGS" -if test -n "$INTEL_CC"; then - PROFILE_GEN_CFLAGS="-prof-gen -prof-dir ." - PROFILE_GEN_LDFLAGS= - PROFILE_USE_CFLAGS="-prof-use -prof-dir ." - PROFILE_USE_LDFLAGS= -fi - dnl Sun Studio on Solaris if test "$SOLARIS_SUNPRO_CC"; then PROFILE_GEN_CFLAGS="-xprofile=collect:$_objdir/$enable_application" @@ -8568,6 +8555,7 @@ AC_SUBST(WCHAR_CFLAGS) AC_SUBST(HOST_CC) AC_SUBST(HOST_CXX) AC_SUBST(HOST_CFLAGS) +AC_SUBST(HOST_CPPFLAGS) AC_SUBST(HOST_CXXFLAGS) AC_SUBST(HOST_LDFLAGS) AC_SUBST(HOST_OPTIMIZE_FLAGS) @@ -9016,6 +9004,7 @@ export LDFLAGS export HOST_CC export HOST_CXX export HOST_CFLAGS +export HOST_CPPFLAGS export HOST_CXXFLAGS export HOST_LDFLAGS diff --git a/docshell/base/timeline/JavascriptTimelineMarker.h b/docshell/base/timeline/JavascriptTimelineMarker.h index 47c9943a5e..5a99242831 100644 --- a/docshell/base/timeline/JavascriptTimelineMarker.h +++ b/docshell/base/timeline/JavascriptTimelineMarker.h @@ -62,7 +62,8 @@ public: return; } - if (!JS::CopyAsyncStack(aCx, asyncStack, asyncCause, &parentFrame, 0)) { + if (JS::IsSavedFrame(asyncStack) && + !JS::CopyAsyncStack(aCx, asyncStack, asyncCause, &parentFrame, 0)) { JS_ClearPendingException(aCx); } else { stackFrame.mAsyncParent = parentFrame; diff --git a/dom/animation/test/chrome.ini b/dom/animation/test/chrome.ini index c63f67c09d..c5fae26349 100644 --- a/dom/animation/test/chrome.ini +++ b/dom/animation/test/chrome.ini @@ -3,6 +3,8 @@ support-files = testcommon.js ../../imptests/testharness.js ../../imptests/testharnessreport.js + !/dom/animation/test/chrome/file_animate_xrays.html + [chrome/test_animate_xrays.html] # file_animate_xrays.html needs to go in mochitest.ini since it is served # over HTTP diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini index 7abf0b088d..eaddce2fe5 100644 --- a/dom/animation/test/mochitest.ini +++ b/dom/animation/test/mochitest.ini @@ -2,83 +2,85 @@ # Support files for chrome tests that we want to load over HTTP need # to go in here, not chrome.ini. support-files = - testcommon.js chrome/file_animate_xrays.html + css-animations/file_animation-cancel.html + css-animations/file_animation-computed-timing.html + css-animations/file_animation-currenttime.html + css-animations/file_animation-finish.html + css-animations/file_animation-finished.html + css-animations/file_animation-id.html + css-animations/file_animation-oncancel.html + css-animations/file_animation-onfinish.html + css-animations/file_animation-pausing.html + css-animations/file_animation-play.html + css-animations/file_animation-playstate.html + css-animations/file_animation-ready.html + css-animations/file_animation-reverse.html + css-animations/file_animation-starttime.html + css-animations/file_animations-dynamic-changes.html + css-animations/file_cssanimation-animationname.html + css-animations/file_document-get-animations.html + css-animations/file_effect-target.html + css-animations/file_element-get-animations.html + css-animations/file_keyframeeffect-getframes.html + css-animations/file_pseudoElement-get-animations.html + css-transitions/file_animation-cancel.html + css-transitions/file_animation-computed-timing.html + css-transitions/file_animation-currenttime.html + css-transitions/file_animation-finished.html + css-transitions/file_animation-pausing.html + css-transitions/file_animation-ready.html + css-transitions/file_animation-starttime.html + css-transitions/file_csstransition-transitionproperty.html + css-transitions/file_document-get-animations.html + css-transitions/file_effect-target.html + css-transitions/file_element-get-animations.html + css-transitions/file_keyframeeffect-getframes.html + css-transitions/file_pseudoElement-get-animations.html + document-timeline/file_document-timeline.html + mozilla/file_deferred_start.html + mozilla/file_hide_and_show.html + mozilla/file_partial_keyframes.html + testcommon.js [css-animations/test_animations-dynamic-changes.html] -support-files = css-animations/file_animations-dynamic-changes.html [css-animations/test_animation-cancel.html] -support-files = css-animations/file_animation-cancel.html [css-animations/test_animation-computed-timing.html] -support-files = css-animations/file_animation-computed-timing.html [css-animations/test_animation-currenttime.html] -support-files = css-animations/file_animation-currenttime.html [css-animations/test_animation-finish.html] -support-files = css-animations/file_animation-finish.html [css-animations/test_animation-finished.html] -support-files = css-animations/file_animation-finished.html [css-animations/test_animation-id.html] -support-files = css-animations/file_animation-id.html [css-animations/test_animation-oncancel.html] -support-files = css-animations/file_animation-oncancel.html [css-animations/test_animation-onfinish.html] -support-files = css-animations/file_animation-onfinish.html [css-animations/test_animation-pausing.html] -support-files = css-animations/file_animation-pausing.html [css-animations/test_animation-play.html] -support-files = css-animations/file_animation-play.html [css-animations/test_animation-playstate.html] -support-files = css-animations/file_animation-playstate.html [css-animations/test_animation-ready.html] -support-files = css-animations/file_animation-ready.html [css-animations/test_animation-reverse.html] -support-files = css-animations/file_animation-reverse.html [css-animations/test_animation-starttime.html] -support-files = css-animations/file_animation-starttime.html [css-animations/test_cssanimation-animationname.html] -support-files = css-animations/file_cssanimation-animationname.html [css-animations/test_document-get-animations.html] -support-files = css-animations/file_document-get-animations.html [css-animations/test_effect-target.html] -support-files = css-animations/file_effect-target.html [css-animations/test_element-get-animations.html] skip-if = buildapp == 'mulet' -support-files = css-animations/file_element-get-animations.html [css-animations/test_keyframeeffect-getframes.html] -support-files = css-animations/file_keyframeeffect-getframes.html [css-transitions/test_animation-cancel.html] -support-files = css-transitions/file_animation-cancel.html [css-transitions/test_animation-computed-timing.html] -support-files = css-transitions/file_animation-computed-timing.html [css-transitions/test_animation-currenttime.html] -support-files = css-transitions/file_animation-currenttime.html [css-transitions/test_animation-finished.html] -support-files = css-transitions/file_animation-finished.html [css-transitions/test_animation-pausing.html] -support-files = css-transitions/file_animation-pausing.html [css-transitions/test_animation-ready.html] -support-files = css-transitions/file_animation-ready.html [css-transitions/test_animation-starttime.html] -support-files = css-transitions/file_animation-starttime.html [css-transitions/test_csstransition-transitionproperty.html] -support-files = css-transitions/file_csstransition-transitionproperty.html [css-transitions/test_document-get-animations.html] -support-files = css-transitions/file_document-get-animations.html [css-transitions/test_effect-target.html] -support-files = css-transitions/file_effect-target.html [css-transitions/test_element-get-animations.html] skip-if = buildapp == 'mulet' -support-files = css-transitions/file_element-get-animations.html [css-transitions/test_keyframeeffect-getframes.html] -support-files = css-transitions/file_keyframeeffect-getframes.html [document-timeline/test_document-timeline.html] -support-files = document-timeline/file_document-timeline.html [document-timeline/test_request_animation_frame.html] skip-if = buildapp == 'mulet' [mozilla/test_deferred_start.html] -support-files = mozilla/file_deferred_start.html skip-if = (toolkit == 'gonk' && debug) [mozilla/test_hide_and_show.html] -support-files = mozilla/file_hide_and_show.html [mozilla/test_partial_keyframes.html] -support-files = mozilla/file_partial_keyframes.html diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 1ca7a6781f..e2cf05dbf4 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3837,6 +3837,22 @@ nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc, return rv; } +/* static */ +nsresult +nsContentUtils::DispatchFocusChromeEvent(nsPIDOMWindow* aWindow) +{ + MOZ_ASSERT(aWindow); + + nsCOMPtr doc = aWindow->GetExtantDoc(); + if (!doc) { + return NS_ERROR_FAILURE; + } + + return DispatchChromeEvent(doc, aWindow, + NS_LITERAL_STRING("DOMServiceWorkerFocusClient"), + true, true); +} + nsresult nsContentUtils::DispatchEventOnlyToChrome(nsIDocument* aDoc, nsISupports* aTarget, @@ -8175,7 +8191,8 @@ nsContentUtils::IsPreloadType(nsContentPolicyType aType) nsresult nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal, nsIDocument* aDoc, - nsIHttpChannel* aChannel) + nsIHttpChannel* aChannel, + mozilla::net::ReferrerPolicy aReferrerPolicy) { NS_ENSURE_ARG_POINTER(aPrincipal); NS_ENSURE_ARG_POINTER(aChannel); @@ -8189,7 +8206,7 @@ nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal, aPrincipal->GetURI(getter_AddRefs(principalURI)); if (!aDoc) { - return aChannel->SetReferrerWithPolicy(principalURI, net::RP_Default); + return aChannel->SetReferrerWithPolicy(principalURI, aReferrerPolicy); } // If it weren't for history.push/replaceState, we could just use the @@ -8218,7 +8235,10 @@ nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal, referrerURI = principalURI; } - net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy(); + net::ReferrerPolicy referrerPolicy = aReferrerPolicy; + if (referrerPolicy == net::RP_Default) { + referrerPolicy = aDoc->GetReferrerPolicy(); + } return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy); } diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index c2396b7bc0..5dec7373b3 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1107,6 +1107,13 @@ public: bool aCancelable, bool *aDefaultAction = nullptr); + /** + * Helper function for dispatching a "DOMServiceWorkerFocusClient" event to + * the chrome event handler of the given DOM Window. This has the effect + * of focusing the corresponding tab and bringing the browser window + * to the foreground. + */ + static nsresult DispatchFocusChromeEvent(nsPIDOMWindow* aWindow); /** * This method creates and dispatches a trusted event. @@ -2534,7 +2541,8 @@ public: */ static nsresult SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal, nsIDocument* aDoc, - nsIHttpChannel* aChannel); + nsIHttpChannel* aChannel, + mozilla::net::ReferrerPolicy aReferrerPolicy); static bool PushEnabled(JSContext* aCx, JSObject* aObj); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 3e33f8cf02..14c1fce641 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -744,14 +744,14 @@ protected: JS::AutoIdVector &props) const; }; -const js::Class OuterWindowProxyClass = - PROXY_CLASS_WITH_EXT( - "Proxy", - 0, /* additional class flags */ - PROXY_MAKE_EXT( - false, /* isWrappedNative */ - nsOuterWindowProxy::ObjectMoved - )); +static const js::ClassExtension OuterWindowProxyClassExtension = PROXY_MAKE_EXT( + nsOuterWindowProxy::ObjectMoved +); + +const js::Class OuterWindowProxyClass = PROXY_CLASS_WITH_EXT( + "Proxy", + 0, /* additional class flags */ + &OuterWindowProxyClassExtension); const char * nsOuterWindowProxy::className(JSContext *cx, JS::Handle proxy) const @@ -12150,7 +12150,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, /* aCalledFromScript = */ true, aDialog, aNavigate, nullptr, argv, aLoadInfo, - getter_AddRefs(domReturn)); + 1.0f, 0, getter_AddRefs(domReturn)); } else { // Force a system caller here so that the window watcher won't screw us // up. We do NOT want this case looking at the JS context on the stack @@ -12171,7 +12171,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, /* aCalledFromScript = */ false, aDialog, aNavigate, nullptr, aExtraArgument, aLoadInfo, - getter_AddRefs(domReturn)); + 1.0f, 0, getter_AddRefs(domReturn)); } } diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 5d994b8b6d..d017134e6c 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2535,6 +2535,14 @@ SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClo JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref); } +static void +SetMemoryGCRefreshFrameSlicesEnabledPrefChangedCallback(const char* aPrefName, void* aClosure) +{ + bool pref = Preferences::GetBool(aPrefName); + JS_SetGCParameter(sRuntime, JSGC_REFRESH_FRAME_SLICES_ENABLED, pref); +} + + static void SetIncrementalCCPrefChangedCallback(const char* aPrefName, void* aClosure) { @@ -2638,6 +2646,9 @@ nsJSContext::EnsureStatics() Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicMarkSlicePrefChangedCallback, "javascript.options.mem.gc_dynamic_mark_slice"); + Preferences::RegisterCallbackAndCall(SetMemoryGCRefreshFrameSlicesEnabledPrefChangedCallback, + "javascript.options.mem.gc_refresh_frame_slices_enabled"); + Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicHeapGrowthPrefChangedCallback, "javascript.options.mem.gc_dynamic_heap_growth"); diff --git a/dom/base/nsXMLHttpRequest.cpp b/dom/base/nsXMLHttpRequest.cpp index db0a2e0790..fbdf84d998 100644 --- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -2610,7 +2610,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable& aBody) nsCOMPtr owner = GetOwner(); nsCOMPtr doc = owner ? owner->GetExtantDoc() : nullptr; nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc, - httpChannel); + httpChannel, mozilla::net::RP_Default); } // Some extensions override the http protocol handler and provide their own diff --git a/dom/base/test/unit_ipc/xpcshell.ini b/dom/base/test/unit_ipc/xpcshell.ini index d8e0d38f65..2ad0d9acfa 100644 --- a/dom/base/test/unit_ipc/xpcshell.ini +++ b/dom/base/test/unit_ipc/xpcshell.ini @@ -2,6 +2,9 @@ head = tail = skip-if = toolkit == 'android' || toolkit == 'gonk' +support-files = + !/dom/base/test/unit/test_bug553888.js + !/dom/base/test/unit/test_xhr_document.js [test_bug553888_wrap.js] [test_xhr_document_ipc.js] diff --git a/dom/base/test/websocket_helpers.js b/dom/base/test/websocket_helpers.js new file mode 100644 index 0000000000..46cf765d7c --- /dev/null +++ b/dom/base/test/websocket_helpers.js @@ -0,0 +1,66 @@ +var current_test = 0; + +function shouldNotOpen(e) { + var ws = e.target; + ok(false, "onopen shouldn't be called on test " + ws._testNumber + "!"); +} + +function shouldCloseCleanly(e) { + var ws = e.target; + ok(e.wasClean, "the ws connection in test " + ws._testNumber + " should be closed cleanly"); +} + +function shouldCloseNotCleanly(e) { + var ws = e.target; + ok(!e.wasClean, "the ws connection in test " + ws._testNumber + " shouldn't be closed cleanly"); +} + +function ignoreError(e) { +} + +function CreateTestWS(ws_location, ws_protocol) { + var ws; + + try { + if (ws_protocol == undefined) { + ws = new WebSocket(ws_location); + } else { + ws = new WebSocket(ws_location, ws_protocol); + } + + ws._testNumber = current_test; + ok(true, "Created websocket for test " + ws._testNumber +"\n"); + + ws.onerror = function(e) { + ok(false, "onerror called on test " + e.target._testNumber + "!"); + } + + } catch (e) { + throw e; + } + + return ws; +} + +function forcegc() { + SpecialPowers.forceGC(); + SpecialPowers.gc(); +} + +function feedback() { + $("feedback").innerHTML = "executing test: " + (current_test+1) + " of " + tests.length + " tests."; +} + +function finish() { + SimpleTest.finish(); +} + +function doTest() { + if (current_test >= tests.length) { + finish(); + return; + } + + feedback(); + tests[current_test++]().then(doTest); +} diff --git a/dom/base/test/websocket_tests.js b/dom/base/test/websocket_tests.js new file mode 100644 index 0000000000..e628965071 --- /dev/null +++ b/dom/base/test/websocket_tests.js @@ -0,0 +1,1244 @@ +// test1: client tries to connect to a http scheme location; +function test1() { + return new Promise(function(resolve, reject) { + try { + var ws = CreateTestWS("http://mochi.test:8888/tests/dom/base/test/file_websocket"); + ok(false, "test1 failed"); + } catch (e) { + ok(true, "test1 failed"); + } + + resolve(); + }); +} + +// test2: assure serialization of the connections; +// this test expects that the serialization list to connect to the proxy +// is empty. +function test2() { + return new Promise(function(resolve, reject) { + var waitTest2Part1 = true; + var waitTest2Part2 = true; + + var ws1 = CreateTestWS("ws://sub2.test2.example.com/tests/dom/base/test/file_websocket", "test-2.1"); + var ws2 = CreateTestWS("ws://sub2.test2.example.com/tests/dom/base/test/file_websocket", "test-2.2"); + + var ws2CanConnect = false; + + function maybeFinished() { + if (!waitTest2Part1 && !waitTest2Part2) { + resolve(); + } + } + + ws1.onopen = function() { + ok(true, "ws1 open in test 2"); + ws2CanConnect = true; + ws1.close(); + } + + ws1.onclose = function(e) { + waitTest2Part1 = false; + maybeFinished(); + } + + ws2.onopen = function() { + ok(ws2CanConnect, "shouldn't connect yet in test-2!"); + ws2.close(); + } + + ws2.onclose = function(e) { + waitTest2Part2 = false; + maybeFinished(); + } + }); +} + +// test3: client tries to connect to an non-existent ws server; +function test3() { + return new Promise(function(resolve, reject) { + var hasError = false; + var ws = CreateTestWS("ws://this.websocket.server.probably.does.not.exist"); + + ws.onopen = shouldNotOpen; + + ws.onerror = function (e) { + hasError = true; + } + + ws.onclose = function(e) { + shouldCloseNotCleanly(e); + ok(hasError, "rcvd onerror event"); + is(e.code, 1006, "test-3 close code should be 1006 but is:" + e.code); + resolve(); + } + }); +} + +// test4: client tries to connect using a relative url; +function test4() { + return new Promise(function(resolve, reject) { + try { + var ws = CreateTestWS("file_websocket"); + ok(false, "test-4 failed"); + } catch (e) { + ok(true, "test-4 failed"); + } + + resolve(); + }); +} + +// test5: client uses an invalid protocol value; +function test5() { + return new Promise(function(resolve, reject) { + try { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", ""); + ok(false, "couldn't accept an empty string in the protocol parameter"); + } catch (e) { + ok(true, "couldn't accept an empty string in the protocol parameter"); + } + + try { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "\n"); + ok(false, "couldn't accept any not printable ASCII character in the protocol parameter"); + } catch (e) { + ok(true, "couldn't accept any not printable ASCII character in the protocol parameter"); + } + + try { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test 5"); + ok(false, "U+0020 not acceptable in protocol parameter"); + } catch (e) { + ok(true, "U+0020 not acceptable in protocol parameter"); + } + + resolve(); + }); +} + +// test6: counter and encoding check; +function test6() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-6"); + var counter = 1; + + ws.onopen = function() { + ws.send(counter); + } + + ws.onmessage = function(e) { + if (counter == 5) { + is(e.data, "あいうえお", "test-6 counter 5 data ok"); + ws.close(); + } else { + is(parseInt(e.data), counter+1, "bad counter"); + counter += 2; + ws.send(counter); + } + } + + ws.onclose = function(e) { + shouldCloseCleanly(e); + resolve(); + } + }); +} + +// test7: onmessage event origin property check +function test7() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://sub2.test2.example.org/tests/dom/base/test/file_websocket", "test-7"); + var gotmsg = false; + + ws.onopen = function() { + ok(true, "test 7 open"); + } + + ws.onmessage = function(e) { + ok(true, "test 7 message"); + is(e.origin, "ws://sub2.test2.example.org", "onmessage origin set to ws:// host"); + gotmsg = true; + ws.close(); + } + + ws.onclose = function(e) { + ok(gotmsg, "recvd message in test 7 before close"); + shouldCloseCleanly(e); + resolve(); + } + }); +} + +// test8: client calls close() and the server sends the close frame (with no +// code or reason) in acknowledgement; +function test8() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-8"); + + ws.onopen = function() { + is(ws.protocol, "test-8", "test-8 subprotocol selection"); + ws.close(); + } + + ws.onclose = function(e) { + shouldCloseCleanly(e); + // We called close() with no close code: so pywebsocket will also send no + // close code, which translates to code 1005 + is(e.code, 1005, "test-8 close code has wrong value:" + e.code); + is(e.reason, "", "test-8 close reason has wrong value:" + e.reason); + resolve(); + } + }); +} + +// test9: client closes the connection before the ws connection is established; +function test9() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://test2.example.org/tests/dom/base/test/file_websocket", "test-9"); + + ws._receivedErrorEvent = false; + + ws.onopen = shouldNotOpen; + + ws.onerror = function(e) { + ws._receivedErrorEvent = true; + } + + ws.onclose = function(e) { + ok(ws._receivedErrorEvent, "Didn't received the error event in test 9."); + shouldCloseNotCleanly(e); + resolve(); + } + + ws.close(); + }); +} + +// test10: client sends a message before the ws connection is established; +function test10() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://sub1.test1.example.com/tests/dom/base/test/file_websocket", "test-10"); + + ws.onclose = function(e) { + shouldCloseCleanly(e); + resolve(); + } + + try { + ws.send("client data"); + ok(false, "Couldn't send data before connecting!"); + } catch (e) { + ok(true, "Couldn't send data before connecting!"); + } + + ws.onopen = function() + { + ok(true, "test 10 opened"); + ws.close(); + } + }); +} + +// test11: a simple hello echo; +function test11() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-11"); + is(ws.readyState, 0, "create bad readyState in test-11!"); + + ws.onopen = function() { + is(ws.readyState, 1, "open bad readyState in test-11!"); + ws.send("client data"); + } + + ws.onmessage = function(e) { + is(e.data, "server data", "bad received message in test-11!"); + ws.close(1000, "Have a nice day"); + + // this ok() is disabled due to a race condition - it state may have + // advanced through 2 (closing) and into 3 (closed) before it is evald + // ok(ws.readyState == 2, "onmessage bad readyState in test-11!"); + } + + ws.onclose = function(e) { + is(ws.readyState, 3, "onclose bad readyState in test-11!"); + shouldCloseCleanly(e); + is(e.code, 1000, "test 11 got wrong close code: " + e.code); + is(e.reason, "Have a nice day", "test 11 got wrong close reason: " + e.reason); + resolve(); + } + }); +} + +// test12: client sends a message containing unpaired surrogates +function test12() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-12"); + + ws.onopen = function() { + try { + // send an unpaired surrogate + ws._gotMessage = false; + ws.send("a\ud800b"); + ok(true, "ok to send an unpaired surrogate"); + } catch (e) { + ok(false, "shouldn't fail any more when sending an unpaired surrogate!"); + } + } + + ws.onmessage = function(msg) { + is(msg.data, "SUCCESS", "Unpaired surrogate in UTF-16 not converted in test-12"); + ws._gotMessage = true; + // Must support unpaired surrogates in close reason, too + ws.close(1000, "a\ud800b"); + } + + ws.onclose = function(e) { + is(ws.readyState, 3, "onclose bad readyState in test-12!"); + ok(ws._gotMessage, "didn't receive message!"); + shouldCloseCleanly(e); + is(e.code, 1000, "test 12 got wrong close code: " + e.code); + is(e.reason, "a\ufffdb", "test 11 didn't get replacement char in close reason: " + e.reason); + resolve(); + } + }); +} + +// test13: server sends an invalid message; +function test13() { + return new Promise(function(resolve, reject) { + // previous versions of this test counted the number of protocol errors + // returned, but the protocol stack typically closes down after reporting a + // protocol level error - trying to resync is too dangerous + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-13"); + ws._timesCalledOnError = 0; + + ws.onerror = function() { + ws._timesCalledOnError++; + } + + ws.onclose = function(e) { + ok(ws._timesCalledOnError > 0, "no error events"); + resolve(); + } + }); +} + +// test14: server sends the close frame, it doesn't close the tcp connection +// and it keeps sending normal ws messages; +function test14() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-14"); + + ws.onmessage = function() { + ok(false, "shouldn't received message after the server sent the close frame"); + } + + ws.onclose = function(e) { + shouldCloseCleanly(e); + resolve(); + }; + }); +} + +// test15: server closes the tcp connection, but it doesn't send the close +// frame; +function test15() { + return new Promise(function(resolve, reject) { + /* + * DISABLED: see comments for test-15 case in file_websocket_wsh.py + */ + resolve(); + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-15"); + ws.onclose = function(e) { + shouldCloseNotCleanly(e); + resolve(); + } + + // termination of the connection might cause an error event if it happens in OPEN + ws.onerror = function() { + } + }); +} + +// test16: client calls close() and tries to send a message; +function test16() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-16"); + + ws.onopen = function() { + ws.close(); + ok(!ws.send("client data"), "shouldn't send message after calling close()"); + } + + ws.onmessage = function() { + ok(false, "shouldn't send message after calling close()"); + } + + ws.onerror = function() { + } + + ws.onclose = function() { + resolve(); + } + }); +} + +// test17: see bug 572975 - all event listeners set +function test17() { + return new Promise(function(resolve, reject) { + var status_test17 = "not started"; + + var test17func = function() { + var local_ws = new WebSocket("ws://sub1.test2.example.org/tests/dom/base/test/file_websocket", "test-17"); + status_test17 = "started"; + + local_ws.onopen = function(e) { + status_test17 = "opened"; + e.target.send("client data"); + forcegc(); + }; + + local_ws.onerror = function() { + ok(false, "onerror called on test " + current_test + "!"); + }; + + local_ws.onmessage = function(e) { + ok(e.data == "server data", "Bad message in test-17"); + status_test17 = "got message"; + forcegc(); + }; + + local_ws.onclose = function(e) { + ok(status_test17 == "got message", "Didn't got message in test-17!"); + shouldCloseCleanly(e); + status_test17 = "closed"; + forcegc(); + resolve(); + }; + + window._test17 = null; + forcegc(); + } + + window._test17 = test17func; + window._test17(); + }); +} + +// The tests that expects that their websockets neither open nor close MUST +// be in the end of the tests, i.e. HERE, in order to prevent blocking the other +// tests. + +// test18: client tries to connect to an http resource; +function test18() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket_http_resource.txt"); + ws.onopen = shouldNotOpen; + ws.onerror = ignoreError; + ws.onclose = function(e) + { + shouldCloseNotCleanly(e); + resolve(); + } + }); +} + +// test19: server closes the tcp connection before establishing the ws +// connection; +function test19() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-19"); + ws.onopen = shouldNotOpen; + ws.onerror = ignoreError; + ws.onclose = function(e) + { + shouldCloseNotCleanly(e); + resolve(); + } + }); +} + +// test20: see bug 572975 - only on error and onclose event listeners set +function test20() { + return new Promise(function(resolve, reject) { + var test20func = function() { + var local_ws = new WebSocket("ws://sub1.test1.example.org/tests/dom/base/test/file_websocket", "test-20"); + + local_ws.onerror = function() { + ok(false, "onerror called on test " + current_test + "!"); + } + + local_ws.onclose = function(e) { + ok(true, "test 20 closed despite gc"); + resolve(); + } + + local_ws = null; + window._test20 = null; + forcegc(); + } + + window._test20 = test20func; + window._test20(); + }); +} + +// test21: see bug 572975 - same as test 17, but delete strong event listeners +// when receiving the message event; +function test21() { + return new Promise(function(resolve, reject) { + var test21func = function() { + var local_ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-21"); + var received_message = false; + + local_ws.onopen = function(e) { + e.target.send("client data"); + forcegc(); + e.target.onopen = null; + forcegc(); + } + + local_ws.onerror = function() { + ok(false, "onerror called on test " + current_test + "!"); + } + + local_ws.onmessage = function(e) { + is(e.data, "server data", "Bad message in test-21"); + received_message = true; + forcegc(); + e.target.onmessage = null; + forcegc(); + } + + local_ws.onclose = function(e) { + shouldCloseCleanly(e); + ok(received_message, "close transitioned through onmessage"); + resolve(); + } + + local_ws = null; + window._test21 = null; + forcegc(); + } + + window._test21 = test21func; + window._test21(); + }); +} + +// test22: server takes too long to establish the ws connection; +function test22() { + return new Promise(function(resolve, reject) { + const pref_open = "network.websocket.timeout.open"; + SpecialPowers.setIntPref(pref_open, 5); + + var ws = CreateTestWS("ws://sub2.test2.example.org/tests/dom/base/test/file_websocket", "test-22"); + + ws.onopen = shouldNotOpen; + ws.onerror = ignoreError; + + ws.onclose = function(e) { + shouldCloseNotCleanly(e); + resolve(); + } + + SpecialPowers.clearUserPref(pref_open); + }); +} + +// test23: should detect WebSocket on window object; +function test23() { + return new Promise(function(resolve, reject) { + ok("WebSocket" in window, "WebSocket should be available on window object"); + resolve(); + }); +} + +// test24: server rejects sub-protocol string +function test24() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-does-not-exist"); + + ws.onopen = shouldNotOpen; + ws.onclose = function(e) { + shouldCloseNotCleanly(e); + resolve(); + } + + ws.onerror = function() { + } + }); +} + +// test25: ctor with valid empty sub-protocol array +function test25() { + return new Promise(function(resolve, reject) { + var prots=[]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + // This test errors because the server requires a sub-protocol, but + // the test just wants to ensure that the ctor doesn't generate an + // exception + ws.onerror = ignoreError; + ws.onopen = shouldNotOpen; + + ws.onclose = function(e) { + is(ws.protocol, "", "test25 subprotocol selection"); + ok(true, "test 25 protocol array close"); + resolve(); + } + }); +} + +// test26: ctor with invalid sub-protocol array containing 1 empty element +function test26() { + return new Promise(function(resolve, reject) { + var prots=[""]; + + try { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + ok(false, "testing empty element sub protocol array"); + } catch (e) { + ok(true, "testing empty sub element protocol array"); + } + + resolve(); + }); +} + +// test27: ctor with invalid sub-protocol array containing an empty element in +// list +function test27() { + return new Promise(function(resolve, reject) { + var prots=["test27", ""]; + + try { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + ok(false, "testing empty element mixed sub protocol array"); + } catch (e) { + ok(true, "testing empty element mixed sub protocol array"); + } + + resolve(); + }); +} + +// test28: ctor using valid 1 element sub-protocol array +function test28() { + return new Promise(function(resolve, reject) { + var prots=["test28"]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 28 protocol array open"); + ws.close(); + } + + ws.onclose = function(e) { + is(ws.protocol, "test28", "test28 subprotocol selection"); + ok(true, "test 28 protocol array close"); + resolve(); + } + }); +} + +// test29: ctor using all valid 5 element sub-protocol array +function test29() { + return new Promise(function(resolve, reject) { + var prots=["test29a", "test29b"]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 29 protocol array open"); + ws.close(); + } + + ws.onclose = function(e) { + ok(true, "test 29 protocol array close"); + resolve(); + } + }); +} + +// test30: ctor using valid 1 element sub-protocol array with element server +// will reject +function test30() { + return new Promise(function(resolve, reject) { + var prots=["test-does-not-exist"]; + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = shouldNotOpen; + + ws.onclose = function(e) { + shouldCloseNotCleanly(e); + resolve(); + } + + ws.onerror = function() { + } + }); +} + +// test31: ctor using valid 2 element sub-protocol array with 1 element server +// will reject and one server will accept +function test31() { + return new Promise(function(resolve, reject) { + var prots=["test-does-not-exist", "test31"]; + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 31 protocol array open"); + ws.close(); + } + + ws.onclose = function(e) { + is(ws.protocol, "test31", "test31 subprotocol selection"); + ok(true, "test 31 protocol array close"); + resolve(); + } + }); +} + +// test32: ctor using invalid sub-protocol array that contains duplicate items +function test32() { + return new Promise(function(resolve, reject) { + var prots=["test32","test32"]; + + try { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + ok(false, "testing duplicated element sub protocol array"); + } catch (e) { + ok(true, "testing duplicated sub element protocol array"); + } + + resolve(); + }); +} + +// test33: test for sending/receiving custom close code (but no close reason) +function test33() { + return new Promise(function(resolve, reject) { + var prots=["test33"]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 33 open"); + ws.close(3131); // pass code but not reason + } + + ws.onclose = function(e) { + ok(true, "test 33 close"); + shouldCloseCleanly(e); + is(e.code, 3131, "test 33 got wrong close code: " + e.code); + is(e.reason, "", "test 33 got wrong close reason: " + e.reason); + resolve(); + } + }); +} + +// test34: test for receiving custom close code and reason +function test34() { + return new Promise(function(resolve, reject) { + var prots=["test-34"]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 34 open"); + ws.close(); + } + + ws.onclose = function(e) + { + ok(true, "test 34 close"); + ok(e.wasClean, "test 34 closed cleanly"); + is(e.code, 1001, "test 34 custom server code"); + is(e.reason, "going away now", "test 34 custom server reason"); + resolve(); + } + }); +} + +// test35: test for sending custom close code and reason +function test35() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-35a"); + + ws.onopen = function(e) { + ok(true, "test 35a open"); + ws.close(3500, "my code"); + } + + ws.onclose = function(e) { + ok(true, "test 35a close"); + ok(e.wasClean, "test 35a closed cleanly"); + var wsb = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-35b"); + + wsb.onopen = function(e) { + ok(true, "test 35b open"); + wsb.close(); + } + + wsb.onclose = function(e) { + ok(true, "test 35b close"); + ok(e.wasClean, "test 35b closed cleanly"); + is(e.code, 3501, "test 35 custom server code"); + is(e.reason, "my code", "test 35 custom server reason"); + resolve(); + } + } + }); +} + +// test36: negative test for sending out of range close code +function test36() { + return new Promise(function(resolve, reject) { + var prots=["test-36"]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 36 open"); + + try { + ws.close(13200); + ok(false, "testing custom close code out of range"); + } catch (e) { + ok(true, "testing custom close code out of range"); + ws.close(3200); + } + } + + ws.onclose = function(e) { + ok(true, "test 36 close"); + ok(e.wasClean, "test 36 closed cleanly"); + resolve(); + } + }); +} + +// test37: negative test for too long of a close reason +function test37() { + return new Promise(function(resolve, reject) { + var prots=["test-37"]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 37 open"); + + try { + ws.close(3100,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123"); + ok(false, "testing custom close reason out of range"); + } catch (e) { + ok(true, "testing custom close reason out of range"); + ws.close(3100,"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012"); + } + } + + ws.onclose = function(e) { + ok(true, "test 37 close"); + ok(e.wasClean, "test 37 closed cleanly"); + + var wsb = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-37b"); + + wsb.onopen = function(e) { + // now test that a rejected close code and reason dont persist + ok(true, "test 37b open"); + try { + wsb.close(3101,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123"); + ok(false, "testing custom close reason out of range 37b"); + } catch (e) { + ok(true, "testing custom close reason out of range 37b"); + wsb.close(); + } + } + + wsb.onclose = function(e) { + ok(true, "test 37b close"); + ok(e.wasClean, "test 37b closed cleanly"); + + var wsc = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-37c"); + + wsc.onopen = function(e) { + ok(true, "test 37c open"); + wsc.close(); + } + + wsc.onclose = function(e) { + isnot(e.code, 3101, "test 37c custom server code not present"); + is(e.reason, "", "test 37c custom server reason not present"); + resolve(); + } + } + } + }); +} + +// test38: ensure extensions attribute is defined +function test38() { + return new Promise(function(resolve, reject) { + var prots=["test-38"]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 38 open"); + isnot(ws.extensions, undefined, "extensions attribute defined"); + // is(ws.extensions, "deflate-stream", "extensions attribute deflate-stream"); + ws.close(); + } + + ws.onclose = function(e) { + ok(true, "test 38 close"); + resolve(); + } + }); +} + +// test39: a basic wss:// connectivity test +function test39() { + return new Promise(function(resolve, reject) { + var prots=["test-39"]; + + var ws = CreateTestWS("wss://example.com/tests/dom/base/test/file_websocket", prots); + status_test39 = "started"; + + ws.onopen = function(e) { + status_test39 = "opened"; + ok(true, "test 39 open"); + ws.close(); + } + + ws.onclose = function(e) { + ok(true, "test 39 close"); + is(status_test39, "opened", "test 39 did open"); + resolve(); + } + }); +} + +// test40: negative test for wss:// with no cert +function test40() { + return new Promise(function(resolve, reject) { + var prots=["test-40"]; + + var ws = CreateTestWS("wss://nocert.example.com/tests/dom/base/test/file_websocket", prots); + + status_test40 = "started"; + ws.onerror = ignoreError; + + ws.onopen = function(e) { + status_test40 = "opened"; + ok(false, "test 40 open"); + ws.close(); + } + + ws.onclose = function(e) { + ok(true, "test 40 close"); + is(status_test40, "started", "test 40 did not open"); + resolve(); + } + }); +} + +// test41: HSTS +function test41() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://example.com/tests/dom/base/test/file_websocket", "test-41a", 1); + + ws.onopen = function(e) { + ok(true, "test 41a open"); + is(ws.url, "ws://example.com/tests/dom/base/test/file_websocket", + "test 41a initial ws should not be redirected"); + ws.close(); + } + + ws.onclose = function(e) { + ok(true, "test 41a close"); + + // establish a hsts policy for example.com + var wsb = CreateTestWS("wss://example.com/tests/dom/base/test/file_websocket", "test-41b", 1); + + wsb.onopen = function(e) { + ok(true, "test 41b open"); + wsb.close(); + } + + wsb.onclose = function(e) { + ok(true, "test 41b close"); + + // try ws:// again, it should be done over wss:// now due to hsts + var wsc = CreateTestWS("ws://example.com/tests/dom/base/test/file_websocket", "test-41c"); + + wsc.onopen = function(e) { + ok(true, "test 41c open"); + is(wsc.url, "wss://example.com/tests/dom/base/test/file_websocket", + "test 41c ws should be redirected by hsts to wss"); + wsc.close(); + } + + wsc.onclose = function(e) { + ok(true, "test 41c close"); + + // clean up the STS state + const Ci = SpecialPowers.Ci; + var loadContext = SpecialPowers.wrap(window) + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); + var flags = 0; + if (loadContext.usePrivateBrowsing) + flags |= Ci.nsISocketProvider.NO_PERMANENT_STORAGE; + SpecialPowers.cleanUpSTSData("http://example.com", flags); + resolve(); + } + } + } + }); +} + +// test42: non-char utf-8 sequences +function test42() { + return new Promise(function(resolve, reject) { + // test some utf-8 non-characters. They should be allowed in the + // websockets context. Test via round trip echo. + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-42"); + var data = ["U+FFFE \ufffe", + "U+FFFF \uffff", + "U+10FFFF \udbff\udfff"]; + var index = 0; + + ws.onopen = function() { + ws.send(data[0]); + ws.send(data[1]); + ws.send(data[2]); + } + + ws.onmessage = function(e) { + is(e.data, data[index], "bad received message in test-42! index="+index); + index++; + if (index == 3) { + ws.close(); + } + } + + ws.onclose = function(e) { + resolve(); + } + }); +} + +// test43: Test setting binaryType attribute +function test43() { + return new Promise(function(resolve, reject) { + var prots=["test-43"]; + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots); + + ws.onopen = function(e) { + ok(true, "test 43 open"); + // Test binaryType setting + ws.binaryType = "arraybuffer"; + ws.binaryType = "blob"; + ws.binaryType = ""; // illegal + is(ws.binaryType, "blob"); + ws.binaryType = "ArrayBuffer"; // illegal + is(ws.binaryType, "blob"); + ws.binaryType = "Blob"; // illegal + is(ws.binaryType, "blob"); + ws.binaryType = "mcfoofluu"; // illegal + is(ws.binaryType, "blob"); + ws.close(); + } + + ws.onclose = function(e) { + ok(true, "test 43 close"); + resolve(); + } + }); +} + +// test44: Test sending/receving binary ArrayBuffer +function test44() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-44"); + is(ws.readyState, 0, "bad readyState in test-44!"); + ws.binaryType = "arraybuffer"; + + ws.onopen = function() { + is(ws.readyState, 1, "open bad readyState in test-44!"); + var buf = new ArrayBuffer(3); + // create byte view + var view = new Uint8Array(buf); + view[0] = 5; + view[1] = 0; // null byte + view[2] = 7; + ws.send(buf); + } + + ws.onmessage = function(e) { + ok(e.data instanceof ArrayBuffer, "Should receive an arraybuffer!"); + var view = new Uint8Array(e.data); + ok(view.length == 2 && view[0] == 0 && view[1] ==4, "testing Reply arraybuffer" ); + ws.close(); + } + + ws.onclose = function(e) { + is(ws.readyState, 3, "onclose bad readyState in test-44!"); + shouldCloseCleanly(e); + resolve(); + } + }); +} + +// test45: Test sending/receving binary Blob +function test45() { + return new Promise(function(resolve, reject) { + function test45Real(blobFile) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-45"); + is(ws.readyState, 0, "bad readyState in test-45!"); + // ws.binaryType = "blob"; // Don't need to specify: blob is the default + + ws.onopen = function() { + is(ws.readyState, 1, "open bad readyState in test-45!"); + ws.send(blobFile); + } + + var test45blob; + + ws.onmessage = function(e) { + test45blob = e.data; + ok(test45blob instanceof Blob, "We should be receiving a Blob"); + + ws.close(); + } + + ws.onclose = function(e) { + is(ws.readyState, 3, "onclose bad readyState in test-45!"); + shouldCloseCleanly(e); + + // check blob contents + var reader = new FileReader(); + reader.onload = function(event) { + is(reader.result, "flob", "response should be 'flob': got '" + + reader.result + "'"); + } + + reader.onerror = function(event) { + testFailed("Failed to read blob: error code = " + reader.error.code); + } + + reader.onloadend = function(event) { + resolve(); + } + + reader.readAsBinaryString(test45blob); + } + } + + SpecialPowers.createFiles([{name: "testBlobFile", data: "flob"}], + function(files) { + test45Real(files[0]); + }, + function(msg) { + testFailed("Failed to create file for test45: " + msg); + resolve(); + }); + }); +} + +// test46: Test that we don't dispatch incoming msgs once in CLOSING state +function test46() { + return new Promise(function(resolve, reject) { + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-46"); + is(ws.readyState, 0, "create bad readyState in test-46!"); + + ws.onopen = function() { + is(ws.readyState, 1, "open bad readyState in test-46!"); + ws.close() + is(ws.readyState, 2, "close must set readyState to 2 in test-46!"); + } + + ws.onmessage = function(e) { + ok(false, "received message after calling close in test-46!"); + } + + ws.onclose = function(e) { + is(ws.readyState, 3, "onclose bad readyState in test-46!"); + shouldCloseCleanly(e); + resolve(); + } + }); +} + +// test47: Make sure onerror/onclose aren't called during close() +function test47() { + return new Promise(function(resolve, reject) { + var hasError = false; + var ws = CreateTestWS("ws://another.websocket.server.that.probably.does.not.exist"); + + ws.onopen = shouldNotOpen; + + ws.onerror = function (e) { + is(ws.readyState, 3, "test-47: readyState should be CLOSED(3) in onerror: got " + + ws.readyState); + ok(!ws._withinClose, "onerror() called during close()!"); + hasError = true; + } + + ws.onclose = function(e) { + shouldCloseNotCleanly(e); + ok(hasError, "test-47: should have called onerror before onclose"); + is(ws.readyState, 3, "test-47: readyState should be CLOSED(3) in onclose: got " + + ws.readyState); + ok(!ws._withinClose, "onclose() called during close()!"); + is(e.code, 1006, "test-47 close code should be 1006 but is:" + e.code); + resolve(); + } + + // Call close before we're connected: throws error + // Make sure we call onerror/onclose asynchronously + ws._withinClose = 1; + ws.close(3333, "Closed before we were open: error"); + ws._withinClose = 0; + is(ws.readyState, 2, "test-47: readyState should be CLOSING(2) after close(): got " + + ws.readyState); + }); +} + +// test48: see bug 1227136 - client calls close() from onopen() and waits until +// WebSocketChannel::mSocketIn is nulled out on socket thread. +function test48() { + return new Promise(function(resolve, reject) { + const pref_close = "network.websocket.timeout.close"; + SpecialPowers.setIntPref(pref_close, 1); + + var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-48"); + + ws.onopen = function() { + ws.close(); + + var date = new Date(); + var curDate = null; + do { + curDate = new Date(); + } while(curDate-date < 1500); + + } + + ws.onclose = function(e) { + ok(true, "ws close in test 48"); + resolve(); + } + + SpecialPowers.clearUserPref(pref_close); + }); +} diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h index 5cd8e761eb..78c611be11 100644 --- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -50,11 +50,19 @@ protected: struct FastDictionaryInitializer { }; + bool mIsAnyMemberPresent = false; + private: // aString is expected to actually be an nsAString*. Should only be // called from StringifyToJSON. static bool AppendJSONToString(const char16_t* aJSONData, uint32_t aDataLength, void* aString); + +public: + bool IsAnyMemberPresent() const + { + return mIsAnyMemberPresent; + } }; // Struct that serves as a base class for all typed arrays and array buffers and diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index dd9a86003a..f615b1753d 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1803,6 +1803,21 @@ NativePropertyHooks sEmptyNativePropertyHooks = { nullptr }; +const js::ClassOps sBoringInterfaceObjectClassClassOps = { + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* mayResolve */ + nullptr, /* finalize */ + ThrowingConstructor, /* call */ + InterfaceHasInstance, /* hasInstance */ + ThrowingConstructor, /* construct */ + nullptr, /* trace */ +}; + const js::ObjectOps sInterfaceObjectClassObjectOps = { nullptr, /* lookupProperty */ nullptr, /* defineProperty */ diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 608cdc7f05..59eb1c397f 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -2508,6 +2508,8 @@ XrayGetNativeProto(JSContext* cx, JS::Handle obj, extern NativePropertyHooks sEmptyNativePropertyHooks; +extern const js::ClassOps sBoringInterfaceObjectClassClassOps; + extern const js::ObjectOps sInterfaceObjectClassObjectOps; // We use one constructor JSNative to represent all DOM interface objects (so @@ -2540,7 +2542,7 @@ inline bool HasConstructor(JSObject* obj) { return JS_IsNativeFunction(obj, Constructor) || - js::GetObjectClass(obj)->construct; + js::GetObjectClass(obj)->getConstruct(); } #endif diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index ad545cd7f7..c3f57fba59 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -983,7 +983,10 @@ DOMInterfaces = { }, 'Request': { - 'binaryNames': { 'headers': 'headers_' }, + 'binaryNames': { + 'headers': 'headers_', + 'referrerPolicy': 'referrerPolicy_' + }, }, 'Response': { diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 91da061179..476fe04511 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -440,27 +440,32 @@ class CGDOMJSClass(CGThing): return fill( """ + static const js::ClassOps sClassOps = { + ${addProperty}, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + ${enumerate}, /* enumerate */ + ${resolve}, /* resolve */ + ${mayResolve}, /* mayResolve */ + ${finalize}, /* finalize */ + ${call}, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + ${trace}, /* trace */ + }; + + static const js::ClassExtension sClassExtension = { + nullptr, /* weakmapKeyDelegateOp */ + ${objectMoved} /* objectMovedOp */ + }; + static const DOMJSClass sClass = { { "${name}", ${flags}, - ${addProperty}, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - ${enumerate}, /* enumerate */ - ${resolve}, /* resolve */ - ${mayResolve}, /* mayResolve */ - ${finalize}, /* finalize */ - ${call}, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - ${trace}, /* trace */ + &sClassOps, JS_NULL_CLASS_SPEC, - { - false, /* isWrappedNative */ - nullptr, /* weakmapKeyDelegateOp */ - ${objectMoved} /* objectMovedOp */ - }, + &sClassExtension, JS_NULL_OBJECT_OPS }, $*{descriptor} @@ -507,11 +512,14 @@ class CGDOMProxyJSClass(CGThing): objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr' return fill( """ + static const js::ClassExtension sClassExtension = PROXY_MAKE_EXT( + ${objectMoved} + ); + static const DOMJSClass sClass = { PROXY_CLASS_WITH_EXT("${name}", ${flags}, - PROXY_MAKE_EXT(false, /* isWrappedNative */ - ${objectMoved})), + &sClassExtension), $*{descriptor} }; """, @@ -590,18 +598,7 @@ class CGPrototypeJSClass(CGThing): { "${name}Prototype", JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - nullptr, /* finalize */ - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - nullptr, /* trace */ + JS_NULL_CLASS_OPS, JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, JS_NULL_OBJECT_OPS @@ -680,24 +677,39 @@ class CGInterfaceObjectJSClass(CGThing): len(self.descriptor.interface.namedConstructors)) (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor) - return fill( + if ctorname == "ThrowingConstructor" and hasinstance == "InterfaceHasInstance": + ret = "" + classOpsPtr = "&sBoringInterfaceObjectClassClassOps" + else: + ret = fill( + """ + static const js::ClassOps sInterfaceObjectClassOps = { + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* mayResolve */ + nullptr, /* finalize */ + ${ctorname}, /* call */ + ${hasInstance}, /* hasInstance */ + ${ctorname}, /* construct */ + nullptr, /* trace */ + }; + + """, + ctorname=ctorname, + hasInstance=hasinstance) + classOpsPtr = "&sInterfaceObjectClassOps" + + ret = ret + fill( """ static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = { { "Function", JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - nullptr, /* finalize */ - ${ctorname}, /* call */ - ${hasInstance}, /* hasInstance */ - ${ctorname}, /* construct */ - nullptr, /* trace */ + ${classOpsPtr}, JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, &sInterfaceObjectClassObjectOps @@ -713,12 +725,13 @@ class CGInterfaceObjectJSClass(CGThing): slotCount=slotCount, ctorname=ctorname, hasInstance=hasinstance, + classOpsPtr=classOpsPtr, hooks=NativePropertyHooks(self.descriptor), name=self.descriptor.interface.identifier.name, prototypeID=prototypeID, depth=depth, protoGetter=protoGetter) - + return ret class CGList(CGThing): """ @@ -12543,6 +12556,10 @@ class CGDictionary(CGThing): "convert": string.Template(conversionInfo.template).substitute(replacements), "propGet": propGet } + # The conversion code will only run where a default value or a value passed + # by the author needs to get converted, so we can remember if we have any + # members present here. + conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n" conversion = ("if (!isNull && !${propGet}) {\n" " return false;\n" "}\n") diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg index 5c599a8c40..ab53058065 100644 --- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -61,6 +61,7 @@ MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, JSEXN_TYPEERR, "Permission denied MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, JSEXN_TYPEERR, "Missing required {0}.") MSG_DEF(MSG_INVALID_REQUEST_METHOD, 1, JSEXN_TYPEERR, "Invalid request method {0}.") MSG_DEF(MSG_INVALID_REQUEST_MODE, 1, JSEXN_TYPEERR, "Invalid request mode {0}.") +MSG_DEF(MSG_INVALID_REFERRER_URL, 1, JSEXN_TYPEERR, "Invalid referrer URL {0}.") MSG_DEF(MSG_FETCH_BODY_CONSUMED_ERROR, 0, JSEXN_TYPEERR, "Body has already been consumed.") MSG_DEF(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR, 0, JSEXN_TYPEERR, "Response statusText may not contain newline or carriage return.") MSG_DEF(MSG_FETCH_FAILED, 0, JSEXN_TYPEERR, "NetworkError when attempting to fetch resource.") diff --git a/dom/bindings/SimpleGlobalObject.cpp b/dom/bindings/SimpleGlobalObject.cpp index 2968da44ab..9019ade2f1 100644 --- a/dom/bindings/SimpleGlobalObject.cpp +++ b/dom/bindings/SimpleGlobalObject.cpp @@ -70,9 +70,7 @@ SimpleGlobal_moved(JSObject *obj, const JSObject *old) globalObject->UpdateWrapper(obj, old); } -const js::Class SimpleGlobalClass = { - "", - JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS, +static const js::ClassOps SimpleGlobalClassOps = { nullptr, nullptr, nullptr, @@ -85,11 +83,20 @@ const js::Class SimpleGlobalClass = { nullptr, nullptr, JS_GlobalObjectTraceHook, - JS_NULL_CLASS_SPEC, { - false, - nullptr, - SimpleGlobal_moved - }, JS_NULL_OBJECT_OPS +}; + +static const js::ClassExtension SimpleGlobalClassExtension = { + nullptr, + SimpleGlobal_moved +}; + +const js::Class SimpleGlobalClass = { + "", + JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS, + &SimpleGlobalClassOps, + JS_NULL_CLASS_SPEC, + &SimpleGlobalClassExtension, + JS_NULL_OBJECT_OPS }; // static diff --git a/dom/bindings/test/chrome.ini b/dom/bindings/test/chrome.ini index 0f6db92748..ebf8ae9062 100644 --- a/dom/bindings/test/chrome.ini +++ b/dom/bindings/test/chrome.ini @@ -1,5 +1,11 @@ [DEFAULT] skip-if = buildapp == 'b2g' +support-files = + !/dom/bindings/test/file_bug707564.html + !/dom/bindings/test/file_bug775543.html + !/dom/bindings/test/file_document_location_set_via_xray.html + !/dom/bindings/test/file_dom_xrays.html + !/dom/bindings/test/file_proxies_via_xray.html [test_bug707564-chrome.html] [test_bug775543.html] diff --git a/dom/bindings/test/mochitest.ini b/dom/bindings/test/mochitest.ini index a83eeebe08..6e8e57f03a 100644 --- a/dom/bindings/test/mochitest.ini +++ b/dom/bindings/test/mochitest.ini @@ -8,6 +8,7 @@ support-files = file_dom_xrays.html file_proxies_via_xray.html forOf_iframe.html + !/js/xpconnect/tests/mochitest/file_empty.html [test_async_stacks.html] [test_ByteString.html] diff --git a/dom/browser-element/mochitest/priority/mochitest.ini b/dom/browser-element/mochitest/priority/mochitest.ini index 980bbd1a94..c143dde51a 100644 --- a/dom/browser-element/mochitest/priority/mochitest.ini +++ b/dom/browser-element/mochitest/priority/mochitest.ini @@ -1,6 +1,15 @@ [DEFAULT] # Good luck running these tests on anything but desktop Linux. -skip-if = toolkit != "gtk2" || ((buildapp =='mulet' || buildapp == 'b2g') && (toolkit != 'gonk' || debug)) || e10s +run-if = os == 'linux' && buildapp == 'browser' && !e10s +support-files = + file_Audio.html + file_HighPriority.html + file_MultipleFrames.html + file_NestedFramesOuter.html + file_WebGLContextLost.html + silence.ogg + !/dom/browser-element/mochitest/browserElementTestHelpers.js + !/dom/browser-element/mochitest/file_empty.html # Note: ../browserElementTestHelpers.js makes all tests in this directory OOP, # because testing the process-priority manager without OOP frames does not make @@ -9,22 +18,17 @@ skip-if = toolkit != "gtk2" || ((buildapp =='mulet' || buildapp == 'b2g') && (to [test_Simple.html] [test_Visibility.html] [test_HighPriority.html] -support-files = file_HighPriority.html [test_Background.html] [test_BackgroundLRU.html] [test_Activity.html] [test_Audio.html] -support-files = file_Audio.html silence.ogg [test_Keyboard.html] [test_MultipleActivities.html] [test_MultipleFrames.html] -support-files = file_MultipleFrames.html [test_Preallocated.html] disabled = bug 968604, bug 987164 [test_ExpectingSystemMessage.html] [test_ExpectingSystemMessage2.html] [test_NestedFrames.html] -support-files = file_NestedFramesOuter.html [test_WebGLContextLost.html] disabled = bug 865844 -support-files = file_WebGLContextLost.html diff --git a/dom/cache/CacheTypes.ipdlh b/dom/cache/CacheTypes.ipdlh index 1f2f576f1a..ba627b8197 100644 --- a/dom/cache/CacheTypes.ipdlh +++ b/dom/cache/CacheTypes.ipdlh @@ -10,6 +10,7 @@ include ChannelInfo; include PBackgroundSharedTypes; using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h"; +using ReferrerPolicy from "mozilla/dom/cache/IPCUtils.h"; using RequestCredentials from "mozilla/dom/cache/IPCUtils.h"; using RequestMode from "mozilla/dom/cache/IPCUtils.h"; using RequestCache from "mozilla/dom/cache/IPCUtils.h"; @@ -60,6 +61,7 @@ struct CacheRequest HeadersEntry[] headers; HeadersGuardEnum headersGuard; nsString referrer; + ReferrerPolicy referrerPolicy; RequestMode mode; RequestCredentials credentials; CacheReadStreamOrVoid body; diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 22a1e1ac07..92ec657b29 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -37,7 +37,7 @@ const int32_t kFirstShippedSchemaVersion = 15; namespace { // Update this whenever the DB schema is changed. -const int32_t kLatestSchemaVersion = 19; +const int32_t kLatestSchemaVersion = 20; // --------- // The following constants define the SQL schema. These are defined in the @@ -104,9 +104,10 @@ const char* const kTableEntries = "response_principal_info TEXT NOT NULL, " "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, " + "request_redirect INTEGER NOT NULL, " + "request_referrer_policy INTEGER NOT NULL" // New columns must be added at the end of table to migrate and // validate properly. - "request_redirect INTEGER NOT NULL" ")"; // Create an index to support the QueryCache() matching algorithm. This @@ -189,6 +190,14 @@ static_assert(int(HeadersGuardEnum::None) == 0 && int(HeadersGuardEnum::Immutable) == 4 && int(HeadersGuardEnum::EndGuard_) == 5, "HeadersGuardEnum values are as expected"); +static_assert(int(ReferrerPolicy::_empty) == 0 && + int(ReferrerPolicy::No_referrer) == 1 && + int(ReferrerPolicy::No_referrer_when_downgrade) == 2 && + int(ReferrerPolicy::Origin) == 3 && + int(ReferrerPolicy::Origin_when_cross_origin) == 4 && + int(ReferrerPolicy::Unsafe_url) == 5 && + int(ReferrerPolicy::EndGuard_) == 6, + "ReferrerPolicy values are as expected"); static_assert(int(RequestMode::Same_origin) == 0 && int(RequestMode::No_cors) == 1 && int(RequestMode::Cors) == 2 && @@ -1635,6 +1644,7 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, "request_url_query, " "request_url_query_hash, " "request_referrer, " + "request_referrer_policy, " "request_headers_guard, " "request_mode, " "request_credentials, " @@ -1658,6 +1668,7 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, ":request_url_query, " ":request_url_query_hash, " ":request_referrer, " + ":request_referrer_policy, " ":request_headers_guard, " ":request_mode, " ":request_credentials, " @@ -1710,6 +1721,10 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, aRequest.referrer()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_referrer_policy"), + static_cast(aRequest.referrerPolicy())); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_headers_guard"), static_cast(aRequest.headersGuard())); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -1988,6 +2003,7 @@ ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId, "request_url_no_query, " "request_url_query, " "request_referrer, " + "request_referrer_policy, " "request_headers_guard, " "request_mode, " "request_credentials, " @@ -2019,48 +2035,54 @@ ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId, rv = state->GetString(3, aSavedRequestOut->mValue.referrer()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + int32_t referrerPolicy; + rv = state->GetInt32(4, &referrerPolicy); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + aSavedRequestOut->mValue.referrerPolicy() = + static_cast(referrerPolicy); + int32_t guard; - rv = state->GetInt32(4, &guard); + rv = state->GetInt32(5, &guard); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.headersGuard() = static_cast(guard); int32_t mode; - rv = state->GetInt32(5, &mode); + rv = state->GetInt32(6, &mode); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.mode() = static_cast(mode); int32_t credentials; - rv = state->GetInt32(6, &credentials); + rv = state->GetInt32(7, &credentials); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.credentials() = static_cast(credentials); int32_t requestContentPolicyType; - rv = state->GetInt32(7, &requestContentPolicyType); + rv = state->GetInt32(8, &requestContentPolicyType); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.contentPolicyType() = static_cast(requestContentPolicyType); int32_t requestCache; - rv = state->GetInt32(8, &requestCache); + rv = state->GetInt32(9, &requestCache); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.requestCache() = static_cast(requestCache); int32_t requestRedirect; - rv = state->GetInt32(9, &requestRedirect); + rv = state->GetInt32(10, &requestRedirect); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mValue.requestRedirect() = static_cast(requestRedirect); bool nullBody = false; - rv = state->GetIsNull(10, &nullBody); + rv = state->GetIsNull(11, &nullBody); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mHasBodyId = !nullBody; if (aSavedRequestOut->mHasBodyId) { - rv = ExtractId(state, 10, &aSavedRequestOut->mBodyId); + rv = ExtractId(state, 11, &aSavedRequestOut->mBodyId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } @@ -2396,10 +2418,10 @@ Validate(mozIStorageConnection* aConn) // Schema migration code // ----- -typedef nsresult (*MigrationFunc)(mozIStorageConnection*); +typedef nsresult (*MigrationFunc)(mozIStorageConnection*, bool&); struct Migration { - Migration(int32_t aFromVersion, MigrationFunc aFunc) + MOZ_CONSTEXPR Migration(int32_t aFromVersion, MigrationFunc aFunc) : mFromVersion(aFromVersion) , mFunc(aFunc) { } @@ -2409,10 +2431,11 @@ struct Migration // Declare migration functions here. Each function should upgrade // the version by a single increment. Don't skip versions. -nsresult MigrateFrom15To16(mozIStorageConnection* aConn); -nsresult MigrateFrom16To17(mozIStorageConnection* aConn); -nsresult MigrateFrom17To18(mozIStorageConnection* aConn); -nsresult MigrateFrom18To19(mozIStorageConnection* aConn); +nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema); +nsresult MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema); +nsresult MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema); +nsresult MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema); +nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema); // Configure migration functions to run for the given starting version. Migration sMigrationList[] = { @@ -2420,45 +2443,11 @@ Migration sMigrationList[] = { Migration(16, MigrateFrom16To17), Migration(17, MigrateFrom17To18), Migration(18, MigrateFrom18To19), + Migration(19, MigrateFrom19To20), }; uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration); -nsresult -Migrate(mozIStorageConnection* aConn) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(aConn); - - int32_t currentVersion = 0; - nsresult rv = aConn->GetSchemaVersion(¤tVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - while (currentVersion < kLatestSchemaVersion) { - // Wiping old databases is handled in DBAction because it requires - // making a whole new mozIStorageConnection. Make sure we don't - // accidentally get here for one of those old databases. - MOZ_ASSERT(currentVersion >= kFirstShippedSchemaVersion); - - for (uint32_t i = 0; i < sMigrationListLength; ++i) { - if (sMigrationList[i].mFromVersion == currentVersion) { - rv = sMigrationList[i].mFunc(aConn); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - break; - } - } - - DebugOnly lastVersion = currentVersion; - rv = aConn->GetSchemaVersion(¤tVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - MOZ_ASSERT(currentVersion > lastVersion); - } - - MOZ_ASSERT(currentVersion == kLatestSchemaVersion); - - return rv; -} - nsresult RewriteEntriesSchema(mozIStorageConnection* aConn) { @@ -2488,7 +2477,55 @@ RewriteEntriesSchema(mozIStorageConnection* aConn) return rv; } -nsresult MigrateFrom15To16(mozIStorageConnection* aConn) +nsresult +Migrate(mozIStorageConnection* aConn) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aConn); + + int32_t currentVersion = 0; + nsresult rv = aConn->GetSchemaVersion(¤tVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + bool rewriteSchema = false; + + while (currentVersion < kLatestSchemaVersion) { + // Wiping old databases is handled in DBAction because it requires + // making a whole new mozIStorageConnection. Make sure we don't + // accidentally get here for one of those old databases. + MOZ_ASSERT(currentVersion >= kFirstShippedSchemaVersion); + + for (uint32_t i = 0; i < sMigrationListLength; ++i) { + if (sMigrationList[i].mFromVersion == currentVersion) { + bool shouldRewrite = false; + rv = sMigrationList[i].mFunc(aConn, shouldRewrite); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + if (shouldRewrite) { + rewriteSchema = true; + } + break; + } + } + + DebugOnly lastVersion = currentVersion; + rv = aConn->GetSchemaVersion(¤tVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + MOZ_ASSERT(currentVersion > lastVersion); + } + + MOZ_ASSERT(currentVersion == kLatestSchemaVersion); + + if (rewriteSchema) { + // Now overwrite the master SQL for the entries table to remove the column + // default value. This is also necessary for our Validate() method to + // pass on this database. + rv = RewriteEntriesSchema(aConn); + } + + return rv; +} + +nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aConn); @@ -2507,20 +2544,16 @@ nsresult MigrateFrom15To16(mozIStorageConnection* aConn) )); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // Now overwrite the master SQL for the entries table to remove the column - // default value. This is also necessary for our Validate() method to - // pass on this database. - rv = RewriteEntriesSchema(aConn); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = aConn->SetSchemaVersion(16); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + aRewriteSchema = true; + return rv; } nsresult -MigrateFrom16To17(mozIStorageConnection* aConn) +MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aConn); @@ -2645,12 +2678,6 @@ MigrateFrom16To17(mozIStorageConnection* aConn) if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (NS_WARN_IF(hasMoreData)) { return NS_ERROR_FAILURE; } - // Finally, rewrite the schema for the entries database, otherwise the - // returned SQL string from sqlite will wrap the name of the table in quotes, - // breaking the checks in Validate(). - rv = RewriteEntriesSchema(aConn); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = aConn->SetSchemaVersion(17); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -2658,7 +2685,7 @@ MigrateFrom16To17(mozIStorageConnection* aConn) } nsresult -MigrateFrom17To18(mozIStorageConnection* aConn) +MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aConn); @@ -2688,7 +2715,7 @@ MigrateFrom17To18(mozIStorageConnection* aConn) } nsresult -MigrateFrom18To19(mozIStorageConnection* aConn) +MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aConn); @@ -2719,6 +2746,33 @@ MigrateFrom18To19(mozIStorageConnection* aConn) return rv; } +nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aConn); + + mozStorageTransaction trans(aConn, true, + mozIStorageConnection::TRANSACTION_IMMEDIATE); + + // Add the request_referrer_policy column with a default value of + // "no-referrer-when-downgrade". Note, we only use a default value here + // because its required by ALTER TABLE and we need to apply the default + // "no-referrer-when-downgrade" to existing records in the table. We don't + // actually want to keep the default in the schema for future INSERTs. + nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE entries " + "ADD COLUMN request_referrer_policy INTEGER NOT NULL DEFAULT 2" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = aConn->SetSchemaVersion(20); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + aRewriteSchema = true; + + return rv; +} + } // anonymous namespace diff --git a/dom/cache/IPCUtils.h b/dom/cache/IPCUtils.h index 6fe6e0b17d..8921cbf512 100644 --- a/dom/cache/IPCUtils.h +++ b/dom/cache/IPCUtils.h @@ -24,6 +24,11 @@ namespace IPC { mozilla::dom::HeadersGuardEnum::None, mozilla::dom::HeadersGuardEnum::EndGuard_> {}; template<> + struct ParamTraits : + public ContiguousEnumSerializer {}; + template<> struct ParamTraits : public ContiguousEnumSerializerGetReferrer(aOut.referrer()); + aOut.referrerPolicy() = aIn->ReferrerPolicy_(); RefPtr headers = aIn->Headers(); MOZ_ASSERT(headers); @@ -340,6 +341,7 @@ TypeUtils::ToInternalRequest(const CacheRequest& aIn) internalRequest->SetURL(url); internalRequest->SetReferrer(aIn.referrer()); + internalRequest->SetReferrerPolicy(aIn.referrerPolicy()); internalRequest->SetMode(aIn.mode()); internalRequest->SetCredentialsMode(aIn.credentials()); internalRequest->SetContentPolicyType(aIn.contentPolicyType()); diff --git a/dom/cache/test/xpcshell/test_migration.js b/dom/cache/test/xpcshell/test_migration.js index 31acc4cb7b..fd81afae00 100644 --- a/dom/cache/test/xpcshell/test_migration.js +++ b/dom/cache/test/xpcshell/test_migration.js @@ -22,6 +22,7 @@ function run_test() { ok(request.redirect === 'follow', 'request.redirect should default to "follow"'); ok(request.cache === 'default', 'request.cache should have been updated to "default"' + request.cache); ok(request.mode === 'navigate', 'request.mode should have been updated to "navigate"'); + ok(request.referrerPolicy === 'no-referrer-when-downgrade', 'request.referrerPolicy should have been updated to "no-referrer-when-downgrade"'); }); return Promise.all(requestList.map(function(request) { return cache.match(request); diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index 198a1c471a..3f7f901d66 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -13,6 +13,7 @@ support-files = window_bug493251.html window_bug659071.html window_wheel_default_action.html + !/gfx/layers/apz/test/mochitest/apz_test_utils.js [test_accel_virtual_modifier.html] [test_addEventListenerExtraArg.html] diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index b94db5b560..c35a2c27df 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -154,6 +154,14 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, return nullptr; } + // Double check that we have chrome privileges if the Request's content + // policy type has been overridden. Note, we must do this before + // entering the global below. Otherwise the IsCallerChrome() will + // always fail. + MOZ_ASSERT_IF(aInput.IsRequest() && + aInput.GetAsRequest().IsContentPolicyTypeOverridden(), + nsContentUtils::IsCallerChrome()); + AutoJSAPI jsapi; jsapi.Init(aGlobal); JSContext* cx = jsapi.cx(); diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index d773cf4037..3b2c994365 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -42,7 +42,7 @@ namespace mozilla { namespace dom { NS_IMPL_ISUPPORTS(FetchDriver, - nsIStreamListener, nsIInterfaceRequestor, + nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor, nsIThreadRetargetableStreamListener) FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal, @@ -129,6 +129,15 @@ FetchDriver::HttpFetch() return NS_ERROR_DOM_BAD_URI; } + // non-GET requests aren't allowed for blob. + if (IsBlobURI(uri)) { + nsAutoCString method; + mRequest->GetMethod(method); + if (!method.EqualsLiteral("GET")) { + return NS_ERROR_DOM_NETWORK_ERR; + } + } + // Step 2 deals with letting ServiceWorkers intercept requests. This is // handled by Necko after the channel is opened. // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be @@ -221,11 +230,16 @@ FetchDriver::HttpFetch() mLoadGroup = nullptr; - // FIXME(nsm): Bug 1120715. - // Step 3.4 "If request's cache mode is default and request's header list - // contains a header named `If-Modified-Since`, `If-None-Match`, - // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode - // to no-store." + // Insert ourselves into the notification callbacks chain so we can set + // headers on redirects. +#ifdef DEBUG + { + nsCOMPtr notificationCallbacks; + chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks)); + MOZ_ASSERT(!notificationCallbacks); + } +#endif + chan->SetNotificationCallbacks(this); // Step 3.5 begins "HTTP network or cache fetch". // HTTP network or cache fetch @@ -240,33 +254,41 @@ FetchDriver::HttpFetch() NS_ENSURE_SUCCESS(rv, rv); // Set the same headers. - AutoTArray headers; - mRequest->Headers()->GetEntries(headers); - bool hasAccept = false; - for (uint32_t i = 0; i < headers.Length(); ++i) { - if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) { - hasAccept = true; - } - if (headers[i].mValue.IsEmpty()) { - httpChan->SetEmptyRequestHeader(headers[i].mName); - } else { - httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */); - } - } - - if (!hasAccept) { - httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"), - NS_LITERAL_CSTRING("*/*"), - false /* merge */); - } + SetRequestHeaders(httpChan); // Step 2. Set the referrer. nsAutoString referrer; mRequest->GetReferrer(referrer); + ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_(); + net::ReferrerPolicy net_referrerPolicy = net::RP_Unset; + switch (referrerPolicy) { + case ReferrerPolicy::_empty: + net_referrerPolicy = net::RP_Default; + break; + case ReferrerPolicy::No_referrer: + net_referrerPolicy = net::RP_No_Referrer; + break; + case ReferrerPolicy::No_referrer_when_downgrade: + net_referrerPolicy = net::RP_No_Referrer_When_Downgrade; + break; + case ReferrerPolicy::Origin: + net_referrerPolicy = net::RP_Origin; + break; + case ReferrerPolicy::Origin_when_cross_origin: + net_referrerPolicy = net::RP_Origin_When_Crossorigin; + break; + case ReferrerPolicy::Unsafe_url: + net_referrerPolicy = net::RP_Unsafe_URL; + break; + default: + MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?"); + break; + } if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) { rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, mDocument, - httpChan); + httpChan, + net_referrerPolicy); NS_ENSURE_SUCCESS(rv, rv); } else if (referrer.IsEmpty()) { rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer); @@ -275,32 +297,20 @@ FetchDriver::HttpFetch() // From "Determine request's Referrer" step 3 // "If request's referrer is a URL, let referrerSource be request's // referrer." - // - // XXXnsm - We never actually hit this from a fetch() call since both - // fetch and Request() create a new internal request whose referrer is - // always set to about:client. Should we just crash here instead until - // someone tries to use FetchDriver for non-fetch() APIs? nsCOMPtr referrerURI; rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr); NS_ENSURE_SUCCESS(rv, rv); + uint32_t documentReferrerPolicy = mDocument ? mDocument->GetReferrerPolicy() : + net::RP_Default; rv = httpChan->SetReferrerWithPolicy(referrerURI, - mDocument ? mDocument->GetReferrerPolicy() : - net::RP_Default); + referrerPolicy == ReferrerPolicy::_empty ? + documentReferrerPolicy : + net_referrerPolicy); NS_ENSURE_SUCCESS(rv, rv); } - // Step 3 "If HTTPRequest's force Origin header flag is set..." - if (mRequest->ForceOriginHeader()) { - nsAutoString origin; - rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin); - NS_ENSURE_SUCCESS(rv, rv); - - httpChan->SetRequestHeader(NS_LITERAL_CSTRING("origin"), - NS_ConvertUTF16toUTF8(origin), - false /* merge */); - } // Bug 1120722 - Authorization will be handled later. // Auth may require prompting, we don't support it yet. // The next patch in this same bug prevents this from aborting the request. @@ -312,12 +322,11 @@ FetchDriver::HttpFetch() // dom/workers/ServiceWorkerManager.cpp internalChan->SetCorsMode(static_cast(mRequest->Mode())); internalChan->SetRedirectMode(static_cast(mRequest->GetRedirectMode())); + mRequest->MaybeSkipCacheIfPerformingRevalidation(); internalChan->SetFetchCacheMode(static_cast(mRequest->GetCacheMode())); } // Step 5. Proxy authentication will be handled by Necko. - // FIXME(nsm): Bug 1120715. - // Step 7-10. "If request's cache mode is neither no-store nor reload..." // Continue setting up 'HTTPRequest'. Content-Type and body data. nsCOMPtr uploadChan = do_QueryInterface(chan); @@ -401,7 +410,7 @@ FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse, return filteredResponse.forget(); } -nsresult +void FetchDriver::FailWithNetworkError() { workers::AssertIsOnMainThread(); @@ -412,7 +421,6 @@ FetchDriver::FailWithNetworkError() mObserver->OnResponseEnd(); mObserver = nullptr; } - return NS_OK; } namespace { @@ -459,7 +467,7 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, nsresult rv; aRequest->GetStatus(&rv); - if (NS_WARN_IF(NS_FAILED(rv))) { + if (NS_FAILED(rv)) { FailWithNetworkError(); return rv; } @@ -662,6 +670,21 @@ FetchDriver::OnStopRequest(nsIRequest* aRequest, return NS_OK; } +NS_IMETHODIMP +FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel, + nsIChannel* aNewChannel, + uint32_t aFlags, + nsIAsyncVerifyRedirectCallback *aCallback) +{ + nsCOMPtr httpChannel = do_QueryInterface(aNewChannel); + if (httpChannel) { + SetRequestHeaders(httpChannel); + } + + aCallback->OnRedirectVerifyCallback(NS_OK); + return NS_OK; +} + NS_IMETHODIMP FetchDriver::CheckListenerChain() { @@ -671,6 +694,11 @@ FetchDriver::CheckListenerChain() NS_IMETHODIMP FetchDriver::GetInterface(const nsIID& aIID, void **aResult) { + if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { + *aResult = static_cast(this); + NS_ADDREF_THIS(); + return NS_OK; + } if (aIID.Equals(NS_GET_IID(nsIStreamListener))) { *aResult = static_cast(this); NS_ADDREF_THIS(); @@ -693,5 +721,40 @@ FetchDriver::SetDocument(nsIDocument* aDocument) mDocument = aDocument; } +void +FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const +{ + MOZ_ASSERT(aChannel); + + AutoTArray headers; + mRequest->Headers()->GetEntries(headers); + bool hasAccept = false; + for (uint32_t i = 0; i < headers.Length(); ++i) { + if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) { + hasAccept = true; + } + if (headers[i].mValue.IsEmpty()) { + aChannel->SetEmptyRequestHeader(headers[i].mName); + } else { + aChannel->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */); + } + } + + if (!hasAccept) { + aChannel->SetRequestHeader(NS_LITERAL_CSTRING("accept"), + NS_LITERAL_CSTRING("*/*"), + false /* merge */); + } + + if (mRequest->ForceOriginHeader()) { + nsAutoString origin; + if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) { + aChannel->SetRequestHeader(NS_LITERAL_CSTRING("origin"), + NS_ConvertUTF16toUTF8(origin), + false /* merge */); + } + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/fetch/FetchDriver.h b/dom/fetch/FetchDriver.h index 1652192c25..4e7ea1defb 100644 --- a/dom/fetch/FetchDriver.h +++ b/dom/fetch/FetchDriver.h @@ -55,6 +55,7 @@ private: }; class FetchDriver final : public nsIStreamListener, + public nsIChannelEventSink, public nsIInterfaceRequestor, public nsIThreadRetargetableStreamListener { @@ -62,6 +63,7 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER + NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER @@ -98,7 +100,9 @@ private: bool aFoundOpaqueRedirect); // Utility since not all cases need to do any post processing of the filtered // response. - nsresult FailWithNetworkError(); + void FailWithNetworkError(); + + void SetRequestHeaders(nsIHttpChannel* aChannel) const; }; } // namespace dom diff --git a/dom/fetch/InternalHeaders.cpp b/dom/fetch/InternalHeaders.cpp index e8d1a76e7d..e2d9a2ddd6 100644 --- a/dom/fetch/InternalHeaders.cpp +++ b/dom/fetch/InternalHeaders.cpp @@ -174,6 +174,17 @@ InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValu nsContentUtils::IsAllowedNonCorsContentType(aValue)); } +// static +bool +InternalHeaders::IsRevalidationHeader(const nsACString& aName) +{ + return aName.EqualsLiteral("if-modified-since") || + aName.EqualsLiteral("if-none-match") || + aName.EqualsLiteral("if-unmodified-since") || + aName.EqualsLiteral("if-match") || + aName.EqualsLiteral("if-range"); +} + //static bool InternalHeaders::IsInvalidName(const nsACString& aName, ErrorResult& aRv) @@ -283,6 +294,18 @@ InternalHeaders::HasOnlySimpleHeaders() const return true; } +bool +InternalHeaders::HasRevalidationHeaders() const +{ + for (uint32_t i = 0; i < mList.Length(); ++i) { + if (IsRevalidationHeader(mList[i].mName)) { + return true; + } + } + + return false; +} + // static already_AddRefed InternalHeaders::BasicHeaders(InternalHeaders* aHeaders) diff --git a/dom/fetch/InternalHeaders.h b/dom/fetch/InternalHeaders.h index 54639cb9aa..1067fd7b5a 100644 --- a/dom/fetch/InternalHeaders.h +++ b/dom/fetch/InternalHeaders.h @@ -99,6 +99,8 @@ public: bool HasOnlySimpleHeaders() const; + bool HasRevalidationHeaders() const; + static already_AddRefed BasicHeaders(InternalHeaders* aHeaders); @@ -142,6 +144,8 @@ private: static bool IsSimpleHeader(const nsACString& aName, const nsACString& aValue); + + static bool IsRevalidationHeader(const nsACString& aName); }; } // namespace dom diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp index 58657bab3f..3d40a947ab 100644 --- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -37,13 +37,17 @@ InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult copy->mSameOriginDataURL = true; copy->mPreserveContentCodings = true; // The default referrer is already about:client. + copy->mReferrerPolicy = mReferrerPolicy; - copy->mContentPolicyType = nsIContentPolicy::TYPE_FETCH; + copy->mContentPolicyType = mContentPolicyTypeOverridden ? + mContentPolicyType : + nsIContentPolicy::TYPE_FETCH; copy->mMode = mMode; copy->mCredentialsMode = mCredentialsMode; copy->mCacheMode = mCacheMode; copy->mRedirectMode = mRedirectMode; copy->mCreatedByFetchEvent = mCreatedByFetchEvent; + copy->mContentPolicyTypeOverridden = mContentPolicyTypeOverridden; return copy.forget(); } @@ -77,6 +81,7 @@ InternalRequest::InternalRequest(const InternalRequest& aOther) , mHeaders(new InternalHeaders(*aOther.mHeaders)) , mContentPolicyType(aOther.mContentPolicyType) , mReferrer(aOther.mReferrer) + , mReferrerPolicy(aOther.mReferrerPolicy) , mMode(aOther.mMode) , mCredentialsMode(aOther.mCredentialsMode) , mResponseTainting(aOther.mResponseTainting) @@ -91,6 +96,7 @@ InternalRequest::InternalRequest(const InternalRequest& aOther) , mUnsafeRequest(aOther.mUnsafeRequest) , mUseURLCredentials(aOther.mUseURLCredentials) , mCreatedByFetchEvent(aOther.mCreatedByFetchEvent) + , mContentPolicyTypeOverridden(aOther.mContentPolicyTypeOverridden) { // NOTE: does not copy body stream... use the fallible Clone() for that } @@ -105,6 +111,13 @@ InternalRequest::SetContentPolicyType(nsContentPolicyType aContentPolicyType) mContentPolicyType = aContentPolicyType; } +void +InternalRequest::OverrideContentPolicyType(nsContentPolicyType aContentPolicyType) +{ + SetContentPolicyType(aContentPolicyType); + mContentPolicyTypeOverridden = true; +} + /* static */ RequestContext InternalRequest::MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType) @@ -361,5 +374,14 @@ InternalRequest::MapChannelToRequestCredentials(nsIChannel* aChannel) return RequestCredentials::Same_origin; } +void +InternalRequest::MaybeSkipCacheIfPerformingRevalidation() +{ + if (mCacheMode == RequestCache::Default && + mHeaders->HasRevalidationHeaders()) { + mCacheMode = RequestCache::No_store; + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/fetch/InternalRequest.h b/dom/fetch/InternalRequest.h index b31b04858f..cc4b274011 100644 --- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -93,6 +93,7 @@ public: , mHeaders(new InternalHeaders(HeadersGuardEnum::None)) , mContentPolicyType(nsIContentPolicy::TYPE_FETCH) , mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR)) + , mReferrerPolicy(ReferrerPolicy::_empty) , mMode(RequestMode::No_cors) , mCredentialsMode(RequestCredentials::Omit) , mResponseTainting(LoadTainting::Basic) @@ -121,12 +122,14 @@ public: RequestRedirect aRequestRedirect, RequestCredentials aRequestCredentials, const nsAString& aReferrer, + ReferrerPolicy aReferrerPolicy, nsContentPolicyType aContentPolicyType) : mMethod(aMethod) , mURL(aURL) , mHeaders(aHeaders) , mContentPolicyType(aContentPolicyType) , mReferrer(aReferrer) + , mReferrerPolicy(aReferrerPolicy) , mMode(aMode) , mCredentialsMode(aRequestCredentials) , mResponseTainting(LoadTainting::Basic) @@ -142,6 +145,12 @@ public: , mUnsafeRequest(false) , mUseURLCredentials(false) { + // Normally we strip the fragment from the URL in Request::Constructor. + // If internal code is directly constructing this object they must + // strip the fragment first. Since these should be well formed URLs we + // can use a simple check for a fragment here. The full parser is + // difficult to use off the main thread. + MOZ_ASSERT(mURL.Find(NS_LITERAL_CSTRING("#")) == kNotFound); } already_AddRefed Clone(); @@ -225,6 +234,18 @@ public: mReferrer.Assign(aReferrer); } + ReferrerPolicy + ReferrerPolicy_() const + { + return mReferrerPolicy; + } + + void + SetReferrerPolicy(ReferrerPolicy aReferrerPolicy) + { + mReferrerPolicy = aReferrerPolicy; + } + bool SkipServiceWorker() const { @@ -314,6 +335,9 @@ public: void SetContentPolicyType(nsContentPolicyType aContentPolicyType); + void + OverrideContentPolicyType(nsContentPolicyType aContentPolicyType); + RequestContext Context() const { @@ -404,6 +428,15 @@ public: bool IsClientRequest() const; + void + MaybeSkipCacheIfPerformingRevalidation(); + + bool + IsContentPolicyTypeOverridden() const + { + return mContentPolicyTypeOverridden; + } + static RequestMode MapChannelToRequestMode(nsIChannel* aChannel); @@ -437,6 +470,7 @@ private: // "about:client": client (default) // URL: an URL nsString mReferrer; + ReferrerPolicy mReferrerPolicy; RequestMode mMode; RequestCredentials mCredentialsMode; @@ -456,6 +490,10 @@ private: // use it to check if Service Workers are simply fetching intercepted Request // objects without modifying them. bool mCreatedByFetchEvent = false; + // This is only set when Request.overrideContentPolicyType() has been set. + // It is illegal to pass such a Request object to a fetch() method unless + // if the caller has chrome privileges. + bool mContentPolicyTypeOverridden = false; }; } // namespace dom diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index 05c1afeba8..7164336526 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -91,9 +91,9 @@ Request::GetInternalRequest() } namespace { -void -GetRequestURLFromDocument(nsIDocument* aDocument, const nsAString& aInput, - nsAString& aRequestURL, ErrorResult& aRv) +already_AddRefed +ParseURLFromDocument(nsIDocument* aDocument, const nsAString& aInput, + ErrorResult& aRv) { MOZ_ASSERT(aDocument); MOZ_ASSERT(NS_IsMainThread()); @@ -103,6 +103,16 @@ GetRequestURLFromDocument(nsIDocument* aDocument, const nsAString& aInput, aRv = NS_NewURI(getter_AddRefs(resolvedURI), aInput, nullptr, baseURI); if (NS_WARN_IF(aRv.Failed())) { aRv.ThrowTypeError(aInput); + } + return resolvedURI.forget(); +} + +void +GetRequestURLFromDocument(nsIDocument* aDocument, const nsAString& aInput, + nsAString& aRequestURL, ErrorResult& aRv) +{ + nsCOMPtr resolvedURI = ParseURLFromDocument(aDocument, aInput, aRv); + if (aRv.Failed()) { return; } @@ -132,9 +142,8 @@ GetRequestURLFromDocument(nsIDocument* aDocument, const nsAString& aInput, CopyUTF8toUTF16(spec, aRequestURL); } -void -GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL, - ErrorResult& aRv) +already_AddRefed +ParseURLFromChrome(const nsAString& aInput, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); @@ -142,6 +151,16 @@ GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL, aRv = NS_NewURI(getter_AddRefs(uri), aInput, nullptr, nullptr); if (NS_WARN_IF(aRv.Failed())) { aRv.ThrowTypeError(aInput); + } + return uri.forget(); +} + +void +GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL, + ErrorResult& aRv) +{ + nsCOMPtr uri = ParseURLFromChrome(aInput, aRv); + if (aRv.Failed()) { return; } @@ -171,9 +190,9 @@ GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL, CopyUTF8toUTF16(spec, aRequestURL); } -void -GetRequestURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput, - nsAString& aRequestURL, ErrorResult& aRv) +already_AddRefed +ParseURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput, + ErrorResult& aRv) { workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); @@ -184,6 +203,16 @@ GetRequestURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput, workers::URL::Constructor(aGlobal, aInput, baseURL, aRv); if (NS_WARN_IF(aRv.Failed())) { aRv.ThrowTypeError(aInput); + } + return url.forget(); +} + +void +GetRequestURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput, + nsAString& aRequestURL, ErrorResult& aRv) +{ + RefPtr url = ParseURLFromWorker(aGlobal, aInput, aRv); + if (aRv.Failed()) { return; } @@ -215,6 +244,38 @@ GetRequestURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput, } } +class ReferrerSameOriginChecker final : public workers::WorkerMainThreadRunnable +{ +public: + ReferrerSameOriginChecker(workers::WorkerPrivate* aWorkerPrivate, + const nsAString& aReferrerURL, + nsresult& aResult) + : workers::WorkerMainThreadRunnable(aWorkerPrivate), + mReferrerURL(aReferrerURL), + mResult(aResult) + { + mWorkerPrivate->AssertIsOnWorkerThread(); + } + + bool + MainThreadRun() override + { + nsCOMPtr uri; + if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), mReferrerURL))) { + nsCOMPtr principal = mWorkerPrivate->GetPrincipal(); + if (principal) { + mResult = principal->CheckMayLoad(uri, /* report */ false, + /* allowIfInheritsPrincipal */ false); + } + } + return true; + } + +private: + const nsString mReferrerURL; + nsresult& mResult; +}; + } // namespace /*static*/ already_AddRefed @@ -231,15 +292,16 @@ Request::Constructor(const GlobalObject& aGlobal, RefPtr inputReq = &aInput.GetAsRequest(); nsCOMPtr body; inputReq->GetBody(getter_AddRefs(body)); + if (inputReq->BodyUsed()) { + aRv.ThrowTypeError(); + return nullptr; + } if (body) { - if (inputReq->BodyUsed()) { - aRv.ThrowTypeError(); - return nullptr; - } temporaryBody = body; } request = inputReq->GetInternalRequest(); + } else { request = new InternalRequest(); } @@ -284,11 +346,85 @@ Request::Constructor(const GlobalObject& aGlobal, aInit.mCredentials.WasPassed() ? aInit.mCredentials.Value() : fallbackCredentials; - if (mode == RequestMode::Navigate) { + if (mode == RequestMode::Navigate || + (aInit.IsAnyMemberPresent() && request->Mode() == RequestMode::Navigate)) { aRv.ThrowTypeError(NS_LITERAL_STRING("navigate")); return nullptr; } + if (aInit.IsAnyMemberPresent()) { + request->SetReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR)); + request->SetReferrerPolicy(ReferrerPolicy::_empty); + } + if (aInit.mReferrer.WasPassed()) { + const nsString& referrer = aInit.mReferrer.Value(); + if (referrer.IsEmpty()) { + request->SetReferrer(NS_LITERAL_STRING("")); + } else { + nsAutoString referrerURL; + if (NS_IsMainThread()) { + nsIDocument* doc = GetEntryDocument(); + nsCOMPtr uri; + if (doc) { + uri = ParseURLFromDocument(doc, referrer, aRv); + } else { + // If we don't have a document, we must assume that this is a full URL. + uri = ParseURLFromChrome(referrer, aRv); + } + if (NS_WARN_IF(aRv.Failed())) { + aRv.ThrowTypeError(referrer); + return nullptr; + } + nsAutoCString spec; + uri->GetSpec(spec); + CopyUTF8toUTF16(spec, referrerURL); + if (!referrerURL.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) { + nsCOMPtr principal = global->PrincipalOrNull(); + if (principal) { + nsresult rv = principal->CheckMayLoad(uri, /* report */ false, + /* allowIfInheritsPrincipal */ false); + if (NS_FAILED(rv)) { + aRv.ThrowTypeError(referrer); + return nullptr; + } + } + } + } else { + RefPtr url = ParseURLFromWorker(aGlobal, referrer, aRv); + if (NS_WARN_IF(aRv.Failed())) { + aRv.ThrowTypeError(referrer); + return nullptr; + } + url->Stringify(referrerURL, aRv); + if (NS_WARN_IF(aRv.Failed())) { + aRv.ThrowTypeError(referrer); + return nullptr; + } + if (!referrerURL.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) { + workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); + nsresult rv = NS_OK; + // ReferrerSameOriginChecker uses a sync loop to get the main thread + // to perform the same-origin check. Overall, on Workers this method + // can create 3 sync loops (two for constructing URLs and one here) so + // in the future we may want to optimize it all by off-loading all of + // this work in a single sync loop. + RefPtr checker = + new ReferrerSameOriginChecker(worker, referrerURL, rv); + checker->Dispatch(aRv); + if (aRv.Failed() || NS_FAILED(rv)) { + aRv.ThrowTypeError(referrer); + return nullptr; + } + } + } + request->SetReferrer(referrerURL); + } + } + + if (aInit.mReferrerPolicy.WasPassed()) { + request->SetReferrerPolicy(aInit.mReferrerPolicy.Value()); + } + if (mode != RequestMode::EndGuard_) { request->ClearCreatedByFetchEvent(); request->SetMode(mode); @@ -370,7 +506,8 @@ Request::Constructor(const GlobalObject& aGlobal, return nullptr; } - if (aInit.mBody.WasPassed() || temporaryBody) { + if ((aInit.mBody.WasPassed() && !aInit.mBody.Value().IsNull()) || + temporaryBody) { // HEAD and GET are not allowed to have a body. nsAutoCString method; request->GetMethod(method); @@ -382,29 +519,34 @@ Request::Constructor(const GlobalObject& aGlobal, } if (aInit.mBody.WasPassed()) { - const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit = aInit.mBody.Value(); - nsCOMPtr stream; - nsAutoCString contentType; - aRv = ExtractByteStreamFromBody(bodyInit, - getter_AddRefs(stream), contentType); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; + const Nullable& bodyInitNullable = + aInit.mBody.Value(); + if (!bodyInitNullable.IsNull()) { + const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit = + bodyInitNullable.Value(); + nsCOMPtr stream; + nsAutoCString contentType; + aRv = ExtractByteStreamFromBody(bodyInit, + getter_AddRefs(stream), contentType); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + temporaryBody = stream; + + if (!contentType.IsVoid() && + !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) { + requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"), + contentType, aRv); + } + + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + request->ClearCreatedByFetchEvent(); + request->SetBody(temporaryBody); } - - temporaryBody = stream; - - if (!contentType.IsVoid() && - !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) { - requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"), - contentType, aRv); - } - - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - request->ClearCreatedByFetchEvent(); - request->SetBody(temporaryBody); } RefPtr domRequest = new Request(global, request); diff --git a/dom/fetch/Request.h b/dom/fetch/Request.h index 5679a065a4..191feb5b54 100644 --- a/dom/fetch/Request.h +++ b/dom/fetch/Request.h @@ -88,9 +88,15 @@ public: } void - SetContentPolicyType(nsContentPolicyType aContentPolicyType) + OverrideContentPolicyType(nsContentPolicyType aContentPolicyType) { - mRequest->SetContentPolicyType(aContentPolicyType); + mRequest->OverrideContentPolicyType(aContentPolicyType); + } + + bool + IsContentPolicyTypeOverridden() const + { + return mRequest->IsContentPolicyTypeOverridden(); } void @@ -99,6 +105,12 @@ public: mRequest->GetReferrer(aReferrer); } + ReferrerPolicy + ReferrerPolicy_() const + { + return mRequest->ReferrerPolicy_(); + } + InternalHeaders* GetInternalHeaders() const { diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index 4276630572..a8a02f02ae 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -183,13 +183,14 @@ FileSystemResponseValue CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - BlobParent* actor = GetBlobParent(mTargetBlobImpl); - if (!actor) { - return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR); + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemDirectoryResponse(); } - FileSystemFileResponse response; - response.blobParent() = actor; - return response; + + return FileSystemFileResponse(path); } void @@ -198,8 +199,12 @@ CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemFileResponse r = aValue; - BlobChild* actor = static_cast(r.blobChild()); - mTargetBlobImpl = actor->GetBlobImpl(); + + NS_ConvertUTF16toUTF8 path(r.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } } nsresult @@ -312,7 +317,6 @@ CreateFileTask::Work() return NS_ERROR_FAILURE; } - mTargetBlobImpl = new BlobImplFile(mTargetPath); return NS_OK; } @@ -331,7 +335,6 @@ CreateFileTask::Work() return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR; } - mTargetBlobImpl = new BlobImplFile(mTargetPath); return NS_OK; } @@ -352,9 +355,9 @@ CreateFileTask::HandlerCallback() return; } - RefPtr blob = Blob::Create(mFileSystem->GetParentObject(), - mTargetBlobImpl); - mPromise->MaybeResolve(blob); + RefPtr file = File::CreateFromFile(mFileSystem->GetParentObject(), + mTargetPath); + mPromise->MaybeResolve(file); mPromise = nullptr; mBlobData = nullptr; } diff --git a/dom/filesystem/CreateFileTask.h b/dom/filesystem/CreateFileTask.h index fc0ba14ceb..ece93d3e9a 100644 --- a/dom/filesystem/CreateFileTask.h +++ b/dom/filesystem/CreateFileTask.h @@ -86,10 +86,6 @@ private: nsCOMPtr mBlobStream; InfallibleTArray mArrayData; bool mReplace; - - // This cannot be a File because this object is created on a different - // thread and File is not thread-safe. Let's use the BlobImpl instead. - RefPtr mTargetBlobImpl; }; } // namespace dom diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index 5c89a8c95e..07b9e18c9e 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -44,7 +44,7 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType, aStorageName, getter_AddRefs(rootFile)); - NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath))); + NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalOrDeviceStorageRootPath))); if (!XRE_IsParentProcess()) { return; @@ -113,7 +113,7 @@ DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const MOZ_ASSERT(aFile); nsCOMPtr rootPath; - nsresult rv = NS_NewLocalFile(GetLocalRootPath(), false, + nsresult rv = NS_NewLocalFile(LocalOrDeviceStorageRootPath(), false, getter_AddRefs(rootPath)); if (NS_WARN_IF(NS_FAILED(rv))) { return false; diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index edab5908f5..a8066980f3 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -118,7 +118,7 @@ Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv) MOZ_ASSERT(aFileSystem); nsCOMPtr path; - aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->GetLocalRootPath()), + aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->LocalOrDeviceStorageRootPath()), true, getter_AddRefs(path)); if (NS_WARN_IF(aRv.Failed())) { return nullptr; @@ -146,6 +146,13 @@ Directory::Create(nsISupports* aParent, nsIFile* aFile, bool isDir; nsresult rv = aFile->IsDirectory(&isDir); MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir); + + if (aType == eNotDOMRootDirectory) { + RefPtr parent; + rv = aFile->GetParent(getter_AddRefs(parent)); + // We must have a parent if this is not the root directory. + MOZ_ASSERT(NS_SUCCEEDED(rv) && parent); + } #endif RefPtr directory = @@ -319,7 +326,6 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, { nsresult error = NS_OK; nsCOMPtr realPath; - RefPtr blob; // Check and get the target path. @@ -328,25 +334,38 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, return nullptr; } + // If this is a File if (aPath.IsFile()) { - blob = aPath.GetAsFile().Impl(); + if (!fs->GetRealPath(aPath.GetAsFile().Impl(), + getter_AddRefs(realPath))) { + error = NS_ERROR_DOM_SECURITY_ERR; + } + + // If this is a string } else if (aPath.IsString()) { error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath)); - } else if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) { - error = NS_ERROR_DOM_SECURITY_ERR; + + // Directory } else { - realPath = aPath.GetAsDirectory().mFile; - // The target must be a descendant of this directory. - if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; + MOZ_ASSERT(aPath.IsDirectory()); + if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) { + error = NS_ERROR_DOM_SECURITY_ERR; + } else { + realPath = aPath.GetAsDirectory().mFile; } } + // The target must be a descendant of this directory. + if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) { + error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; + } + RefPtr task = - RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv); + RemoveTask::Create(fs, mFile, realPath, aRecursive, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + task->SetError(error); FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); @@ -355,12 +374,20 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, void Directory::GetPath(nsAString& aRetval, ErrorResult& aRv) { - if (mType == eDOMRootDirectory) { - aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); - } else { - // TODO: this should be a bit different... - GetName(aRetval, aRv); + // This operation is expensive. Better to cache the result. + if (mPath.IsEmpty()) { + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + fs->GetDOMPath(mFile, mType, mPath, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } } + + aRetval = mPath; } nsresult @@ -402,19 +429,12 @@ FileSystemBase* Directory::GetFileSystem(ErrorResult& aRv) { if (!mFileSystem) { - nsCOMPtr parent; - aRv = mFile->GetParent(getter_AddRefs(parent)); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - // Parent can be null if mFile is pointing to the top directory. - if (!parent) { - parent = mFile; - } + // Any subdir inherits the FileSystem of the parent Directory. If we are + // here it's because we are dealing with the DOM root. + MOZ_ASSERT(mType == eDOMRootDirectory); nsAutoString path; - aRv = parent->GetPath(path); + aRv = mFile->GetPath(path); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } diff --git a/dom/filesystem/Directory.h b/dom/filesystem/Directory.h index 5e6ac2deef..1d121fbefe 100644 --- a/dom/filesystem/Directory.h +++ b/dom/filesystem/Directory.h @@ -40,13 +40,12 @@ class Directory final , public nsWrapperCache { public: - struct BlobImplOrDirectoryPath + struct FileOrDirectoryPath { - RefPtr mBlobImpl; - nsString mDirectoryPath; + nsString mPath; enum { - eBlobImpl, + eFilePath, eDirectoryPath } mType; }; @@ -167,6 +166,7 @@ private: DirectoryType mType; nsString mFilters; + nsString mPath; }; } // namespace dom diff --git a/dom/filesystem/FileSystemBase.cpp b/dom/filesystem/FileSystemBase.cpp index 3d6e4529ae..75909292c2 100644 --- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -67,9 +67,8 @@ FileSystemBase::GetParentObject() const bool FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const { - MOZ_ASSERT(XRE_IsParentProcess(), - "Should be on parent process!"); MOZ_ASSERT(aFile, "aFile Should not be null."); + MOZ_ASSERT(aPath); nsAutoString filePath; ErrorResult rv; @@ -99,5 +98,78 @@ FileSystemBase::IsSafeDirectory(Directory* aDir) const return false; } +void +FileSystemBase::GetDOMPath(nsIFile* aFile, + Directory::DirectoryType aType, + nsAString& aRetval, + ErrorResult& aRv) const +{ + MOZ_ASSERT(aFile); + + if (aType == Directory::eDOMRootDirectory) { + aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); + return; + } + + nsCOMPtr fileSystemPath; + aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(LocalOrDeviceStorageRootPath()), + true, getter_AddRefs(fileSystemPath)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(FileSystemUtils::IsDescendantPath(fileSystemPath, aFile)); + + nsCOMPtr path; + aRv = aFile->Clone(getter_AddRefs(path)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + nsTArray parts; + + while (true) { + bool equal = false; + aRv = fileSystemPath->Equals(path, &equal); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (equal) { + break; + } + + nsAutoString leafName; + aRv = path->GetLeafName(leafName); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + parts.AppendElement(leafName); + + nsCOMPtr parentPath; + aRv = path->GetParent(getter_AddRefs(parentPath)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(parentPath); + + aRv = parentPath->Clone(getter_AddRefs(path)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + } + + MOZ_ASSERT(!parts.IsEmpty()); + + aRetval.Truncate(); + + for (int32_t i = parts.Length() - 1; i >= 0; --i) { + aRetval.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); + aRetval.Append(parts[i]); + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index 6dc97247f4..a3cbfcecb0 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -9,12 +9,12 @@ #include "nsAutoPtr.h" #include "nsString.h" +#include "Directory.h" namespace mozilla { namespace dom { class BlobImpl; -class Directory; class FileSystemBase { @@ -47,10 +47,20 @@ public: virtual void GetRootName(nsAString& aRetval) const = 0; + void + GetDOMPath(nsIFile* aFile, Directory::DirectoryType aType, + nsAString& aRetval, ErrorResult& aRv) const; + + /* + * Return the local root path of the FileSystem implementation. + * For OSFileSystem, this is equal to the path of the root Directory; + * For DeviceStorageFileSystem, this is the path of the SDCard, parent + * directory of the exposed root Directory (per type). + */ const nsAString& - GetLocalRootPath() const + LocalOrDeviceStorageRootPath() const { - return mLocalRootPath; + return mLocalOrDeviceStorageRootPath; } bool @@ -92,9 +102,17 @@ protected: // The local path of the root (i.e. the OS path, with OS path separators, of // the OS directory that acts as the root of this OSFileSystem). - // Only available in the parent process. - // In the child process, we don't use it and its value should be empty. - nsString mLocalRootPath; + // This path must be set by the FileSystem implementation immediately + // because it will be used for the validation of any FileSystemTaskBase. + // The concept of this path is that, any task will never go out of it and this + // must be considered the OS 'root' of the current FileSystem. Different + // Directory object can have different OS 'root' path. + // To be more clear, any path managed by this FileSystem implementation must + // be discendant of this local root path. + // The reason why it's not just called 'localRootPath' is because for + // DeviceStorage this contains the path of the device storage SDCard, that is + // the parent directory of the exposed root path. + nsString mLocalOrDeviceStorageRootPath; bool mShutdown; diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp index 46cae6d86c..3468d59d45 100644 --- a/dom/filesystem/FileSystemTaskBase.cpp +++ b/dom/filesystem/FileSystemTaskBase.cpp @@ -201,36 +201,6 @@ FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue) return true; } -BlobParent* -FileSystemTaskBase::GetBlobParent(BlobImpl* aFile) const -{ - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); - MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - MOZ_ASSERT(aFile); - - // Load the lazy dom file data from the parent before sending to the child. - nsString mimeType; - aFile->GetType(mimeType); - - // We call GetSize and GetLastModified to prepopulate the value in the - // BlobImpl. - { - ErrorResult rv; - aFile->GetSize(rv); - rv.SuppressException(); - } - - { - ErrorResult rv; - aFile->GetLastModified(rv); - rv.SuppressException(); - } - - ContentParent* cp = static_cast(mRequestParent->Manager()); - return cp->GetOrCreateActorForBlobImpl(aFile); -} - void FileSystemTaskBase::SetError(const nsresult& aErrorValue) { diff --git a/dom/filesystem/FileSystemTaskBase.h b/dom/filesystem/FileSystemTaskBase.h index 3da16d9b25..e769d6082d 100644 --- a/dom/filesystem/FileSystemTaskBase.h +++ b/dom/filesystem/FileSystemTaskBase.h @@ -205,9 +205,6 @@ protected: virtual bool Recv__delete__(const FileSystemResponseValue& value) override; - BlobParent* - GetBlobParent(BlobImpl* aBlob) const; - nsresult mErrorValue; RefPtr mFileSystem; diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index f2672d1e27..07b4695934 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -138,19 +138,14 @@ GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const nsTArray inputs; for (unsigned i = 0; i < mTargetData.Length(); i++) { - if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) { - BlobParent* blobParent = GetBlobParent(mTargetData[i].mBlobImpl); - if (!blobParent) { - continue; - } - - FileSystemDirectoryListingResponseBlob blobData; - blobData.blobParent() = blobParent; - inputs.AppendElement(blobData); + if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath) { + FileSystemDirectoryListingResponseFile fileData; + fileData.fileRealPath() = mTargetData[i].mPath; + inputs.AppendElement(fileData); } else { - MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath); + MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath); FileSystemDirectoryListingResponseDirectory directoryData; - directoryData.directoryRealPath() = mTargetData[i].mDirectoryPath; + directoryData.directoryRealPath() = mTargetData[i].mPath; inputs.AppendElement(directoryData); } } @@ -172,18 +167,21 @@ GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& for (uint32_t i = 0; i < r.data().Length(); ++i) { const FileSystemDirectoryListingResponseData& data = r.data()[i]; - if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseBlob) { - PBlobChild* blob = data.get_FileSystemDirectoryListingResponseBlob().blobChild(); + Directory::FileOrDirectoryPath element; - Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); - element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; - element->mBlobImpl = static_cast(blob)->GetBlobImpl(); + if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseFile) { + element.mType = Directory::FileOrDirectoryPath::eFilePath; + element.mPath = data.get_FileSystemDirectoryListingResponseFile().fileRealPath(); } else { MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory); - Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); - element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; - element->mDirectoryPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath(); + element.mType = Directory::FileOrDirectoryPath::eDirectoryPath; + element.mPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath(); + } + + if (!mTargetData.AppendElement(element, fallible)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; } } } @@ -287,21 +285,18 @@ GetDirectoryListingTask::Work() } } - if (isDir) { - nsAutoString path; - if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) { - continue; - } + nsAutoString path; + if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) { + continue; + } - Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); - element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; - element->mDirectoryPath = path; - } else { - BlobImplFile* impl = new BlobImplFile(currFile); + Directory::FileOrDirectoryPath element; + element.mPath = path; + element.mType = isDir ? Directory::FileOrDirectoryPath::eDirectoryPath + : Directory::FileOrDirectoryPath::eFilePath; - Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); - element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; - element->mBlobImpl = impl; + if (!mTargetData.AppendElement(element, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; } } return NS_OK; @@ -333,44 +328,45 @@ GetDirectoryListingTask::HandlerCallback() } for (unsigned i = 0; i < count; i++) { - if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) { - nsCOMPtr directoryPath; - NS_ConvertUTF16toUTF8 path(mTargetData[i].mDirectoryPath); - nsresult rv = NS_NewNativeLocalFile(path, true, - getter_AddRefs(directoryPath)); - if (NS_WARN_IF(NS_FAILED(rv))) { - mPromise->MaybeReject(rv); - mPromise = nullptr; - return; - } + nsCOMPtr path; + NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mPath); + nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } #ifdef DEBUG - nsCOMPtr rootPath; - rv = NS_NewLocalFile(mFileSystem->GetLocalRootPath(), false, - getter_AddRefs(rootPath)); - if (NS_WARN_IF(NS_FAILED(rv))) { - mPromise->MaybeReject(rv); - mPromise = nullptr; - return; - } + nsCOMPtr rootPath; + rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false, + getter_AddRefs(rootPath)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } - MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, directoryPath)); + MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path)); #endif + if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath) { RefPtr directory = - Directory::Create(mFileSystem->GetParentObject(), - directoryPath, - Directory::eNotDOMRootDirectory, - mFileSystem); + Directory::Create(mFileSystem->GetParentObject(), path, + Directory::eNotDOMRootDirectory, mFileSystem); MOZ_ASSERT(directory); // Propogate mFilter onto sub-Directory object: directory->SetContentFilters(mFilters); listing[i].SetAsDirectory() = directory; } else { - MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl); - listing[i].SetAsFile() = - File::Create(mFileSystem->GetParentObject(), mTargetData[i].mBlobImpl); + MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath); + + RefPtr file = + File::CreateFromFile(mFileSystem->GetParentObject(), path); + MOZ_ASSERT(file); + + listing[i].SetAsFile() = file; } } diff --git a/dom/filesystem/GetDirectoryListingTask.h b/dom/filesystem/GetDirectoryListingTask.h index f0a3ae7fd5..2b75867958 100644 --- a/dom/filesystem/GetDirectoryListingTask.h +++ b/dom/filesystem/GetDirectoryListingTask.h @@ -77,7 +77,7 @@ private: // We cannot store File or Directory objects bacause this object is created // on a different thread and File and Directory are not thread-safe. - nsTArray mTargetData; + FallibleTArray mTargetData; }; } // namespace dom diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index ccb4b851b4..c3c0f6d1cc 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -130,23 +130,17 @@ FileSystemResponseValue GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - if (mIsDirectory) { - nsAutoString path; - aRv = mTargetPath->GetPath(path); - if (NS_WARN_IF(aRv.Failed())) { - return FileSystemDirectoryResponse(); - } + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemDirectoryResponse(); + } + if (mIsDirectory) { return FileSystemDirectoryResponse(path); } - BlobParent* actor = GetBlobParent(mTargetBlobImpl); - if (!actor) { - return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR); - } - FileSystemFileResponse response; - response.blobParent() = actor; - return response; + return FileSystemFileResponse(path); } void @@ -157,8 +151,13 @@ GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& a switch (aValue.type()) { case FileSystemResponseValue::TFileSystemFileResponse: { FileSystemFileResponse r = aValue; - BlobChild* actor = static_cast(r.blobChild()); - mTargetBlobImpl = actor->GetBlobImpl(); + + NS_ConvertUTF16toUTF8 path(r.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + mIsDirectory = false; break; } @@ -242,8 +241,6 @@ GetFileOrDirectoryTask::Work() return NS_ERROR_DOM_SECURITY_ERR; } - mTargetBlobImpl = new BlobImplFile(mTargetPath); - return NS_OK; } @@ -274,9 +271,9 @@ GetFileOrDirectoryTask::HandlerCallback() return; } - RefPtr blob = Blob::Create(mFileSystem->GetParentObject(), - mTargetBlobImpl); - mPromise->MaybeResolve(blob); + RefPtr file = File::CreateFromFile(mFileSystem->GetParentObject(), + mTargetPath); + mPromise->MaybeResolve(file); mPromise = nullptr; } diff --git a/dom/filesystem/GetFileOrDirectoryTask.h b/dom/filesystem/GetFileOrDirectoryTask.h index 4430e567d7..959180665a 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.h +++ b/dom/filesystem/GetFileOrDirectoryTask.h @@ -76,10 +76,6 @@ private: // Whether we get a directory. bool mIsDirectory; Directory::DirectoryType mType; - - // This cannot be a File bacause this object is created on a different - // thread and File is not thread-safe. Let's use the BlobImpl instead. - RefPtr mTargetBlobImpl; }; } // namespace dom diff --git a/dom/filesystem/OSFileSystem.cpp b/dom/filesystem/OSFileSystem.cpp index 50335a982b..f5e3d76fb3 100644 --- a/dom/filesystem/OSFileSystem.cpp +++ b/dom/filesystem/OSFileSystem.cpp @@ -19,7 +19,7 @@ namespace dom { OSFileSystem::OSFileSystem(const nsAString& aRootDir) { - mLocalRootPath = aRootDir; + mLocalOrDeviceStorageRootPath = aRootDir; // Non-mobile devices don't have the concept of separate permissions to // access different parts of devices storage like Pictures, or Videos, etc. @@ -33,7 +33,7 @@ OSFileSystem::OSFileSystem(const nsAString& aRootDir) already_AddRefed OSFileSystem::Clone() { - RefPtr fs = new OSFileSystem(mLocalRootPath); + RefPtr fs = new OSFileSystem(mLocalOrDeviceStorageRootPath); if (mParent) { fs->Init(mParent); } @@ -104,7 +104,7 @@ OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb) void OSFileSystem::SerializeDOMPath(nsAString& aOutput) const { - aOutput = mLocalRootPath; + aOutput = mLocalOrDeviceStorageRootPath; } } // namespace dom diff --git a/dom/filesystem/PFileSystemRequest.ipdl b/dom/filesystem/PFileSystemRequest.ipdl index 6d2e934930..b3671d4ead 100644 --- a/dom/filesystem/PFileSystemRequest.ipdl +++ b/dom/filesystem/PFileSystemRequest.ipdl @@ -12,7 +12,7 @@ namespace dom { struct FileSystemFileResponse { - PBlob blob; + nsString realPath; }; struct FileSystemDirectoryResponse @@ -20,9 +20,10 @@ struct FileSystemDirectoryResponse nsString realPath; }; -struct FileSystemDirectoryListingResponseBlob +struct FileSystemDirectoryListingResponseFile { - PBlob blob; + // This is the full real path for the file that we are sending via IPC. + nsString fileRealPath; }; struct FileSystemDirectoryListingResponseDirectory @@ -33,7 +34,7 @@ struct FileSystemDirectoryListingResponseDirectory union FileSystemDirectoryListingResponseData { - FileSystemDirectoryListingResponseBlob; + FileSystemDirectoryListingResponseFile; FileSystemDirectoryListingResponseDirectory; }; diff --git a/dom/filesystem/RemoveTask.cpp b/dom/filesystem/RemoveTask.cpp index 8bebeef1a2..00b702ad47 100644 --- a/dom/filesystem/RemoveTask.cpp +++ b/dom/filesystem/RemoveTask.cpp @@ -21,7 +21,6 @@ namespace dom { /* static */ already_AddRefed RemoveTask::Create(FileSystemBase* aFileSystem, nsIFile* aDirPath, - BlobImpl* aTargetBlob, nsIFile* aTargetPath, bool aRecursive, ErrorResult& aRv) @@ -29,9 +28,10 @@ RemoveTask::Create(FileSystemBase* aFileSystem, MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); MOZ_ASSERT(aDirPath); + MOZ_ASSERT(aTargetPath); RefPtr task = - new RemoveTask(aFileSystem, aDirPath, aTargetBlob, aTargetPath, aRecursive); + new RemoveTask(aFileSystem, aDirPath, aTargetPath, aRecursive); // aTargetPath can be null. In this case SetError will be called. @@ -72,33 +72,26 @@ RemoveTask::Create(FileSystemBase* aFileSystem, task->mRecursive = aParam.recursive(); - const FileSystemPathOrFileValue& target = aParam.target(); - - if (target.type() == FileSystemPathOrFileValue::TnsString) { - NS_ConvertUTF16toUTF8 path(target); - aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - return task.forget(); + NS_ConvertUTF16toUTF8 path(aParam.targetDirectory()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; } - BlobParent* bp = static_cast(static_cast(target)); - task->mTargetBlobImpl = bp->GetBlobImpl(); - MOZ_ASSERT(task->mTargetBlobImpl); + if (!FileSystemUtils::IsDescendantPath(task->mDirPath, task->mTargetPath)) { + aRv.Throw(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR); + return nullptr; + } return task.forget(); } RemoveTask::RemoveTask(FileSystemBase* aFileSystem, nsIFile* aDirPath, - BlobImpl* aTargetBlob, nsIFile* aTargetPath, bool aRecursive) : FileSystemTaskBase(aFileSystem) , mDirPath(aDirPath) - , mTargetBlobImpl(aTargetBlob) , mTargetPath(aTargetPath) , mRecursive(aRecursive) , mReturnValue(false) @@ -106,6 +99,7 @@ RemoveTask::RemoveTask(FileSystemBase* aFileSystem, MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); MOZ_ASSERT(aDirPath); + MOZ_ASSERT(aTargetPath); } RemoveTask::RemoveTask(FileSystemBase* aFileSystem, @@ -147,23 +141,15 @@ RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath, } param.recursive() = mRecursive; - if (mTargetBlobImpl) { - RefPtr blob = Blob::Create(mFileSystem->GetParentObject(), - mTargetBlobImpl); - BlobChild* actor - = ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob); - if (actor) { - param.target() = actor; - } - } else { - nsAutoString path; - aRv = mTargetPath->GetPath(path); - if (NS_WARN_IF(aRv.Failed())) { - return param; - } - param.target() = path; + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return param; } + + param.targetDirectory() = path; + return param; } @@ -194,16 +180,7 @@ RemoveTask::Work() return NS_ERROR_FAILURE; } - // Get the path if a File is passed as the target. - if (mTargetBlobImpl) { - if (!mFileSystem->GetRealPath(mTargetBlobImpl, - getter_AddRefs(mTargetPath))) { - return NS_ERROR_DOM_SECURITY_ERR; - } - if (!FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)) { - return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; - } - } + MOZ_ASSERT(FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)); bool exists = false; nsresult rv = mTargetPath->Exists(&exists); diff --git a/dom/filesystem/RemoveTask.h b/dom/filesystem/RemoveTask.h index d3fbfd1ebb..6bef5bfb57 100644 --- a/dom/filesystem/RemoveTask.h +++ b/dom/filesystem/RemoveTask.h @@ -23,7 +23,6 @@ public: static already_AddRefed Create(FileSystemBase* aFileSystem, nsIFile* aDirPath, - BlobImpl* aTargetBlob, nsIFile* aTargetPath, bool aRecursive, ErrorResult& aRv); @@ -64,7 +63,6 @@ protected: private: RemoveTask(FileSystemBase* aFileSystem, nsIFile* aDirPath, - BlobImpl* aTargetBlob, nsIFile* aTargetPath, bool aRecursive); @@ -73,12 +71,13 @@ private: FileSystemRequestParent* aParent); RefPtr mPromise; + + // This path is the Directory::mFile. nsCOMPtr mDirPath; - // This cannot be a File because this object will be used on a different - // thread and File is not thread-safe. Let's use the BlobImpl instead. - RefPtr mTargetBlobImpl; + // This is what we want to remove. mTargetPath is discendant path of mDirPath. nsCOMPtr mTargetPath; + bool mRecursive; bool mReturnValue; }; diff --git a/dom/filesystem/tests/script_fileList.js b/dom/filesystem/tests/script_fileList.js index 81fd6b38d1..f7fbca2e9d 100644 --- a/dom/filesystem/tests/script_fileList.js +++ b/dom/filesystem/tests/script_fileList.js @@ -1,11 +1,23 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.importGlobalProperties(["File"]); -addMessageListener("dir.open", function () { +addMessageListener("dir.open", function (e) { var testFile = Cc["@mozilla.org/file/directory_service;1"] .getService(Ci.nsIDirectoryService) .QueryInterface(Ci.nsIProperties) - .get("ProfD", Ci.nsIFile); + .get(e.path == 'root' ? 'ProfD' : e.path, Ci.nsIFile); + + // Let's go back to the root of the FileSystem + if (e.path == 'root') { + while (true) { + var parent = testFile.parent; + if (!parent) { + break; + } + + testFile = parent; + } + } sendAsyncMessage("dir.opened", { dir: testFile.path diff --git a/dom/filesystem/tests/test_basic.html b/dom/filesystem/tests/test_basic.html index 1b9f355b81..1a929f56a8 100644 --- a/dom/filesystem/tests/test_basic.html +++ b/dom/filesystem/tests/test_basic.html @@ -12,7 +12,7 @@ var directory; -function create_fileList() { +function create_fileList(aPath) { var url = SimpleTest.getTestFileURL("script_fileList.js"); var script = SpecialPowers.loadChromeScript(url); @@ -31,7 +31,7 @@ function create_fileList() { } script.addMessageListener("dir.opened", onOpened); - script.sendAsyncMessage("dir.open"); + script.sendAsyncMessage("dir.open", { path: aPath }); } function test_basic() { @@ -48,15 +48,17 @@ function checkSubDir(dir) { for (var i = 0; i < data.length; ++i) { ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories"); if (data[i] instanceof Directory) { - isnot(data[i].name, '/', "Subdirectory should be called with the leafname"); - isnot(data[i].path, '/', "Subdirectory path should be called with the leafname"); + isnot(data[i].name, '/', "Subdirectory should be called with the leafname: " + data[i].name); + isnot(data[i].path, '/', "Subdirectory path should be called with the leafname:" + data[i].path); + isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path."); + is(data[i].path,dir.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname"); } } } ); } -function getFilesAndDirectories() { +function getFilesAndDirectories(aRecursive) { directory.getFilesAndDirectories().then( function(data) { ok(data.length, "We should have some data."); @@ -65,8 +67,11 @@ function getFilesAndDirectories() { ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories"); if (data[i] instanceof Directory) { isnot(data[i].name, '/', "Subdirectory should be called with the leafname"); - isnot(data[i].path, '/', "Subdirectory path should be called with the leafname"); - promises.push(checkSubDir(data[i])); + is(data[i].path, '/' + data[i].name, "Subdirectory path should be called '/' + leafname"); + + if (aRecursive) { + promises.push(checkSubDir(data[i])); + } } } @@ -79,11 +84,13 @@ function getFilesAndDirectories() { } var tests = [ - create_fileList, - + function() { create_fileList('ProfD') }, test_basic, + function() { getFilesAndDirectories(true) }, - getFilesAndDirectories, + function() { create_fileList('root') }, + test_basic, + function() { getFilesAndDirectories(false) }, ]; function next() { diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 6bf5f7b754..a86fe2b035 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -254,7 +254,7 @@ class HTMLInputElementState final : public nsISupports nsTArray& aResult) const { for (uint32_t i = 0; i < mBlobImplsOrDirectoryPaths.Length(); ++i) { - if (mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) { + if (mBlobImplsOrDirectoryPaths[i].mType == BlobImplOrDirectoryPath::eBlobImpl) { RefPtr file = File::Create(aWindow, mBlobImplsOrDirectoryPaths[i].mBlobImpl); @@ -263,7 +263,7 @@ class HTMLInputElementState final : public nsISupports OwningFileOrDirectory* element = aResult.AppendElement(); element->SetAsFile() = file; } else { - MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath); + MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == BlobImplOrDirectoryPath::eDirectoryPath); nsCOMPtr file; NS_ConvertUTF16toUTF8 path(mBlobImplsOrDirectoryPaths[i].mDirectoryPath); @@ -287,11 +287,10 @@ class HTMLInputElementState final : public nsISupports mBlobImplsOrDirectoryPaths.Clear(); for (uint32_t i = 0; i < aArray.Length(); ++i) { if (aArray[i].IsFile()) { - Directory::BlobImplOrDirectoryPath* data = - mBlobImplsOrDirectoryPaths.AppendElement(); + BlobImplOrDirectoryPath* data = mBlobImplsOrDirectoryPaths.AppendElement(); data->mBlobImpl = aArray[i].GetAsFile()->Impl(); - data->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; + data->mType = BlobImplOrDirectoryPath::eBlobImpl; } else { MOZ_ASSERT(aArray[i].IsDirectory()); nsAutoString fullPath; @@ -300,11 +299,11 @@ class HTMLInputElementState final : public nsISupports continue; } - Directory::BlobImplOrDirectoryPath* data = + BlobImplOrDirectoryPath* data = mBlobImplsOrDirectoryPaths.AppendElement(); data->mDirectoryPath = fullPath; - data->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; + data->mType = BlobImplOrDirectoryPath::eDirectoryPath; } } } @@ -320,7 +319,18 @@ class HTMLInputElementState final : public nsISupports nsString mValue; - nsTArray mBlobImplsOrDirectoryPaths; + struct BlobImplOrDirectoryPath + { + RefPtr mBlobImpl; + nsString mDirectoryPath; + + enum { + eBlobImpl, + eDirectoryPath + } mType; + }; + + nsTArray mBlobImplsOrDirectoryPaths; bool mChecked; bool mCheckedSet; diff --git a/dom/html/test/forms/mochitest.ini b/dom/html/test/forms/mochitest.ini index 0e4a1d8d36..cef6c516e4 100644 --- a/dom/html/test/forms/mochitest.ini +++ b/dom/html/test/forms/mochitest.ini @@ -2,6 +2,7 @@ support-files = save_restore_radio_groups.sjs test_input_number_data.js + !/dom/html/test/reflect.js [test_bug1039548.html] [test_button_attributes_reflection.html] diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index f64560c53c..9238ccabff 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -7782,7 +7782,7 @@ class NormalJSRuntime { friend class nsAutoPtr; - static const JSClass kGlobalClass; + static const JSClass sGlobalClass; static const uint32_t kRuntimeHeapSize = 768 * 1024; JSRuntime* mRuntime; @@ -23228,9 +23228,7 @@ CreateIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) return NS_OK; } -const JSClass NormalJSRuntime::kGlobalClass = { - "IndexedDBTransactionThreadGlobal", - JSCLASS_GLOBAL_FLAGS, +static const JSClassOps sNormalJSRuntimeGlobalClassOps = { /* addProperty */ nullptr, /* delProperty */ nullptr, /* getProperty */ nullptr, @@ -23245,6 +23243,12 @@ const JSClass NormalJSRuntime::kGlobalClass = { /* trace */ JS_GlobalObjectTraceHook }; +const JSClass NormalJSRuntime::sGlobalClass = { + "IndexedDBTransactionThreadGlobal", + JSCLASS_GLOBAL_FLAGS, + &sNormalJSRuntimeGlobalClassOps +}; + bool NormalJSRuntime::Init() { @@ -23266,7 +23270,7 @@ NormalJSRuntime::Init() JSAutoRequest ar(mContext); JS::CompartmentOptions options; - mGlobal = JS_NewGlobalObject(mContext, &kGlobalClass, nullptr, + mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr, JS::FireOnNewGlobalHook, options); if (NS_WARN_IF(!mGlobal)) { return false; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index b0853cc0df..fd5d6e59b5 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -70,6 +70,7 @@ #include "mozilla/unused.h" #include "mozInlineSpellChecker.h" +#include "nsDocShell.h" #include "nsIConsoleListener.h" #include "nsICycleCollectorListener.h" #include "nsIDragService.h" @@ -868,12 +869,29 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener, baseURI->GetSpec(baseURIString); } + nsCOMPtr opener = do_QueryInterface(aParent); + nsIDocShell* openerShell; + RefPtr openerDocShell; + float fullZoom = 1.0f; + if (opener && (openerShell = opener->GetDocShell())) { + openerDocShell = static_cast(openerShell); + nsCOMPtr cv; + openerDocShell->GetContentViewer(getter_AddRefs(cv)); + if (cv) { + cv->GetFullZoom(&fullZoom); + } + } + nsresult rv; if (!SendCreateWindow(aTabOpener, newChild, aChromeFlags, aCalledFromJS, aPositionSpecified, aSizeSpecified, url, name, features, baseURIString, + openerDocShell + ? openerDocShell->GetOriginAttributes() + : OriginAttributes(), + fullZoom, &rv, aWindowIsNew, &frameScripts, diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 929e651c48..0acd21638d 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5386,6 +5386,8 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab, const nsString& aName, const nsCString& aFeatures, const nsCString& aBaseURI, + const OriginAttributes& aOpenerOriginAttributes, + const float& aFullZoom, nsresult* aResult, bool* aWindowIsNew, InfallibleTArray* aFrameScripts, @@ -5546,7 +5548,7 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab, *aResult = pwwatch->OpenWindow2(parent, uri, name, features, aCalledFromJS, false, false, thisTabParent, nullptr, nullptr, - getter_AddRefs(window)); + aFullZoom, 1, getter_AddRefs(window)); if (NS_WARN_IF(NS_FAILED(*aResult))) { return true; diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 27c15d9038..f343acb937 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -497,6 +497,8 @@ public: const nsString& aName, const nsCString& aFeatures, const nsCString& aBaseURI, + const OriginAttributes& aOpenerOriginAttributes, + const float& aFullZoom, nsresult* aResult, bool* aWindowIsNew, InfallibleTArray* aFrameScripts, diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 5d616dcbfa..3f72afedba 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -310,17 +310,11 @@ struct FileSystemGetFileOrDirectoryParams bool isRoot; }; -union FileSystemPathOrFileValue -{ - nsString; - PBlob; -}; - struct FileSystemRemoveParams { nsString filesystem; nsString directory; - FileSystemPathOrFileValue target; + nsString targetDirectory; bool recursive; }; @@ -1179,7 +1173,9 @@ parent: nsCString aURI, nsString aName, nsCString aFeatures, - nsCString aBaseURI) + nsCString aBaseURI, + OriginAttributes aOpenerOriginAttributes, + float aFullZoom) returns (nsresult rv, bool windowOpened, FrameScriptInfo[] frameScripts, diff --git a/dom/manifest/ManifestObtainer.jsm b/dom/manifest/ManifestObtainer.jsm index f539363af1..88cba0d91a 100644 --- a/dom/manifest/ManifestObtainer.jsm +++ b/dom/manifest/ManifestObtainer.jsm @@ -66,7 +66,12 @@ this.ManifestObtainer = { // jshint ignore:line if (!aContent || isXULBrowser(aContent)) { throw new TypeError("Invalid input. Expected a DOM Window."); } - const manifest = yield fetchManifest(aContent); + let manifest; + try { + manifest = yield fetchManifest(aContent); + } catch (err) { + throw err; + } return manifest; } )}; @@ -134,42 +139,22 @@ const fetchManifest = Task.async(function* (aWindow) { } // Throws on malformed URLs const manifestURL = new aWindow.URL(elem.href, elem.baseURI); - if (!canLoadManifest(elem)) { - let msg = `Content Security Policy: The page's settings blocked the `; - msg += `loading of a resource at ${elem.href}`; - throw new Error(msg); - } const reqInit = { mode: "cors" }; if (elem.crossOrigin === "use-credentials") { reqInit.credentials = "include"; } - const req = new aWindow.Request(manifestURL, reqInit); - req.setContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST); - const response = yield aWindow.fetch(req); + const request = new aWindow.Request(manifestURL, reqInit); + request.overrideContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST); + let response; + try { + response = yield aWindow.fetch(request); + } catch (err) { + throw err; + } const manifest = yield processResponse(response, aWindow); return manifest; }); -/** - * Checks against security manager if we can load the web manifest. - * @param {HTMLLinkElement} aElem The HTML element to security check. - * @return {Boolean} True if it can, false if it can't. - */ -function canLoadManifest(aElem) { - const contentPolicy = Cc["@mozilla.org/layout/content-policy;1"] - .getService(Ci.nsIContentPolicy); - const mimeType = aElem.type || "application/manifest+json"; - const elemURI = BrowserUtils.makeURI( - aElem.href, aElem.ownerDocument.characterSet - ); - const shouldLoad = contentPolicy.shouldLoad( - Ci.nsIContentPolicy.TYPE_WEB_MANIFEST, elemURI, - aElem.ownerDocument.documentURIObject, - aElem, mimeType, null - ); - return shouldLoad === Ci.nsIContentPolicy.ACCEPT; -} - this.EXPORTED_SYMBOLS = ["ManifestObtainer"]; // jshint ignore:line diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index 5a8beec63b..0382adb99c 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -304,6 +304,8 @@ support-files = wavedata_u8.wav^headers^ wavedata_ulaw.wav wavedata_ulaw.wav^headers^ + !/dom/canvas/test/captureStream_common.js + !/dom/html/test/reflect.js [test_access_control.html] skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984 diff --git a/dom/media/tests/mochitest/identity/mochitest.ini b/dom/media/tests/mochitest/identity/mochitest.ini index d1b3c8f80a..2cb70686cf 100644 --- a/dom/media/tests/mochitest/identity/mochitest.ini +++ b/dom/media/tests/mochitest/identity/mochitest.ini @@ -6,6 +6,14 @@ skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' || an support-files = /.well-known/idp-proxy/idp.js identityPcTest.js + !/dom/media/tests/mochitest/blacksilence.js + !/dom/media/tests/mochitest/dataChannel.js + !/dom/media/tests/mochitest/head.js + !/dom/media/tests/mochitest/network.js + !/dom/media/tests/mochitest/pc.js + !/dom/media/tests/mochitest/sdpUtils.js + !/dom/media/tests/mochitest/templates.js + !/dom/media/tests/mochitest/turnConfig.js tags = msg [test_idpproxy.html] diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index d6eda5df78..767189a500 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -14,6 +14,14 @@ support-files = blacksilence.js turnConfig.js sdpUtils.js + !/dom/canvas/test/captureStream_common.js + !/dom/canvas/test/webgl-mochitest/webgl-util.js + !/dom/media/test/manifest.js + !/dom/media/test/320x240.ogv + !/dom/media/test/r11025_s16_c1.wav + !/dom/media/test/bug461281.ogg + !/dom/media/test/seek.webm + !/dom/media/test/gizmo.mp4 [test_dataChannel_basicAudio.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' # Bug 962984 for debug, bug 963244 for opt diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index e98b4fd1e8..2a814c2395 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -219,6 +219,26 @@ CreateNPObjectMember(NPP npp, JSContext *cx, JS::Handle id, NPVariant* getPropertyResult, JS::MutableHandle vp); +const static js::ClassOps sNPObjectJSWrapperClassOps = { + NPObjWrapper_AddProperty, + NPObjWrapper_DelProperty, + NPObjWrapper_GetProperty, + NPObjWrapper_SetProperty, + nullptr, + NPObjWrapper_Resolve, + nullptr, /* mayResolve */ + NPObjWrapper_Finalize, + NPObjWrapper_Call, + nullptr, /* hasInstance */ + NPObjWrapper_Construct, + nullptr, /* trace */ +}; + +const static js::ClassExtension sNPObjectJSWrapperClassExtension = { + nullptr, /* weakmapKeyDelegateOp */ + NPObjWrapper_ObjectMoved +}; + const static js::ObjectOps sNPObjectJSWrapperObjectOps = { nullptr, // lookupProperty nullptr, // defineProperty @@ -233,30 +253,14 @@ const static js::ObjectOps sNPObjectJSWrapperObjectOps = { nullptr, }; -const static js::Class sNPObjectJSWrapperClass = - { +const static js::Class sNPObjectJSWrapperClass = { NPRUNTIME_JSCLASS_NAME, JSCLASS_HAS_PRIVATE, - NPObjWrapper_AddProperty, - NPObjWrapper_DelProperty, - NPObjWrapper_GetProperty, - NPObjWrapper_SetProperty, - nullptr, - NPObjWrapper_Resolve, - nullptr, /* mayResolve */ - NPObjWrapper_Finalize, - NPObjWrapper_Call, - nullptr, /* hasInstance */ - NPObjWrapper_Construct, - nullptr, /* trace */ + &sNPObjectJSWrapperClassOps, JS_NULL_CLASS_SPEC, - { - false, /* isWrappedNative */ - nullptr, /* weakmapKeyDelegateOp */ - NPObjWrapper_ObjectMoved - }, + &sNPObjectJSWrapperClassExtension, &sNPObjectJSWrapperObjectOps - }; +}; typedef struct NPObjectMemberPrivate { JS::Heap npobjWrapper; @@ -281,14 +285,17 @@ NPObjectMember_Trace(JSTracer *trc, JSObject *obj); static bool NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp); -static const JSClass sNPObjectMemberClass = - { - "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE, - nullptr, nullptr, NPObjectMember_GetProperty, nullptr, - nullptr, nullptr, nullptr, - NPObjectMember_Finalize, NPObjectMember_Call, - nullptr, nullptr, NPObjectMember_Trace - }; +static const JSClassOps sNPObjectMemberClassOps = { + nullptr, nullptr, NPObjectMember_GetProperty, nullptr, + nullptr, nullptr, nullptr, + NPObjectMember_Finalize, NPObjectMember_Call, + nullptr, nullptr, NPObjectMember_Trace +}; + +static const JSClass sNPObjectMemberClass = { + "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE, + &sNPObjectMemberClassOps +}; static void OnWrapperDestroyed(); diff --git a/dom/plugins/test/mochitest/chrome.ini b/dom/plugins/test/mochitest/chrome.ini index 557d4f6a8c..c2b46b28d6 100644 --- a/dom/plugins/test/mochitest/chrome.ini +++ b/dom/plugins/test/mochitest/chrome.ini @@ -4,6 +4,7 @@ support-files = hang_test.js privatemode_perwindowpb.xul plugin-utils.js + !/toolkit/crashreporter/test/browser/crashreport.sjs [test_bug479979.xul] [test_bug751809.html] diff --git a/dom/plugins/test/mochitest/mochitest.ini b/dom/plugins/test/mochitest/mochitest.ini index 43382a2ea7..486308dae0 100644 --- a/dom/plugins/test/mochitest/mochitest.ini +++ b/dom/plugins/test/mochitest/mochitest.ini @@ -21,6 +21,7 @@ support-files = pluginstream.js post.sjs plugin-utils.js + !/toolkit/components/passwordmgr/test/authenticate.sjs [test_GCrace.html] [test_NPNVdocumentOrigin.html] diff --git a/dom/plugins/test/unit/xpcshell.ini b/dom/plugins/test/unit/xpcshell.ini index af511cb378..db2f081dd0 100644 --- a/dom/plugins/test/unit/xpcshell.ini +++ b/dom/plugins/test/unit/xpcshell.ini @@ -4,6 +4,8 @@ head = head_plugins.js tail = tags = addons firefox-appdir = browser +support-files = + !/toolkit/mozapps/extensions/test/xpcshell/head_addons.js [test_allowed_types.js] skip-if = appname == "thunderbird" diff --git a/dom/push/PushComponents.js b/dom/push/PushComponents.js index e14d4e26e3..dd1f9cf03b 100644 --- a/dom/push/PushComponents.js +++ b/dom/push/PushComponents.js @@ -255,6 +255,17 @@ Object.assign(PushServiceParent.prototype, { let name = requestName.slice("Push:".length); return "PushService:" + name + ":" + suffix; }, + + // Methods used for mocking in tests. + + replaceServiceBackend(options) { + this._service.changeTestServer(options.serverURI, options); + }, + + restoreServiceBackend() { + var defaultServerURL = Services.prefs.getCharPref("dom.push.serverURL"); + this._service.changeTestServer(defaultServerURL); + }, }); /** diff --git a/dom/push/PushService.jsm b/dom/push/PushService.jsm index 3bd215f3f5..3fc4e68dae 100644 --- a/dom/push/PushService.jsm +++ b/dom/push/PushService.jsm @@ -229,6 +229,20 @@ this.PushService = { } }, + // Used for testing. + changeTestServer(url, options = {}) { + console.debug("changeTestServer()"); + + this._stateChangeProcessEnqueue(_ => { + if (this._state < PUSH_SERVICE_ACTIVATING) { + console.debug("changeTestServer: PushService not activated?"); + return Promise.resolve(); + } + + return this._changeServerURL(url, CHANGING_SERVICE_EVENT, options); + }); + }, + observe: function observe(aSubject, aTopic, aData) { switch (aTopic) { /* @@ -361,7 +375,7 @@ this.PushService = { return [service, uri]; }, - _changeServerURL: function(serverURI, event) { + _changeServerURL: function(serverURI, event, options = {}) { console.debug("changeServerURL()"); switch(event) { @@ -386,7 +400,7 @@ this.PushService = { if (this._state == PUSH_SERVICE_INIT) { this._setState(PUSH_SERVICE_ACTIVATING); // The service has not been running - start it. - return this._startService(service, uri) + return this._startService(service, uri, options) .then(_ => this._stateChangeProcessEnqueue(_ => this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"))) ); @@ -398,7 +412,7 @@ this.PushService = { // check is called in changeStateConnectionEnabledEvent function) return this._stopService(CHANGING_SERVICE_EVENT) .then(_ => - this._startService(service, uri) + this._startService(service, uri, options) ) .then(_ => this._stateChangeProcessEnqueue(_ => this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"))) @@ -603,9 +617,10 @@ this.PushService = { this._stateChangeProcessEnqueue(_ => { - this._changeServerURL("", UNINIT_EVENT); + var p = this._changeServerURL("", UNINIT_EVENT); this._setState(PUSH_SERVICE_UNINIT); console.debug("uninit: shutdown complete!"); + return p; }); }, diff --git a/dom/push/test/mochitest.ini b/dom/push/test/mochitest.ini index 7d176125d9..d9339aa4bd 100644 --- a/dom/push/test/mochitest.ini +++ b/dom/push/test/mochitest.ini @@ -2,32 +2,21 @@ subsuite = push support-files = worker.js - push-server.sjs frame.html webpush.js lifetime_worker.js test_utils.js + mockpushserviceparent.js +skip-if = os == "android" || toolkit == "gonk" [test_has_permissions.html] -skip-if = os == "android" || toolkit == "gonk" [test_permissions.html] -skip-if = os == "android" || toolkit == "gonk" [test_register.html] -skip-if = os == "android" || toolkit == "gonk" [test_multiple_register.html] -skip-if = os == "android" || toolkit == "gonk" [test_multiple_register_during_service_activation.html] -skip-if = os == "android" || toolkit == "gonk" [test_unregister.html] -skip-if = os == "android" || toolkit == "gonk" [test_multiple_register_different_scope.html] -skip-if = os == "android" || toolkit == "gonk" [test_subscription_change.html] -skip-if = os == "android" || toolkit == "gonk" [test_data.html] -skip-if = os == "android" || toolkit == "gonk" -# Disabled for too many intermittent failures (bug 1164432) -# [test_try_registering_offline_disabled.html] -# skip-if = os == "android" || toolkit == "gonk" +[test_try_registering_offline_disabled.html] [test_serviceworker_lifetime.html] -skip-if = os == "android" || toolkit == "gonk" diff --git a/dom/push/test/mockpushserviceparent.js b/dom/push/test/mockpushserviceparent.js new file mode 100644 index 0000000000..344dbf249a --- /dev/null +++ b/dom/push/test/mockpushserviceparent.js @@ -0,0 +1,115 @@ +"use strict"; + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +/** + * Defers one or more callbacks until the next turn of the event loop. Multiple + * callbacks are executed in order. + * + * @param {Function[]} callbacks The callbacks to execute. One callback will be + * executed per tick. + */ +function waterfall(...callbacks) { + callbacks.reduce((promise, callback) => promise.then(() => { + callback(); + }), Promise.resolve()).catch(Cu.reportError); +} + +/** + * Minimal implementation of a mock WebSocket connect to be used with + * PushService. Forwards and receive messages from the implementation + * that lives in the content process. + */ +function MockWebSocketParent(originalURI) { + this._originalURI = originalURI; +} + +MockWebSocketParent.prototype = { + _originalURI: null, + + _listener: null, + _context: null, + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsISupports, + Ci.nsIWebSocketChannel + ]), + + get originalURI() { + return this._originalURI; + }, + + asyncOpen(uri, origin, windowId, listener, context) { + this._listener = listener; + this._context = context; + waterfall(() => this._listener.onStart(this._context)); + }, + + sendMsg(msg) { + sendAsyncMessage("client-msg", msg); + }, + + close() { + waterfall(() => this._listener.onStop(this._context, Cr.NS_OK)); + }, + + serverSendMsg(msg) { + waterfall(() => this._listener.onMessageAvailable(this._context, msg), + () => this._listener.onAcknowledge(this._context, 0)); + }, +}; + +function MockNetworkInfo() {} + +MockNetworkInfo.prototype = { + getNetworkInformation() { + return {mcc: '', mnc: '', ip: ''}; + }, + + getNetworkState(callback) { + callback({mcc: '', mnc: '', ip: '', netid: ''}); + }, + + getNetworkStateChangeEventName() { + return 'network:offline-status-changed'; + } +}; + +var pushService = Cc["@mozilla.org/push/Service;1"]. + getService(Ci.nsIPushService). + wrappedJSObject; + +var mockWebSocket; + +addMessageListener("setup", function () { + mockWebSocket = new Promise((resolve, reject) => { + var mockSocket = null; + pushService.replaceServiceBackend({ + serverURI: "wss://push.example.org/", + networkInfo: new MockNetworkInfo(), + makeWebSocket(uri) { + if (!mockSocket) { + mockSocket = new MockWebSocketParent(uri); + resolve(mockSocket); + } + + return mockSocket; + } + }); + }); +}); + +addMessageListener("teardown", function () { + mockWebSocket.then(socket => { + socket.close(); + pushService.restoreServiceBackend(); + }); +}); + +addMessageListener("server-msg", function (msg) { + mockWebSocket.then(socket => { + socket.serverSendMsg(msg); + }); +}); diff --git a/dom/push/test/push-server.sjs b/dom/push/test/push-server.sjs deleted file mode 100644 index 8beaedcbbe..0000000000 --- a/dom/push/test/push-server.sjs +++ /dev/null @@ -1,52 +0,0 @@ -function debug(str) { -// dump("@@@ push-server " + str + "\n"); -} - -function concatUint8Arrays(arrays, size) { - let index = 0; - return arrays.reduce((result, a) => { - result.set(new Uint8Array(a), index); - index += a.byteLength; - return result; - }, new Uint8Array(size)); -} - -function handleRequest(request, response) -{ - debug("handling request!"); - - const Cc = Components.classes; - const Ci = Components.interfaces; - - let params = request.getHeader("X-Push-Server"); - debug("params = " + params); - - let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); - xhr.open(request.getHeader("X-Push-Method"), params); - - for (let headers = request.headers; headers.hasMoreElements();) { - let header = headers.getNext().QueryInterface(Ci.nsISupportsString).data; - if (header.toLowerCase() != "x-push-server") { - xhr.setRequestHeader(header, request.getHeader(header)); - } - } - - let bodyStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); - bodyStream.setInputStream(request.bodyInputStream); - let size = 0; - let data = []; - for (let available; available = bodyStream.available();) { - data.push(bodyStream.readByteArray(available)); - size += available; - } - xhr.send(concatUint8Arrays(data, size)); - - xhr.onload = function(e) { - debug("xhr : " + this.status); - } - xhr.onerror = function(e) { - debug("xhr error: " + e); - } - - response.setStatusLine(request.httpVersion, "200", "OK"); -} diff --git a/dom/push/test/test_data.html b/dom/push/test/test_data.html index d1ed78bf5e..0bbb816956 100644 --- a/dom/push/test/test_data.html +++ b/dom/push/test/test_data.html @@ -25,10 +25,24 @@ http://creativecommons.org/licenses/publicdomain/ + @@ -118,12 +119,7 @@ http://creativecommons.org/licenses/publicdomain/ }).then(SimpleTest.finish); } - SpecialPowers.pushPrefEnv({"set": [ - ["dom.push.enabled", true], - ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true] - ]}, runTest); + setupPrefsAndMock(new MockWebSocket()).then(_ => runTest()); SpecialPowers.addPermission('push', true, document); SimpleTest.waitForExplicitFinish(); diff --git a/dom/push/test/test_multiple_register_different_scope.html b/dom/push/test/test_multiple_register_different_scope.html index 3915c54b8d..c626ed5608 100644 --- a/dom/push/test/test_multiple_register_different_scope.html +++ b/dom/push/test/test_multiple_register_different_scope.html @@ -10,6 +10,7 @@ http://creativecommons.org/licenses/publicdomain/ Test for Bug 1150812 + @@ -113,12 +114,7 @@ http://creativecommons.org/licenses/publicdomain/ }).then(SimpleTest.finish); } - SpecialPowers.pushPrefEnv({"set": [ - ["dom.push.enabled", true], - ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true] - ]}, runTest); + setupPrefsAndMock(new MockWebSocket()).then(_ => runTest()); SpecialPowers.addPermission('push', true, document); SimpleTest.waitForExplicitFinish(); diff --git a/dom/push/test/test_multiple_register_during_service_activation.html b/dom/push/test/test_multiple_register_during_service_activation.html index 2c6dcb7904..efbfcdd281 100644 --- a/dom/push/test/test_multiple_register_during_service_activation.html +++ b/dom/push/test/test_multiple_register_during_service_activation.html @@ -12,6 +12,7 @@ http://creativecommons.org/licenses/publicdomain/ Test for Bug 1150812 + @@ -54,13 +55,11 @@ http://creativecommons.org/licenses/publicdomain/ }); } -var defaultServerURL = SpecialPowers.getCharPref("dom.push.serverURL"); - function setupMultipleSubscriptions(swr) { - // We need to do this to restart service so that a queue will be formed. - SpecialPowers.pushPrefEnv({"set": [["dom.push.serverURL", defaultServerURL]]}, - null); + teardownMockPushService(); + setupMockPushService(new MockWebSocket()); + return Promise.all([ subscribe(swr), subscribe(swr) @@ -100,13 +99,7 @@ var defaultServerURL = SpecialPowers.getCharPref("dom.push.serverURL"); }).then(SimpleTest.finish); } - SpecialPowers.pushPrefEnv({"set": [ - ["dom.push.enabled", true], - ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true], - ["dom.push.serverURL", "wss://something.org"] - ]}, runTest); + setupPrefsAndMock(new MockWebSocket()).then(_ => runTest()); SpecialPowers.addPermission('push', true, document); SimpleTest.waitForExplicitFinish(); diff --git a/dom/push/test/test_register.html b/dom/push/test/test_register.html index e5e4645922..31925dfee4 100644 --- a/dom/push/test/test_register.html +++ b/dom/push/test/test_register.html @@ -29,9 +29,24 @@ http://creativecommons.org/licenses/publicdomain/ // console.log(str + "\n"); } + var mockSocket = new MockWebSocket(); + + var channelID = null; + + mockSocket.onRegister = function(request) { + channelID = request.channelID; + this.serverSendMsg(JSON.stringify({ + messageType: "register", + uaid: "c69e2014-9e15-438d-b253-d79cc2df60a8", + channelID, + status: 200, + pushEndpoint: "https://example.com/endpoint/1" + })); + }; + var registration; add_task(function* start() { - yield setupPrefs(); + yield setupPrefsAndMock(mockSocket); yield setPushPermission(true); var url = "worker.js" + "?" + (Math.random()); @@ -64,16 +79,16 @@ http://creativecommons.org/licenses/publicdomain/ }); add_task(function* waitForPushNotification() { - yield Promise.all([ - controlledFrame.waitOnWorkerMessage("finished"), - fetch("http://mochi.test:8888/tests/dom/push/test/push-server.sjs", { - method: "PUT", - headers: { - "X-Push-Method": "POST", - "X-Push-Server": pushSubscription.endpoint, - }, - }), - ]); + var finishedPromise = controlledFrame.waitOnWorkerMessage("finished"); + + // Send a blank message. + mockSocket.serverSendMsg(JSON.stringify({ + messageType: "notification", + version: "vDummy", + channelID: channelID + })); + + yield finishedPromise; }); add_task(function* unsubscribe() { diff --git a/dom/push/test/test_serviceworker_lifetime.html b/dom/push/test/test_serviceworker_lifetime.html index d305cba805..3219680dc6 100644 --- a/dom/push/test/test_serviceworker_lifetime.html +++ b/dom/push/test/test_serviceworker_lifetime.html @@ -23,6 +23,7 @@ Test for Bug 1188545 + @@ -71,13 +72,27 @@ return p; } + var mockSocket = new MockWebSocket(); + var endpoint = "https://example.com/endpoint/1"; + var channelID = null; + mockSocket.onRegister = function(request) { + channelID = request.channelID; + this.serverSendMsg(JSON.stringify({ + messageType: "register", + uaid: "fa8f2e4b-5ddc-4408-b1e3-5f25a02abff0", + channelID, + status: 200, + pushEndpoint: endpoint + })); + }; + function sendPushToPushServer(pushEndpoint) { - // Work around CORS for now. - var xhr = new XMLHttpRequest(); - xhr.open('GET', "http://mochi.test:8888/tests/dom/push/test/push-server.sjs", true); - xhr.setRequestHeader("X-Push-Method", "PUT"); - xhr.setRequestHeader("X-Push-Server", pushEndpoint); - xhr.send("version=24601"); + is(pushEndpoint, endpoint, "Got unexpected endpoint"); + mockSocket.serverSendMsg(JSON.stringify({ + messageType: "notification", + version: "vDummy", + channelID + })); } function unregisterPushNotification(ctx) { @@ -317,12 +332,7 @@ }).then(SimpleTest.finish); } - SpecialPowers.pushPrefEnv({"set": [ - ["dom.push.enabled", true], - ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true], - ]}, runTest); + setupPrefsAndMock(mockSocket).then(_ => runTest()); SpecialPowers.addPermission('desktop-notification', true, document); SimpleTest.waitForExplicitFinish(); diff --git a/dom/push/test/test_subscription_change.html b/dom/push/test/test_subscription_change.html index 7b8ef0a214..a5814c25cf 100644 --- a/dom/push/test/test_subscription_change.html +++ b/dom/push/test/test_subscription_change.html @@ -27,7 +27,7 @@ http://creativecommons.org/licenses/publicdomain/ var registration; add_task(function* start() { - yield setupPrefs(); + yield setupPrefsAndMock(new MockWebSocket()); yield setPushPermission(true); var url = "worker.js" + "?" + (Math.random()); diff --git a/dom/push/test/test_try_registering_offline_disabled.html b/dom/push/test/test_try_registering_offline_disabled.html index 0b61b80b70..f77675d7d2 100644 --- a/dom/push/test/test_try_registering_offline_disabled.html +++ b/dom/push/test/test_try_registering_offline_disabled.html @@ -10,6 +10,7 @@ http://creativecommons.org/licenses/publicdomain/ Test for Bug 1150812 + @@ -86,6 +87,17 @@ http://creativecommons.org/licenses/publicdomain/ }); } + // Load chrome script to change offline status in the + // parent process. + var chromeScript = SpecialPowers.loadChromeScript(_ => { + var { classes: Cc, interfaces: Ci } = Components; + var ioService = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + addMessageListener("change-status", function(offline) { + ioService.offline = offline; + }); + }); + function offlineObserver(res) { this._res = res; } @@ -110,16 +122,13 @@ http://creativecommons.org/licenses/publicdomain/ obsService.addObserver(SpecialPowers.wrapCallbackObject(new offlineObserver(res)), "network:offline-status-changed", false); - var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] - .getService(SpecialPowers.Ci.nsIIOService); - ioService.offline = offline; + chromeScript.sendAsyncMessage("change-status", offline); }); } function changePushServerConnectionEnabled(enable) { debug("changePushServerConnectionEnabled"); - SpecialPowers.pushPrefEnv({"set": [["dom.push.connection.enabled", enable]]}, - null); + SpecialPowers.setBoolPref("dom.push.connection.enabled", enable); } function unsubscribe(sub) { @@ -287,12 +296,7 @@ http://creativecommons.org/licenses/publicdomain/ .then(SimpleTest.finish); } - SpecialPowers.pushPrefEnv({"set": [ - ["dom.push.enabled", true], - ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true] - ]}, runTest); + setupPrefsAndMock(new MockWebSocket()).then(_ => runTest()); SpecialPowers.addPermission('push', true, document); SimpleTest.waitForExplicitFinish(); diff --git a/dom/push/test/test_unregister.html b/dom/push/test/test_unregister.html index 15459a5955..ad00da297a 100644 --- a/dom/push/test/test_unregister.html +++ b/dom/push/test/test_unregister.html @@ -10,6 +10,7 @@ http://creativecommons.org/licenses/publicdomain/ Test for Bug 1170817 + @@ -79,12 +80,7 @@ http://creativecommons.org/licenses/publicdomain/ }).then(SimpleTest.finish); } - SpecialPowers.pushPrefEnv({"set": [ - ["dom.push.enabled", true], - ["dom.serviceWorkers.exemptFromPerDomainMax", true], - ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true] - ]}, runTest); + setupPrefsAndMock(new MockWebSocket()).then(_ => runTest()); SpecialPowers.addPermission('push', true, document); SimpleTest.waitForExplicitFinish(); diff --git a/dom/push/test/test_utils.js b/dom/push/test/test_utils.js index f18363bab0..b109ec8818 100644 --- a/dom/push/test/test_utils.js +++ b/dom/push/test/test_utils.js @@ -1,11 +1,115 @@ +(function (g) { + "use strict"; + + let url = SimpleTest.getTestFileURL("mockpushserviceparent.js"); + let chromeScript = SpecialPowers.loadChromeScript(url); + + let userAgentID = "8e1c93a9-139b-419c-b200-e715bb1e8ce8"; + + let currentMockSocket = null; + + function setupMockPushService(mockWebSocket) { + currentMockSocket = mockWebSocket; + currentMockSocket._isActive = true; + chromeScript.sendSyncMessage("setup"); + chromeScript.addMessageListener("client-msg", function(msg) { + mockWebSocket.handleMessage(msg); + }); + } + + function teardownMockPushService() { + if (currentMockSocket) { + currentMockSocket._isActive = false; + chromeScript.sendSyncMessage("teardown"); + } + } + + /** + * Minimal implementation of web sockets for use in testing. Forwards + * messages to a mock web socket in the parent process that is used + * by the push service. + */ + function MockWebSocket() {} + + let registerCount = 0; + + // Default implementation to make the push server work minimally. + // Override methods to implement custom functionality. + MockWebSocket.prototype = { + // We only allow one active mock web socket to talk to the parent. + // This flag is used to keep track of which mock web socket is active. + _isActive: false, + + onHello(request) { + this.serverSendMsg(JSON.stringify({ + messageType: "hello", + uaid: userAgentID, + status: 200, + use_webpush: true, + })); + }, + + onRegister(request) { + this.serverSendMsg(JSON.stringify({ + messageType: "register", + uaid: userAgentID, + channelID: request.channelID, + status: 200, + pushEndpoint: "https://example.com/endpoint/" + registerCount++ + })); + }, + + onUnregister(request) { + // Do nothing. + }, + + onAck(request) { + // Do nothing. + }, + + handleMessage(msg) { + let request = JSON.parse(msg); + let messageType = request.messageType; + switch (messageType) { + case "hello": + this.onHello(request); + break; + case "register": + this.onRegister(request); + break; + case "unregister": + this.onUnregister(request); + break; + case "ack": + this.onAck(request); + break; + default: + throw new Error("Unexpected message: " + messageType); + } + }, + + serverSendMsg(msg) { + if (this._isActive) { + chromeScript.sendAsyncMessage("server-msg", msg); + } + }, + }; + + g.MockWebSocket = MockWebSocket; + g.setupMockPushService = setupMockPushService; + g.teardownMockPushService = teardownMockPushService; +}(this)); + // Remove permissions and prefs when the test finishes. -SimpleTest.registerCleanupFunction(() => +SimpleTest.registerCleanupFunction(() => { new Promise(resolve => { SpecialPowers.flushPermissions(_ => { SpecialPowers.flushPrefEnv(resolve); }); - }) -); + }).then(_ => { + teardownMockPushService(); + }); +}); function setPushPermission(allow) { return new Promise(resolve => { @@ -15,10 +119,12 @@ function setPushPermission(allow) { }); } -function setupPrefs() { +function setupPrefsAndMock(mockSocket) { return new Promise(resolve => { + setupMockPushService(mockSocket); SpecialPowers.pushPrefEnv({"set": [ ["dom.push.enabled", true], + ["dom.push.connection.enabled", true], ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true] diff --git a/dom/push/test/webpush.js b/dom/push/test/webpush.js index 093a7a44e3..6aacc5ae1b 100644 --- a/dom/push/test/webpush.js +++ b/dom/push/test/webpush.js @@ -161,15 +161,7 @@ }).then(bsConcat); } - /* - * Request push for a message. This returns a promise that resolves when the - * push has been delivered to the push service. - * - * @param subscription A PushSubscription that contains endpoint and p256dh - * parameters. - * @param data The message to send. - */ - function webpush(subscription, data) { + function webPushEncrypt(subscription, data) { data = ensureView(data); var salt = g.crypto.getRandomValues(new Uint8Array(16)); @@ -181,26 +173,14 @@ webCrypto.exportKey('raw', localKey.publicKey), ]); }).then(([payload, pubkey]) => { - var options = { - method: 'PUT', - headers: { - 'X-Push-Server': subscription.endpoint, - // Web Push requires POST requests. - 'X-Push-Method': 'POST', - 'Encryption-Key': 'keyid=p256dh;dh=' + base64url.encode(pubkey), - Encryption: 'keyid=p256dh;salt=' + base64url.encode(salt), - 'Content-Encoding': 'aesgcm128' - }, - body: payload, + return { + data: base64url.encode(payload), + encryption: 'keyid=p256dh;salt=' + base64url.encode(salt), + encryption_key: 'keyid=p256dh;dh=' + base64url.encode(pubkey), + encoding: 'aesgcm128' }; - return fetch('http://mochi.test:8888/tests/dom/push/test/push-server.sjs', options); - }).then(response => { - if (response.status / 100 !== 2) { - throw new Error('Unable to deliver message'); - } - return response; }); } - g.webpush = webpush; + g.webPushEncrypt = webPushEncrypt; }(this)); diff --git a/dom/push/test/xpcshell/test_notification_http2.js b/dom/push/test/xpcshell/test_notification_http2.js index b60ade9b4a..4517cae2a2 100644 --- a/dom/push/test/xpcshell/test_notification_http2.js +++ b/dom/push/test/xpcshell/test_notification_http2.js @@ -9,6 +9,8 @@ const {PushDB, PushService, PushServiceHttp2} = serviceExports; var prefs; var tlsProfile; +var pushEnabled; +var pushConnectionEnabled; var serverPort = -1; @@ -19,14 +21,19 @@ function run_test() { dump("using port " + serverPort + "\n"); do_get_profile(); + setPrefs(); prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile"); + pushEnabled = prefs.getBoolPref("dom.push.enabled"); + pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled"); // Set to allow the cert presented by our H2 server var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit"); prefs.setIntPref("network.http.speculative-parallel-limit", 0); prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false); + prefs.setBoolPref("dom.push.enabled", true); + prefs.setBoolPref("dom.push.connection.enabled", true); addCertOverride("localhost", serverPort, Ci.nsICertOverrideService.ERROR_UNTRUSTED | @@ -155,4 +162,6 @@ add_task(function* test_pushNotifications() { add_task(function* test_complete() { prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile); + prefs.setBoolPref("dom.push.enabled", pushEnabled); + prefs.setBoolPref("dom.push.connection.enabled", pushConnectionEnabled); }); diff --git a/dom/push/test/xpcshell/test_register_success_http2.js b/dom/push/test/xpcshell/test_register_success_http2.js index 5217ebbfa3..e8f250d744 100644 --- a/dom/push/test/xpcshell/test_register_success_http2.js +++ b/dom/push/test/xpcshell/test_register_success_http2.js @@ -11,6 +11,8 @@ var prefs; var tlsProfile; var serverURL; var serverPort = -1; +var pushEnabled; +var pushConnectionEnabled; var db; function run_test() { @@ -22,11 +24,15 @@ function run_test() { prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile"); + pushEnabled = prefs.getBoolPref("dom.push.enabled"); + pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled"); // Set to allow the cert presented by our H2 server var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit"); prefs.setIntPref("network.http.speculative-parallel-limit", 0); prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false); + prefs.setBoolPref("dom.push.enabled", true); + prefs.setBoolPref("dom.push.connection.enabled", true); addCertOverride("localhost", serverPort, Ci.nsICertOverrideService.ERROR_UNTRUSTED | @@ -119,4 +125,6 @@ add_task(function* test_pushSubscriptionMissingLink2() { add_task(function* test_complete() { prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile); + prefs.setBoolPref("dom.push.enabled", pushEnabled); + prefs.setBoolPref("dom.push.connection.enabled", pushConnectionEnabled); }); diff --git a/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js b/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js index b3b8d24366..dcfdd2f3dd 100644 --- a/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js +++ b/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js @@ -50,6 +50,7 @@ function run_test() { servicePrefs.set('testing.notifyWorkers', false); servicePrefs.set('testing.notifyAllObservers', true); + setPrefs(); run_next_test(); } diff --git a/dom/push/test/xpcshell/test_unregister_success_http2.js b/dom/push/test/xpcshell/test_unregister_success_http2.js index c7c63f8293..d218f1c348 100644 --- a/dom/push/test/xpcshell/test_unregister_success_http2.js +++ b/dom/push/test/xpcshell/test_unregister_success_http2.js @@ -18,6 +18,8 @@ const {PushDB, PushService, PushServiceHttp2} = serviceExports; var prefs; var tlsProfile; +var pushEnabled; +var pushConnectionEnabled; var serverPort = -1; @@ -30,11 +32,15 @@ function run_test() { prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile"); + pushEnabled = prefs.getBoolPref("dom.push.enabled"); + pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled"); // Set to allow the cert presented by our H2 server var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit"); prefs.setIntPref("network.http.speculative-parallel-limit", 0); prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false); + prefs.setBoolPref("dom.push.enabled", true); + prefs.setBoolPref("dom.push.connection.enabled", true); addCertOverride("localhost", serverPort, Ci.nsICertOverrideService.ERROR_UNTRUSTED | @@ -81,4 +87,6 @@ add_task(function* test_pushUnsubscriptionSuccess() { add_task(function* test_complete() { prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile); + prefs.setBoolPref("dom.push.enabled", pushEnabled); + prefs.setBoolPref("dom.push.connection.enabled", pushConnectionEnabled); }); diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index a1fed7ffa3..8f0315337f 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -55,11 +55,14 @@ DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) flags |= nsIScriptSecurityManager::ALLOW_CHROME; } - rv = nsContentUtils::GetSecurityManager()-> - CheckLoadURIWithPrincipal(loadingPrincipal, - aURI, - flags); - NS_ENSURE_SUCCESS(rv, rv); + // We don't have a loadingPrincipal for TYPE_DOCUMENT + if (aLoadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) { + rv = nsContentUtils::GetSecurityManager()-> + CheckLoadURIWithPrincipal(loadingPrincipal, + aURI, + flags); + NS_ENSURE_SUCCESS(rv, rv); + } // If the loadingPrincipal and the triggeringPrincipal are different, then make // sure the triggeringPrincipal is allowed to access that URI. @@ -237,7 +240,8 @@ DoContentSecurityChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) } case nsIContentPolicy::TYPE_FONT: { - MOZ_ASSERT(false, "contentPolicyType not supported yet"); + mimeTypeGuess = EmptyCString(); + requestingContext = aLoadInfo->LoadingNode(); break; } @@ -372,14 +376,6 @@ nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel, rv = loadInfo->SetEnforceSecurity(true); NS_ENSURE_SUCCESS(rv, rv); - // Allow subresource loads if TriggeringPrincipal is the SystemPrincipal. - // For example, allow user stylesheets to load XBL from external files. - if (nsContentUtils::IsSystemPrincipal(loadInfo->TriggeringPrincipal()) && - loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT && - loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_SUBDOCUMENT) { - return NS_OK; - } - if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener); NS_ENSURE_SUCCESS(rv, rv); @@ -474,6 +470,10 @@ nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) // Handle cookie policies uint32_t cookiePolicy = loadInfo->GetCookiePolicy(); if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) { + + // We shouldn't have the SEC_COOKIES_SAME_ORIGIN flag for top level loads + MOZ_ASSERT(loadInfo->GetExternalContentPolicyType() != + nsIContentPolicy::TYPE_DOCUMENT); nsIPrincipal* loadingPrincipal = loadInfo->LoadingPrincipal(); // It doesn't matter what we pass for the third, data-inherits, argument. @@ -497,6 +497,14 @@ nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) return NS_OK; } + // Allow subresource loads if TriggeringPrincipal is the SystemPrincipal. + // For example, allow user stylesheets to load XBL from external files. + if (nsContentUtils::IsSystemPrincipal(loadInfo->TriggeringPrincipal()) && + loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT && + loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_SUBDOCUMENT) { + return NS_OK; + } + // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) || (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) { diff --git a/dom/security/test/cors/file_CrossSiteXHR_server.sjs b/dom/security/test/cors/file_CrossSiteXHR_server.sjs index 14fd4c3b27..66d1104684 100644 --- a/dom/security/test/cors/file_CrossSiteXHR_server.sjs +++ b/dom/security/test/cors/file_CrossSiteXHR_server.sjs @@ -52,10 +52,13 @@ function handleRequest(request, response) for(headerName in headers) { // Content-Type is changed if there was a body if (!(headerName == "Content-Type" && body) && - request.getHeader(headerName) != headers[headerName]) { + (!request.hasHeader(headerName) || + request.getHeader(headerName) != headers[headerName])) { + var actual = request.hasHeader(headerName) ? request.getHeader(headerName) + : ""; sendHttp500(response, "Header " + headerName + " had wrong value. Expected " + - headers[headerName] + " got " + request.getHeader(headerName)); + headers[headerName] + " got " + actual); return; } } @@ -151,6 +154,9 @@ function handleRequest(request, response) newURL = hops[query.hop].server + "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?" + "hop=" + (query.hop + 1) + "&hops=" + escape(query.hops); + if ("headers" in query) { + newURL += "&headers=" + escape(query.headers); + } response.setStatusLine(null, 307, "redirect"); response.setHeader("Location", newURL); diff --git a/dom/security/test/csp/browser.ini b/dom/security/test/csp/browser.ini index aa6dcc2f27..0846e87fe3 100644 --- a/dom/security/test/csp/browser.ini +++ b/dom/security/test/csp/browser.ini @@ -1,4 +1,13 @@ [DEFAULT] -skip-if = e10s # Bug 1170385 - csp-on-violate-policy message not sent in browser tests with e10s +support-files = + !/dom/security/test/csp/file_testserver.sjs + !/dom/security/test/csp/file_web_manifest.html + !/dom/security/test/csp/file_web_manifest.json + !/dom/security/test/csp/file_web_manifest.json^headers^ + !/dom/security/test/csp/file_web_manifest_https.html + !/dom/security/test/csp/file_web_manifest_https.json + !/dom/security/test/csp/file_web_manifest_mixed_content.html + !/dom/security/test/csp/file_web_manifest_remote.html [browser_test_web_manifest.js] [browser_test_web_manifest_mixed_content.js] +[browser_manifest-src-override-default-src.js] diff --git a/dom/security/test/csp/browser_manifest-src-override-default-src.js b/dom/security/test/csp/browser_manifest-src-override-default-src.js new file mode 100644 index 0000000000..0c4c7b7bc0 --- /dev/null +++ b/dom/security/test/csp/browser_manifest-src-override-default-src.js @@ -0,0 +1,108 @@ +/* + * Description of the tests: + * Tests check that default-src can be overridden by manifest-src. + */ +/*globals Cu, is, ok*/ +"use strict"; +const { + ManifestObtainer +} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {}); +const path = "/tests/dom/security/test/csp/"; +const testFile = `${path}file_web_manifest.html`; +const mixedContentFile = `${path}file_web_manifest_mixed_content.html`; +const server = `${path}file_testserver.sjs`; +const defaultURL = new URL(`http://example.org${server}`); +const mixedURL = new URL(`http://mochi.test:8888${server}`); +const tests = [ + // Check interaction with default-src and another origin, + // CSP allows fetching from example.org, so manifest should load. + { + expected: `CSP manifest-src overrides default-src of elsewhere.com`, + get tabURL() { + const url = new URL(defaultURL); + url.searchParams.append("file", testFile); + url.searchParams.append("cors", "*"); + url.searchParams.append("csp", "default-src http://elsewhere.com; manifest-src http://example.org"); + return url.href; + }, + run(manifest) { + is(manifest.name, "loaded", this.expected); + } + }, + // Check interaction with default-src none, + // CSP allows fetching manifest from example.org, so manifest should load. + { + expected: `CSP manifest-src overrides default-src`, + get tabURL() { + const url = new URL(mixedURL); + url.searchParams.append("file", mixedContentFile); + url.searchParams.append("cors", "http://test:80"); + url.searchParams.append("csp", "default-src 'self'; manifest-src http://test:80"); + return url.href; + }, + run(manifest) { + is(manifest.name, "loaded", this.expected); + } + }, +]; + +//jscs:disable +add_task(function* () { + //jscs:enable + const testPromises = tests.map((test) => { + const tabOptions = { + gBrowser, + url: test.tabURL, + skipAnimation: true, + }; + return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test)); + }); + yield Promise.all(testPromises); +}); + +function* testObtainingManifest(aBrowser, aTest) { + const expectsBlocked = aTest.expected.includes("block"); + const observer = (expectsBlocked) ? createNetObserver(aTest) : null; + // Expect an exception (from promise rejection) if there a content policy + // that is violated. + try { + const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser); + aTest.run(manifest); + } catch (e) { + const wasBlocked = e.message.includes("NetworkError when attempting to fetch resource"); + ok(wasBlocked,`Expected promise rejection obtaining ${aTest.tabURL}: ${e.message}`); + if (observer) { + yield observer.untilFinished; + } + } +} + +// Helper object used to observe policy violations. It waits 1 seconds +// for a response, and then times out causing its associated test to fail. +function createNetObserver(test) { + let finishedTest; + let success = false; + const finished = new Promise((resolver) => { + finishedTest = resolver; + }); + const timeoutId = setTimeout(() => { + if (!success) { + test.run("This test timed out."); + finishedTest(); + } + }, 1000); + var observer = { + get untilFinished(){ + return finished; + }, + observe(subject, topic) { + SpecialPowers.removeObserver(observer, "csp-on-violate-policy"); + test.run(topic); + finishedTest(); + clearTimeout(timeoutId); + success = true; + }, + }; + SpecialPowers.addObserver(observer, "csp-on-violate-policy", false); + return observer; +} diff --git a/dom/security/test/csp/browser_test_web_manifest.js b/dom/security/test/csp/browser_test_web_manifest.js index 8c5824d313..3991b425c3 100644 --- a/dom/security/test/csp/browser_test_web_manifest.js +++ b/dom/security/test/csp/browser_test_web_manifest.js @@ -5,260 +5,233 @@ * In particular, the tests check that default-src and manifest-src directives are * are respected by the ManifestObtainer. */ -/*globals Components*/ -'use strict'; -requestLongerTimeout(10); // e10s tests take time. +/*globals Cu, is, ok*/ +"use strict"; const { ManifestObtainer -} = Cu.import('resource://gre/modules/ManifestObtainer.jsm', {}); -const path = '/tests/dom/security/test/csp/'; -const testFile = `file=${path}file_web_manifest.html`; -const remoteFile = `file=${path}file_web_manifest_remote.html`; -const httpsManifest = `file=${path}file_web_manifest_https.html`; -const mixedContent = `file=${path}file_web_manifest_mixed_content.html`; -const server = 'file_testserver.sjs'; -const defaultURL = `http://example.org${path}${server}`; -const remoteURL = `http://mochi.test:8888`; -const secureURL = `https://example.com${path}${server}`; +} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {}); +const path = "/tests/dom/security/test/csp/"; +const testFile = `${path}file_web_manifest.html`; +const remoteFile = `${path}file_web_manifest_remote.html`; +const httpsManifest = `${path}file_web_manifest_https.html`; +const server = `${path}file_testserver.sjs`; +const defaultURL = new URL(`http://example.org${server}`); +const secureURL = new URL(`https://example.com:443${server}`); const tests = [ // CSP block everything, so trying to load a manifest // will result in a policy violation. { - expected: `default-src 'none' blocks fetching manifest.`, + expected: "default-src 'none' blocks fetching manifest.", get tabURL() { - let queryParts = [ - `csp=default-src 'none'`, - testFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", testFile); + url.searchParams.append("csp", "default-src 'none'"); + return url.href; }, run(topic) { - is(topic, 'csp-on-violate-policy', this.expected); + is(topic, "csp-on-violate-policy", this.expected); } }, // CSP allows fetching only from mochi.test:8888, // so trying to load a manifest from same origin // triggers a CSP violation. { - expected: `default-src mochi.test:8888 blocks manifest fetching.`, + expected: "default-src mochi.test:8888 blocks manifest fetching.", get tabURL() { - let queryParts = [ - `csp=default-src mochi.test:8888`, - testFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", testFile); + url.searchParams.append("csp", "default-src mochi.test:8888"); + return url.href; }, run(topic) { - is(topic, 'csp-on-violate-policy', this.expected); + is(topic, "csp-on-violate-policy", this.expected); } }, // CSP restricts fetching to 'self', so allowing the manifest // to load. The name of the manifest is then checked. { - expected: `CSP default-src 'self' allows fetch of manifest.`, + expected: "CSP default-src 'self' allows fetch of manifest.", get tabURL() { - let queryParts = [ - `csp=default-src 'self'`, - testFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", testFile); + url.searchParams.append("csp", "default-src 'self'"); + return url.href; }, run(manifest) { - is(manifest.name, 'loaded', this.expected); + is(manifest.name, "loaded", this.expected); } }, // CSP only allows fetching from mochi.test:8888 and remoteFile // requests a manifest from that origin, so manifest should load. { - expected: 'CSP default-src mochi.test:8888 allows fetching manifest.', + expected: "CSP default-src mochi.test:8888 allows fetching manifest.", get tabURL() { - let queryParts = [ - `csp=default-src http://mochi.test:8888`, - remoteFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", remoteFile); + url.searchParams.append("csp", "default-src http://mochi.test:8888"); + return url.href; }, run(manifest) { - is(manifest.name, 'loaded', this.expected); + is(manifest.name, "loaded", this.expected); } }, // default-src blocks everything, so any attempt to // fetch a manifest from another origin will trigger a // policy violation. { - expected: `default-src 'none' blocks mochi.test:8888`, + expected: "default-src 'none' blocks mochi.test:8888", get tabURL() { - let queryParts = [ - `csp=default-src 'none'`, - remoteFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", remoteFile); + url.searchParams.append("csp", "default-src 'none'"); + return url.href; }, run(topic) { - is(topic, 'csp-on-violate-policy', this.expected); + is(topic, "csp-on-violate-policy", this.expected); } }, // CSP allows fetching from self, so manifest should load. { - expected: `CSP manifest-src allows self`, + expected: "CSP manifest-src allows self", get tabURL() { - let queryParts = [ - `manifest-src 'self'`, - testFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", testFile); + url.searchParams.append("csp", "manifest-src 'self'"); + return url.href; }, run(manifest) { - is(manifest.name, 'loaded', this.expected); + is(manifest.name, "loaded", this.expected); } }, // CSP allows fetching from example.org, so manifest should load. { - expected: `CSP manifest-src allows http://example.org`, + expected: "CSP manifest-src allows http://example.org", get tabURL() { - let queryParts = [ - `manifest-src http://example.org`, - testFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", testFile); + url.searchParams.append("csp", "manifest-src http://example.org"); + return url.href; }, run(manifest) { - is(manifest.name, 'loaded', this.expected); + is(manifest.name, "loaded", this.expected); } - }, - // Check interaction with default-src and another origin, - // CSP allows fetching from example.org, so manifest should load. - { - expected: `CSP manifest-src overrides default-src of elsewhere.com`, + }, { + expected: "CSP manifest-src allows mochi.test:8888", get tabURL() { - let queryParts = [ - `default-src: http://elsewhere.com; manifest-src http://example.org`, - testFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", remoteFile); + url.searchParams.append("cors", "*"); + url.searchParams.append("csp", "default-src *; manifest-src http://mochi.test:8888"); + return url.href; }, run(manifest) { - is(manifest.name, 'loaded', this.expected); - } - }, - // Check interaction with default-src none, - // CSP allows fetching manifest from example.org, so manifest should load. - { - expected: `CSP manifest-src overrides default-src`, - get tabURL() { - let queryParts = [ - `default-src: 'none'; manifest-src 'self'`, - testFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; - }, - run(manifest) { - is(manifest.name, 'loaded', this.expected); - } - }, - // CSP allows fetching from mochi.test:8888, which has a - // CORS header set to '*'. So the manifest should load. - { - expected: `CSP manifest-src allows mochi.test:8888`, - get tabURL() { - let queryParts = [ - `csp=default-src *; manifest-src http://mochi.test:8888`, - remoteFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; - }, - run(manifest) { - is(manifest.name, 'loaded', this.expected); + is(manifest.name, "loaded", this.expected); } }, // CSP restricts fetching to mochi.test:8888, but the test // file is at example.org. Hence, a policy violation is // triggered. { - expected: `CSP blocks manifest fetching from example.org.`, + expected: "CSP blocks manifest fetching from example.org.", get tabURL() { - let queryParts = [ - `csp=manifest-src mochi.test:8888`, - testFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", testFile); + url.searchParams.append("csp", "manifest-src mochi.test:8888"); + return url.href; }, run(topic) { - is(topic, 'csp-on-violate-policy', this.expected); + is(topic, "csp-on-violate-policy", this.expected); } }, // CSP is set to only allow manifest to be loaded from same origin, // but the remote file attempts to load from a different origin. Thus // this causes a CSP violation. { - expected: `CSP manifest-src 'self' blocks cross-origin fetch.`, + expected: "CSP manifest-src 'self' blocks cross-origin fetch.", get tabURL() { - let queryParts = [ - `csp=manifest-src 'self'`, - remoteFile - ]; - return `${defaultURL}?${queryParts.join('&')}`; + const url = new URL(defaultURL); + url.searchParams.append("file", remoteFile); + url.searchParams.append("csp", "manifest-src 'self'"); + return url.href; }, run(topic) { - is(topic, 'csp-on-violate-policy', this.expected); + is(topic, "csp-on-violate-policy", this.expected); } - } + }, + // CSP allows fetching over TLS from example.org, so manifest should load. + { + expected: "CSP manifest-src allows example.com over TLS", + get tabURL() { + // secureURL loads https://example.com:443 + // and gets manifest from https://example.org:443 + const url = new URL(secureURL); + url.searchParams.append("file", httpsManifest); + url.searchParams.append("cors", "*"); + url.searchParams.append("csp", "manifest-src https://example.com:443"); + return url.href; + }, + run(manifest) { + is(manifest.name, "loaded", this.expected); + } + }, ]; //jscs:disable add_task(function* () { //jscs:enable - for (let test of tests) { - let tabOptions = { - gBrowser: gBrowser, + const testPromises = tests.map((test) => { + const tabOptions = { + gBrowser, url: test.tabURL, + skipAnimation: true, }; - yield BrowserTestUtils.withNewTab( - tabOptions, - browser => testObtainingManifest(browser, test) - ); - } - - function* testObtainingManifest(aBrowser, aTest) { - const observer = (/blocks/.test(aTest.expected)) ? new NetworkObserver(aTest) : null; - let manifest; - // Expect an exception (from promise rejection) if there a content policy - // that is violated. - try { - manifest = yield ManifestObtainer.browserObtainManifest(aBrowser); - } catch (e) { - const msg = `Expected promise rejection obtaining.`; - ok(/blocked the loading of a resource/.test(e.message), msg); - if (observer) { - yield observer.finished; - } - return; - } - // otherwise, we test manifest's content. - if (manifest) { - aTest.run(manifest); - } - } + return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test)); + }); + yield Promise.all(testPromises); }); -// Helper object used to observe policy violations. It waits 10 seconds +function* testObtainingManifest(aBrowser, aTest) { + const expectsBlocked = aTest.expected.includes("block"); + const observer = (expectsBlocked) ? createNetObserver(aTest) : null; + // Expect an exception (from promise rejection) if there a content policy + // that is violated. + try { + const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser); + aTest.run(manifest); + } catch (e) { + const wasBlocked = e.message.includes("NetworkError when attempting to fetch resource"); + ok(wasBlocked, `Expected promise rejection obtaining ${aTest.tabURL}: ${e.message}`); + if (observer) { + yield observer.untilFinished; + } + } +} + +// Helper object used to observe policy violations. It waits 1 seconds // for a response, and then times out causing its associated test to fail. -function NetworkObserver(test) { +function createNetObserver(test) { let finishedTest; let success = false; - this.finished = new Promise((resolver) => { + const finished = new Promise((resolver) => { finishedTest = resolver; - }) - this.observe = function observer(subject, topic) { - SpecialPowers.removeObserver(this, 'csp-on-violate-policy'); - test.run(topic); - finishedTest(); - success = true; - }; - SpecialPowers.addObserver(this, 'csp-on-violate-policy', false); - setTimeout(() => { + }); + const timeoutId = setTimeout(() => { if (!success) { - test.run('This test timed out.'); + test.run("This test timed out."); finishedTest(); } }, 1000); -} + var observer = { + get untilFinished() { + return finished; + }, + observe(subject, topic) { + SpecialPowers.removeObserver(observer, "csp-on-violate-policy"); + test.run(topic); + finishedTest(); + clearTimeout(timeoutId); + success = true; + }, + }; + SpecialPowers.addObserver(observer, "csp-on-violate-policy", false); + return observer; +} \ No newline at end of file diff --git a/dom/security/test/csp/browser_test_web_manifest_mixed_content.js b/dom/security/test/csp/browser_test_web_manifest_mixed_content.js index 38c96c7077..9238acbcdf 100644 --- a/dom/security/test/csp/browser_test_web_manifest_mixed_content.js +++ b/dom/security/test/csp/browser_test_web_manifest_mixed_content.js @@ -3,52 +3,51 @@ * Check that mixed content blocker works prevents fetches of * mixed content manifests. */ -'use strict'; +/*globals Cu, ok*/ +"use strict"; const { ManifestObtainer -} = Cu.import('resource://gre/modules/ManifestObtainer.jsm', {}); -const path = '/tests/dom/security/test/csp/'; -const mixedContent = `file=${path}file_web_manifest_mixed_content.html`; -const server = 'file_testserver.sjs'; -const secureURL = `https://example.com${path}${server}`; +} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {}); +const path = "/tests/dom/security/test/csp/"; +const mixedContent = `${path}file_web_manifest_mixed_content.html`; +const server = `${path}file_testserver.sjs`; +const secureURL = new URL(`https://example.com${server}`); const tests = [ // Trying to load mixed content in file_web_manifest_mixed_content.html // needs to result in an error. { - expected: `Mixed Content Blocker prevents fetching manifest.`, + expected: "Mixed Content Blocker prevents fetching manifest.", get tabURL() { - let queryParts = [ - mixedContent - ]; - return `${secureURL}?${queryParts.join('&')}`; + const url = new URL(secureURL); + url.searchParams.append("file", mixedContent); + return url.href; }, run(error) { // Check reason for error. - const check = /blocked the loading of a resource/.test(error.message); + const check = /NetworkError when attempting to fetch resource/.test(error.message); ok(check, this.expected); } } ]; //jscs:disable -add_task(function*() { +add_task(function* () { //jscs:enable - for (let test of tests) { - let tabOptions = { - gBrowser: gBrowser, + const testPromises = tests.map((test) => { + const tabOptions = { + gBrowser, url: test.tabURL, + skipAnimation: true, }; - yield BrowserTestUtils.withNewTab( - tabOptions, - browser => testObtainingManifest(browser, test) - ); - } - - function* testObtainingManifest(aBrowser, aTest) { - try { - yield ManifestObtainer.browserObtainManifest(aBrowser); - } catch (e) { - aTest.run(e) - } - } + return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test)); + }); + yield Promise.all(testPromises); }); + +function* testObtainingManifest(aBrowser, aTest) { + try { + yield ManifestObtainer.browserObtainManifest(aBrowser); + } catch (e) { + aTest.run(e); + } +} diff --git a/dom/security/test/csp/chrome.ini b/dom/security/test/csp/chrome.ini index 92862ebd6b..548e06ce76 100644 --- a/dom/security/test/csp/chrome.ini +++ b/dom/security/test/csp/chrome.ini @@ -1,5 +1,7 @@ [DEFAULT] -skip-if = buildapp == 'b2g' +skip-if = buildapp == 'b2g' || os == 'android' +support-files = + !/dom/security/test/csp/file_bug768029.html [test_bug768029.html] [test_bug773891.html] diff --git a/dom/security/test/csp/file_testserver.sjs b/dom/security/test/csp/file_testserver.sjs index 7bd32ae7d1..e10956c62c 100644 --- a/dom/security/test/csp/file_testserver.sjs +++ b/dom/security/test/csp/file_testserver.sjs @@ -1,53 +1,50 @@ // SJS file for CSP mochitests - +"use strict"; Components.utils.import("resource://gre/modules/NetUtil.jsm"); +Components.utils.importGlobalProperties(["URLSearchParams"]); function loadHTMLFromFile(path) { // Load the HTML to return in the response from file. // Since it's relative to the cwd of the test runner, we start there and // append to get to the actual path of the file. - var testHTMLFile = + const testHTMLFile = Components.classes["@mozilla.org/file/directory_service;1"]. getService(Components.interfaces.nsIProperties). get("CurWorkD", Components.interfaces.nsILocalFile); - var dirs = path.split("/"); - for (var i = 0; i < dirs.length; i++) { - testHTMLFile.append(dirs[i]); - } - var testHTMLFileStream = + + const testHTMLFileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. createInstance(Components.interfaces.nsIFileInputStream); + + path + .split("/") + .filter(path => path) + .reduce((file, path) => { + testHTMLFile.append(path) + return testHTMLFile; + }, testHTMLFile); testHTMLFileStream.init(testHTMLFile, -1, 0, 0); - var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available()); - return testHTML; + const isAvailable = testHTMLFileStream.available(); + return NetUtil.readInputStreamToString(testHTMLFileStream, isAvailable); } - -function handleRequest(request, response) -{ - var query = {}; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); - query[name] = unescape(value); - }); - - var csp = (query['csp']) ? unescape(query['csp']) : ""; - var file = unescape(query['file']); - var cors = unescape(query['cors']); +function handleRequest(request, response) { + const query = new URLSearchParams(request.queryString); // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); - // Deliver the CSP policy encoded in the URI - if(csp){ - response.setHeader("Content-Security-Policy", csp, false); + // Deliver the CSP policy encoded in the URL + if(query.has("csp")){ + response.setHeader("Content-Security-Policy", query.get("csp"), false); } - // Deliver the CORS header in the URI - if(cors){ - response.setHeader("Access-Control-Allow-Origin", cors, false); + + // Deliver the CORS header in the URL + if(query.has("cors")){ + response.setHeader("Access-Control-Allow-Origin", query.get("cors"), false); } + // Send HTML to test allowed/blocked behaviors response.setHeader("Content-Type", "text/html", false); - - response.write(loadHTMLFromFile(file)); + response.write(loadHTMLFromFile(query.get("file"))); } diff --git a/dom/security/test/mixedcontentblocker/mochitest.ini b/dom/security/test/mixedcontentblocker/mochitest.ini index f3a16e3e6d..2ce34c8b26 100644 --- a/dom/security/test/mixedcontentblocker/mochitest.ini +++ b/dom/security/test/mixedcontentblocker/mochitest.ini @@ -11,6 +11,8 @@ support-files = file_main_bug803225.html file_main_bug803225_websocket_wsh.py file_server.sjs + !/dom/media/test/320x240.ogv + !/image/test/mochitest/blue.png [test_main.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #TIMED_OUT, SSL_REQUIRED # Bug 1141029 Mulet parity with B2G Desktop for TC diff --git a/dom/tests/browser/browser.ini b/dom/tests/browser/browser.ini index 2326b1ac8e..6ea9165360 100644 --- a/dom/tests/browser/browser.ini +++ b/dom/tests/browser/browser.ini @@ -7,6 +7,7 @@ support-files = test_bug1004814.html worker_bug1004814.js geo_leak_test.html + !/dom/tests/mochitest/geolocation/network_geolocation.sjs [browser_test_toolbars_visibility.js] support-files = diff --git a/dom/tests/mochitest/chrome/chrome.ini b/dom/tests/mochitest/chrome/chrome.ini index f04c02cd38..e2e17fd619 100644 --- a/dom/tests/mochitest/chrome/chrome.ini +++ b/dom/tests/mochitest/chrome/chrome.ini @@ -28,6 +28,10 @@ support-files = window_docshell_swap.xul window_focus.xul window_focus_docnav.xul + !/dom/tests/mochitest/general/file_clonewrapper.html + !/dom/tests/mochitest/general/file_moving_nodeList.html + !/dom/tests/mochitest/general/file_moving_xhr.html + !/dom/tests/mochitest/geolocation/network_geolocation.sjs [test_DOMWindowCreated.xul] [test_DOM_element_instanceof.xul] diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/empty.js b/dom/tests/mochitest/fetch/empty.js similarity index 100% rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/empty.js rename to dom/tests/mochitest/fetch/empty.js diff --git a/dom/tests/mochitest/fetch/empty.js^headers^ b/dom/tests/mochitest/fetch/empty.js^headers^ new file mode 100644 index 0000000000..d0b9633bb0 --- /dev/null +++ b/dom/tests/mochitest/fetch/empty.js^headers^ @@ -0,0 +1 @@ +Service-Worker-Allowed: / diff --git a/dom/tests/mochitest/fetch/mochitest.ini b/dom/tests/mochitest/fetch/mochitest.ini index 0952705946..988c3b3f1f 100644 --- a/dom/tests/mochitest/fetch/mochitest.ini +++ b/dom/tests/mochitest/fetch/mochitest.ini @@ -16,6 +16,18 @@ support-files = reroute.js reroute.js^headers^ sw_reroute.js + empty.js + empty.js^headers^ + !/dom/base/test/file_XHR_binary1.bin + !/dom/base/test/file_XHR_binary1.bin^headers^ + !/dom/base/test/file_XHR_binary2.bin + !/dom/base/test/file_XHR_pass1.xml + !/dom/base/test/file_XHR_pass2.txt + !/dom/base/test/file_XHR_pass3.txt + !/dom/base/test/file_XHR_pass3.txt^headers^ + !/dom/base/test/responseIdentical.sjs + !/dom/html/test/form_submit_server.sjs + !/dom/security/test/cors/file_CrossSiteXHR_server.sjs [test_headers.html] [test_headers_sw_reroute.html] @@ -27,14 +39,20 @@ skip-if = (buildapp != 'b2g' && buildapp != 'mulet') [test_fetch_basic.html] skip-if = (e10s && debug && os == 'win') [test_fetch_basic_sw_reroute.html] -skip-if = buildapp == 'b2g' || (e10s && debug && os == 'win') # Bug 1137683 +skip-if = buildapp == 'b2g' # Bug 1137683 +[test_fetch_basic_sw_empty_reroute.html] +skip-if = buildapp == 'b2g' [test_fetch_basic_http.html] skip-if = (e10s && debug && os == 'win') [test_fetch_basic_http_sw_reroute.html] -skip-if = buildapp == 'b2g' || (e10s && debug && os == 'win') # Bug 1137683 +skip-if = buildapp == 'b2g' # Bug 1137683 +[test_fetch_basic_http_sw_empty_reroute.html] +skip-if = buildapp == 'b2g' [test_fetch_cors.html] skip-if = buildapp == 'b2g' || (toolkit == 'android' && debug) || (e10s && debug && os == 'win') # Bug 1210552 && 1210282 [test_fetch_cors_sw_reroute.html] +skip-if = buildapp == 'b2g' || (toolkit == 'android' && debug) # Bug 1137683 && 1210282 +[test_fetch_cors_sw_empty_reroute.html] skip-if = buildapp == 'b2g' || (toolkit == 'android' && debug) # Bug 1210282 [test_formdataparsing.html] skip-if = (e10s && debug && os == 'win') diff --git a/dom/tests/mochitest/fetch/reroute.html b/dom/tests/mochitest/fetch/reroute.html index b233aec85f..bb12212ea9 100644 --- a/dom/tests/mochitest/fetch/reroute.html +++ b/dom/tests/mochitest/fetch/reroute.html @@ -6,6 +6,13 @@ diff --git a/dom/tests/mochitest/fetch/sw_reroute.js b/dom/tests/mochitest/fetch/sw_reroute.js index 57c46c6c5a..631ec2b6d1 100644 --- a/dom/tests/mochitest/fetch/sw_reroute.js +++ b/dom/tests/mochitest/fetch/sw_reroute.js @@ -16,7 +16,9 @@ function testScript(script) { ["dom.serviceWorkers.exemptFromPerDomainMax", true]] }, function() { navigator.serviceWorker.ready.then(setupSW); - navigator.serviceWorker.register("reroute.js", {scope: "/"}); + var scriptURL = location.href.includes("sw_empty_reroute.html") + ? "empty.js" : "reroute.js"; + navigator.serviceWorker.register(scriptURL, {scope: "/"}); }); } diff --git a/dom/tests/mochitest/fetch/test_fetch_basic.js b/dom/tests/mochitest/fetch/test_fetch_basic.js index 8b968cc6a9..16ce9d8a34 100644 --- a/dom/tests/mochitest/fetch/test_fetch_basic.js +++ b/dom/tests/mochitest/fetch/test_fetch_basic.js @@ -72,10 +72,33 @@ function testSameOriginBlobURL() { }); } +function testNonGetBlobURL() { + var blob = new Blob(["english ", "sentence"], { type: "text/plain" }); + var url = URL.createObjectURL(blob); + return Promise.all( + [ + "HEAD", + "POST", + "PUT", + "DELETE" + ].map(method => { + var req = new Request(url, { method: method }); + return fetch(req).then(function(res) { + ok(false, "Blob URL with non-GET request should not succeed"); + }).catch(function(e) { + ok(e instanceof TypeError, "Blob URL with non-GET request should get a TypeError"); + }); + }) + ).then(function() { + URL.revokeObjectURL(url); + }); +} + function runTest() { return Promise.resolve() .then(testAboutURL) .then(testDataURL) .then(testSameOriginBlobURL) + .then(testNonGetBlobURL) // Put more promise based tests here. } diff --git a/dom/tests/mochitest/fetch/test_fetch_basic_http_sw_empty_reroute.html b/dom/tests/mochitest/fetch/test_fetch_basic_http_sw_empty_reroute.html new file mode 100644 index 0000000000..0f5052edac --- /dev/null +++ b/dom/tests/mochitest/fetch/test_fetch_basic_http_sw_empty_reroute.html @@ -0,0 +1,22 @@ + + + + + Bug 1039846 - Test fetch() http fetching in worker + + + + +

+ +

+
+
+
+
+
diff --git a/dom/tests/mochitest/fetch/test_fetch_basic_sw_empty_reroute.html b/dom/tests/mochitest/fetch/test_fetch_basic_sw_empty_reroute.html
new file mode 100644
index 0000000000..bcf959addb
--- /dev/null
+++ b/dom/tests/mochitest/fetch/test_fetch_basic_sw_empty_reroute.html
@@ -0,0 +1,22 @@
+
+
+
+
+  Bug 1039846 - Test fetch() function in worker
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/tests/mochitest/fetch/test_fetch_cors.js b/dom/tests/mochitest/fetch/test_fetch_cors.js
index 84ae68ed5a..188d35027a 100644
--- a/dom/tests/mochitest/fetch/test_fetch_cors.js
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.js
@@ -1444,6 +1444,10 @@ function testCORSRedirects() {
       body: test.body,
     };
 
+    if (test.headers) {
+      req.url += "&headers=" + escape(test.headers.toSource());
+    }
+
     if (test.pass) {
       if (test.body)
         req.url += "&body=" + escape(test.body);
@@ -1501,6 +1505,17 @@ function testNoCORSRedirects() {
                     },
                     ],
            },
+           { pass: 1,
+             method: "GET",
+             // Must use a simple header due to no-cors header restrictions.
+             headers: { "accept-language": "en-us",
+                      },
+             hops: [{ server: origin,
+                    },
+                    { server: "http://example.com",
+                    },
+                    ],
+           },
            { pass: 1,
              method: "GET",
              hops: [{ server: origin,
@@ -1540,6 +1555,10 @@ function testNoCORSRedirects() {
       body: test.body,
     };
 
+    if (test.headers) {
+      req.url += "&headers=" + escape(test.headers.toSource());
+    }
+
     if (test.pass) {
       if (test.body)
         req.url += "&body=" + escape(test.body);
diff --git a/dom/tests/mochitest/fetch/test_fetch_cors_sw_empty_reroute.html b/dom/tests/mochitest/fetch/test_fetch_cors_sw_empty_reroute.html
new file mode 100644
index 0000000000..7ad368cfd6
--- /dev/null
+++ b/dom/tests/mochitest/fetch/test_fetch_cors_sw_empty_reroute.html
@@ -0,0 +1,22 @@
+
+
+
+
+  Bug 1039846 - Test fetch() CORS mode
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/tests/mochitest/general/mochitest.ini b/dom/tests/mochitest/general/mochitest.ini
index 4e6fad5ff1..3996fd0033 100644
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -46,6 +46,12 @@ support-files =
   workerStoragePrevented.js
   storagePermissionsUtils.js
   frameSelectEvents.html
+  !/image/test/mochitest/big.png
+  !/image/test/mochitest/blue.png
+  !/image/test/mochitest/clear.png
+  !/image/test/mochitest/damon.jpg
+  !/image/test/mochitest/over.png
+  !/image/test/mochitest/red.png
 
 [test_497898.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
diff --git a/dom/webidl/Request.webidl b/dom/webidl/Request.webidl
index bfe2a1b36b..a7c05bc55c 100644
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -19,7 +19,8 @@ interface Request {
 
   [Func="mozilla::dom::Request::RequestContextEnabled"]
   readonly attribute RequestContext context;
-  readonly attribute DOMString referrer;
+  readonly attribute USVString referrer;
+  readonly attribute ReferrerPolicy referrerPolicy;
   readonly attribute RequestMode mode;
   readonly attribute RequestCredentials credentials;
   [Func="mozilla::dom::Request::RequestCacheEnabled"]
@@ -31,14 +32,16 @@ interface Request {
 
   // Bug 1124638 - Allow chrome callers to set the context.
   [ChromeOnly]
-  void setContentPolicyType(nsContentPolicyType context);
+  void overrideContentPolicyType(nsContentPolicyType context);
 };
 Request implements Body;
 
 dictionary RequestInit {
   ByteString method;
   HeadersInit headers;
-  BodyInit body;
+  BodyInit? body;
+  USVString referrer;
+  ReferrerPolicy referrerPolicy;
   RequestMode mode;
   RequestCredentials credentials;
   RequestCache cache;
@@ -59,3 +62,4 @@ enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
 enum RequestCredentials { "omit", "same-origin", "include" };
 enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache" };
 enum RequestRedirect { "follow", "error", "manual" };
+enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" };
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
index 0978b58689..fc461a2bf0 100644
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -555,6 +555,15 @@ LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */)
       continue;
     }
 
+    matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_refresh_frame_slices_enabled");
+    if (memPrefName == matchName ||
+        (gRuntimeServiceDuringInit && index == 15)) {
+      bool prefValue = GetWorkerPref(matchName, false);
+      UpdateOtherJSGCMemoryOption(rts, JSGC_REFRESH_FRAME_SLICES_ENABLED,
+                                 prefValue ? 0 : 1);
+      continue;
+    }
+
 #ifdef DEBUG
     nsAutoCString message("Workers don't support the 'mem.");
     message.Append(memPrefName);
diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp
index fdc315f899..d70db1eedf 100644
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -194,7 +194,7 @@ ChannelFromScriptURL(nsIPrincipal* principal,
 
   if (nsCOMPtr httpChannel = do_QueryInterface(channel)) {
     rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
-                                                       httpChannel);
+                                                       httpChannel, mozilla::net::RP_Default);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp
index 2f48518389..47bae64cb1 100644
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -503,6 +503,11 @@ public:
     if (NS_SUCCEEDED(rv)) {
       MOZ_ASSERT(window);
 
+      rv = nsContentUtils::DispatchFocusChromeEvent(window);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
       WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
       MOZ_ASSERT(workerPrivate);
 
@@ -601,7 +606,7 @@ private:
                            spec.get(),
                            nullptr,
                            nullptr,
-                           false, false, true, nullptr, nullptr, nullptr,
+                           false, false, true, nullptr, nullptr, nullptr, 1.0f, 0,
                            getter_AddRefs(newWindow));
       nsCOMPtr pwindow = do_QueryInterface(newWindow);
       pwindow.forget(aWindow);
diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp
index cfc581340b..7c3584e512 100644
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1102,6 +1102,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable
   nsContentPolicyType mContentPolicyType;
   nsCOMPtr mUploadStream;
   nsCString mReferrer;
+  ReferrerPolicy mReferrerPolicy;
 public:
   FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
                      KeepAliveToken* aKeepAliveToken,
@@ -1126,6 +1127,7 @@ public:
     , mRequestCredentials(RequestCredentials::Same_origin)
     , mContentPolicyType(nsIContentPolicy::TYPE_INVALID)
     , mReferrer(kFETCH_CLIENT_REFERRER_STR)
+    , mReferrerPolicy(ReferrerPolicy::_empty)
   {
     MOZ_ASSERT(aWorkerPrivate);
   }
@@ -1152,7 +1154,15 @@ public:
     rv = mInterceptedChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = uri->GetSpec(mSpec);
+    // Normally we rely on the Request constructor to strip the fragment, but
+    // when creating the FetchEvent we bypass the constructor.  So strip the
+    // fragment manually here instead.  We can't do it later when we create
+    // the Request because that code executes off the main thread.
+    nsCOMPtr uriNoFragment;
+    rv = uri->CloneIgnoringRef(getter_AddRefs(uriNoFragment));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = uriNoFragment->GetSpec(mSpec);
     NS_ENSURE_SUCCESS(rv, rv);
 
     uint32_t loadFlags;
@@ -1165,17 +1175,40 @@ public:
 
     mContentPolicyType = loadInfo->InternalContentPolicyType();
 
-    nsCOMPtr referrerURI;
-    rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrerURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (referrerURI) {
-      rv = referrerURI->GetSpec(mReferrer);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-
     nsCOMPtr httpChannel = do_QueryInterface(channel);
     MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
 
+    nsAutoCString referrer;
+    // Ignore the return value since the Referer header may not exist.
+    httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Referer"), referrer);
+    if (!referrer.IsEmpty()) {
+      mReferrer = referrer;
+    }
+
+    uint32_t referrerPolicy = 0;
+    rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
+    NS_ENSURE_SUCCESS(rv, rv);
+    switch (referrerPolicy) {
+    case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER:
+      mReferrerPolicy = ReferrerPolicy::No_referrer;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_ORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
+      mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL:
+      mReferrerPolicy = ReferrerPolicy::Unsafe_url;
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Invalid Referrer Policy enum value?");
+      break;
+    }
+
     rv = httpChannel->GetRequestMethod(mMethod);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1290,6 +1323,7 @@ private:
                                                               mRequestRedirect,
                                                               mRequestCredentials,
                                                               NS_ConvertUTF8toUTF16(mReferrer),
+                                                              mReferrerPolicy,
                                                               mContentPolicyType);
     internalReq->SetBody(mUploadStream);
     // For Telemetry, note that this Request object was created by a Fetch event.
diff --git a/dom/workers/ServiceWorkerWindowClient.cpp b/dom/workers/ServiceWorkerWindowClient.cpp
index 70cfae95f4..8b036f412b 100644
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -95,10 +95,7 @@ public:
     if (window) {
       nsCOMPtr doc = window->GetDocument();
       if (doc) {
-        nsContentUtils::DispatchChromeEvent(doc,
-                                            window->GetOuterWindow(),
-                                            NS_LITERAL_STRING("DOMServiceWorkerFocusClient"),
-                                            true, true);
+        nsContentUtils::DispatchFocusChromeEvent(window->GetOuterWindow());
         clientInfo.reset(new ServiceWorkerClientInfo(doc));
       }
     }
diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp
index da06e83a99..7059ecc90d 100644
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -711,7 +711,6 @@ WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx,
   aGlobal.set(scope->GetWrapper());
 }
 
-
 void
 WorkerDebuggerGlobalScope::CreateSandbox(JSContext* aCx, const nsAString& aName,
                                          JS::Handle aPrototype,
diff --git a/dom/workers/Workers.h b/dom/workers/Workers.h
index 11872144f0..6c8db6dd03 100644
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -92,6 +92,7 @@ struct JSSettings
     JSSettings_JSGC_SLICE_TIME_BUDGET,
     JSSettings_JSGC_DYNAMIC_HEAP_GROWTH,
     JSSettings_JSGC_DYNAMIC_MARK_SLICE,
+    JSSettings_JSGC_REFRESH_FRAME_SLICES,
     // JSGC_MODE not supported
 
     // This must be last so that we get an accurate count.
diff --git a/dom/workers/test/chrome.ini b/dom/workers/test/chrome.ini
index 6248081ef4..89af58f0db 100644
--- a/dom/workers/test/chrome.ini
+++ b/dom/workers/test/chrome.ini
@@ -10,10 +10,6 @@ support-files =
   WorkerDebugger.postMessage_childWorker.js
   WorkerDebugger.postMessage_debugger.js
   WorkerDebugger.postMessage_worker.js
-  WorkerDebugger_frozen_iframe1.html
-  WorkerDebugger_frozen_iframe2.html
-  WorkerDebugger_frozen_worker1.js
-  WorkerDebugger_frozen_worker2.js
   WorkerDebuggerGlobalScope.createSandbox_debugger.js
   WorkerDebuggerGlobalScope.createSandbox_sandbox.js
   WorkerDebuggerGlobalScope.createSandbox_worker.js
@@ -28,18 +24,22 @@ support-files =
   WorkerDebuggerManager_childWorker.js
   WorkerDebuggerManager_worker.js
   WorkerDebugger_childWorker.js
-  WorkerDebugger_worker.js
+  WorkerDebugger_frozen_iframe1.html
+  WorkerDebugger_frozen_iframe2.html
+  WorkerDebugger_frozen_worker1.js
+  WorkerDebugger_frozen_worker2.js
   WorkerDebugger_sharedWorker.js
   WorkerDebugger_suspended_debugger.js
   WorkerDebugger_suspended_worker.js
+  WorkerDebugger_worker.js
   WorkerTest.jsm
   WorkerTest_subworker.js
   WorkerTest_worker.js
+  bug1062920_worker.js
   chromeWorker_subworker.js
   chromeWorker_worker.js
   dom_worker_helper.js
-  file_url.jsm
-  file_worker.js
+  empty.html
   fileBlobSubWorker_worker.js
   fileBlob_worker.js
   filePosting_worker.js
@@ -48,14 +48,12 @@ support-files =
   fileReaderSync_worker.js
   fileSlice_worker.js
   fileSubWorker_worker.js
+  file_url.jsm
   file_worker.js
   jsm_url_worker.js
-  workersDisabled_worker.js
-  file_url.jsm
-  bug1062920_worker.js
-  empty.html
   sharedWorker_privateBrowsing.js
   test_bug883784.jsm
+  workersDisabled_worker.js
 
 [test_WorkerDebugger.initialize.xul]
 [test_WorkerDebugger.postMessage.xul]
diff --git a/dom/workers/test/mochitest.ini b/dom/workers/test/mochitest.ini
index 42b0102465..4fd9ce1b5d 100644
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -114,6 +114,28 @@ support-files =
   sharedWorker_lifetime.js
   worker_fileReader.js
   fileapi_chromeScript.js
+  !/dom/base/test/file_XHRResponseURL.js
+  !/dom/base/test/file_XHRResponseURL.sjs
+  !/dom/base/test/file_XHRResponseURL.text
+  !/dom/base/test/file_XHRResponseURL.text^headers^
+  !/dom/base/test/file_XHRResponseURL_nocors.text
+  !/dom/base/test/file_XHR_timeout.sjs
+  !/dom/base/test/file_websocket_basic_wsh.py
+  !/dom/base/test/file_websocket_hello_wsh.py
+  !/dom/base/test/file_websocket_http_resource.txt
+  !/dom/base/test/file_websocket_permessage_deflate_disabled_wsh.py
+  !/dom/base/test/file_websocket_permessage_deflate_params_wsh.py
+  !/dom/base/test/file_websocket_permessage_deflate_rejected_wsh.py
+  !/dom/base/test/file_websocket_permessage_deflate_wsh.py
+  !/dom/base/test/file_websocket_wsh.py
+  !/dom/base/test/test_XHR_system.html
+  !/dom/base/test/test_XHR_timeout.js
+  !/dom/base/test/test_performance_observer.js
+  !/dom/base/test/test_performance_user_timing.js
+  !/dom/base/test/websocket_helpers.js
+  !/dom/base/test/websocket_tests.js
+  !/dom/tests/mochitest/notification/MockServices.js
+  !/dom/tests/mochitest/notification/NotificationTest.js
 
 [test_404.html]
 [test_atob.html]
diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js
index 430b19d702..579e9f5680 100644
--- a/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js
@@ -12,6 +12,7 @@ self.addEventListener('fetch', function (event) {
   }
 
   ok(request.mode === 'cors', 'EventSource should make a CORS request');
+  ok(request.cache === 'no-store', 'EventSource should make a no-store request');
   var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'});
   event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
     return fetchResponse;
diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js
index d2c5e4856d..187d0bc6f9 100644
--- a/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js
@@ -11,6 +11,7 @@ self.addEventListener('fetch', function (event) {
   }
 
   ok(request.mode === 'cors', 'EventSource should make a CORS request');
+  ok(request.cache === 'no-store', 'EventSource should make a no-store request');
   var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'});
   event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
     return fetchResponse;
diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js
index 6ae3b9c9d8..45a80e324d 100644
--- a/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js
@@ -12,6 +12,7 @@ self.addEventListener('fetch', function (event) {
   }
 
   ok(request.mode === 'cors', 'EventSource should make a CORS request');
+  ok(request.cache === 'no-store', 'EventSource should make a no-store request');
   var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'no-cors'});
   event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
     return fetchResponse;
diff --git a/dom/workers/test/serviceworkers/fetch/index.html b/dom/workers/test/serviceworkers/fetch/index.html
index 0e7ab74094..4db0fb1399 100644
--- a/dom/workers/test/serviceworkers/fetch/index.html
+++ b/dom/workers/test/serviceworkers/fetch/index.html
@@ -117,6 +117,15 @@
     my_ok(this.test_result, "iframe load should be intercepted");
   });
 
+  test_onload(function() {
+    var elem = document.createElement('iframe');
+    elem.id = 'intercepted-iframe-2';
+    elem.src = "navigate.html";
+    return elem;
+  }, function() {
+    my_ok(this.test_result, "iframe should successfully load");
+  });
+
   gExpected++;
   var xmlDoc = document.implementation.createDocument(null, null, null);
   xmlDoc.load('load_cross_origin_xml_document_synthetic.xml');
diff --git a/dom/workers/test/serviceworkers/fetch_event_worker.js b/dom/workers/test/serviceworkers/fetch_event_worker.js
index a4ba2ae9f5..1caef71e89 100644
--- a/dom/workers/test/serviceworkers/fetch_event_worker.js
+++ b/dom/workers/test/serviceworkers/fetch_event_worker.js
@@ -147,6 +147,31 @@ onfetch = function(ev) {
     ));
   }
 
+  else if (ev.request.url.includes("navigate.html")) {
+    var navigateModeCorrectlyChecked = false;
+    var requests = [ // should not throw
+      new Request(ev.request),
+      new Request(ev.request, undefined),
+      new Request(ev.request, null),
+      new Request(ev.request, {}),
+      new Request(ev.request, {someUnrelatedProperty: 42}),
+    ];
+    try {
+      var request3 = new Request(ev.request, {method: "GET"}); // should throw
+    } catch(e) {
+      navigateModeCorrectlyChecked = requests[0].mode == "navigate";
+    }
+    if (navigateModeCorrectlyChecked) {
+      ev.respondWith(Promise.resolve(
+        new Response("", {
+          headers : {
+            "Content-Type": "text/html"
+          }
+        })
+      ));
+    }
+  }
+
   else if (ev.request.url.includes("nonexistent_worker_script.js")) {
     ev.respondWith(Promise.resolve(
       new Response("postMessage('worker-intercept-success')", {})
diff --git a/dom/workers/test/serviceworkers/force_refresh_worker.js b/dom/workers/test/serviceworkers/force_refresh_worker.js
index a09eeff35e..f0752d0cb0 100644
--- a/dom/workers/test/serviceworkers/force_refresh_worker.js
+++ b/dom/workers/test/serviceworkers/force_refresh_worker.js
@@ -19,6 +19,14 @@ self.addEventListener('fetch', function (event) {
     caches.open(name).then(function(cache) {
       return cache.match(event.request);
     }).then(function(response) {
+      // If this is one of our primary cached responses, then the window
+      // must have generated the request via a normal window reload.  That
+      // should be detectable in the event.request.cache attribute.
+      if (response && event.request.cache !== 'no-cache') {
+        dump('### ### FetchEvent.request.cache is "' + event.request.cache +
+             '" instead of expected "no-cache"\n');
+        return Response.error();
+      }
       return response || fetch(event.request);
     })
   );
diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini
index f697fa6195..3714c518a5 100644
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -194,6 +194,9 @@ support-files =
   open_window/client.html
   lorem_script.js
   file_blob_response_worker.js
+  !/dom/security/test/cors/file_CrossSiteXHR_server.sjs
+  !/dom/tests/mochitest/notification/MockServices.js
+  !/dom/tests/mochitest/notification/NotificationTest.js
 
 [test_bug1151916.html]
 [test_claim.html]
diff --git a/dom/workers/test/websocket_helpers.js b/dom/workers/test/websocket_helpers.js
new file mode 100644
index 0000000000..c2dc3f969f
--- /dev/null
+++ b/dom/workers/test/websocket_helpers.js
@@ -0,0 +1,19 @@
+function feedback() {
+  postMessage({type: 'feedback', msg: "executing test: " + (current_test+1) + " of " + tests.length + " tests." });
+}
+
+function ok(status, msg) {
+  postMessage({type: 'status', status: !!status, msg: msg});
+}
+
+function is(a, b, msg) {
+  ok(a === b, msg);
+}
+
+function isnot(a, b, msg) {
+  ok(a != b, msg);
+}
+
+function finish() {
+  postMessage({type: 'finish'});
+}
diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp
index 5d01c89d32..b41a03854e 100644
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -86,15 +86,19 @@ XBLEnumerate(JSContext *cx, JS::Handle obj)
   return protoBinding->ResolveAllFields(cx, obj);
 }
 
+static const JSClassOps gPrototypeJSClassOps = {
+    nullptr, nullptr, nullptr, nullptr,
+    XBLEnumerate, nullptr,
+    nullptr, XBLFinalize,
+    nullptr, nullptr, nullptr, nullptr
+};
+
 static const JSClass gPrototypeJSClass = {
     "XBL prototype JSClass",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     // Our one reserved slot holds the relevant nsXBLPrototypeBinding
     JSCLASS_HAS_RESERVED_SLOTS(1),
-    nullptr, nullptr, nullptr, nullptr,
-    XBLEnumerate, nullptr,
-    nullptr, XBLFinalize,
-    nullptr, nullptr, nullptr, nullptr
+    &gPrototypeJSClassOps
 };
 
 // Implementation /////////////////////////////////////////////////////////////////
diff --git a/embedding/components/windowwatcher/nsPIWindowWatcher.idl b/embedding/components/windowwatcher/nsPIWindowWatcher.idl
index 445269a42a..89e64c38fe 100644
--- a/embedding/components/windowwatcher/nsPIWindowWatcher.idl
+++ b/embedding/components/windowwatcher/nsPIWindowWatcher.idl
@@ -63,6 +63,9 @@ interface nsPIWindowWatcher : nsISupports
                        false, this argument is ignored.
 
       @param aArgs Window argument
+      @param aOpenerFullZoom the full zoom multiplier for the opener window.
+                             this can be null in the single process case where
+                             the opener full zoom is obtained from aParent.
       @return the new window
 
       @note This method may examine the JS context stack for purposes of
@@ -73,12 +76,14 @@ interface nsPIWindowWatcher : nsISupports
             (which is determined based on the JS stack and the value of
             aParent).  This is not guaranteed, however.
   */
+  [optional_argc]
   nsIDOMWindow openWindow2(in nsIDOMWindow aParent, in string aUrl,
                            in string aName, in string aFeatures,
                            in boolean aCalledFromScript, in boolean aDialog,
                            in boolean aNavigate, in nsITabParent aOpeningTab,
                            in nsISupports aArgs,
-			   in nsIDocShellLoadInfo aLoadInfo);
+			         in nsIDocShellLoadInfo aLoadInfo,
+                                 [optional] in float aOpenerFullZoom);
 
   /**
    * Find a named docshell tree item amongst all windows registered
diff --git a/embedding/components/windowwatcher/nsWindowWatcher.cpp b/embedding/components/windowwatcher/nsWindowWatcher.cpp
index edb8e07fdb..a6d2c03802 100644
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -369,7 +369,8 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow* aParent,
   return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
                             /* calledFromJS = */ false, dialog,
                             /* navigate = */ true, nullptr, argv,
-                            /* aLoadInfo */ nullptr, aResult);
+                            /* aLoadInfo */ nullptr,
+                            /* openerFullZoom = */ nullptr, aResult);
 }
 
 struct SizeSpec
@@ -428,6 +429,8 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent,
                              nsITabParent* aOpeningTab,
                              nsISupports* aArguments,
                              nsIDocShellLoadInfo* aLoadInfo,
+                             float aOpenerFullZoom,
+                             uint8_t aOptionalArgc,
                              nsIDOMWindow** aResult)
 			       
 {
@@ -450,6 +453,7 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent,
                             aCalledFromScript, dialog,
                             aNavigate, aOpeningTab, argv,
                             aLoadInfo,
+                            aOptionalArgc >= 1 ? &aOpenerFullZoom : nullptr,
                             aResult);
 }
 
@@ -494,6 +498,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
                                     nsITabParent* aOpeningTab,
                                     nsIArray* aArgv,
                                     nsIDocShellLoadInfo* aLoadInfo,
+                                    float* aOpenerFullZoom,
                                     nsIDOMWindow** aResult)
 {
   nsresult rv = NS_OK;
@@ -1090,7 +1095,8 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
   }
 
   if (isNewToplevelWindow) {
-    SizeOpenedDocShellItem(newDocShellItem, aParent, isCallerChrome, sizeSpec);
+    SizeOpenedDocShellItem(newDocShellItem, aParent, isCallerChrome, sizeSpec,
+                           aOpenerFullZoom);
   }
 
   // XXXbz isn't windowIsModal always true when windowIsModalContentDialog?
@@ -2077,7 +2083,8 @@ void
 nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem* aDocShellItem,
                                         nsIDOMWindow* aParent,
                                         bool aIsCallerChrome,
-                                        const SizeSpec& aSizeSpec)
+                                        const SizeSpec& aSizeSpec,
+                                        float* aOpenerFullZoom)
 {
   // position and size of window
   int32_t left = 0, top = 0, width = 100, height = 100;
@@ -2094,8 +2101,8 @@ nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem* aDocShellItem,
     return;
   }
 
-  double openerZoom = 1.0;
-  if (aParent) {
+  double openerZoom = aOpenerFullZoom ? *aOpenerFullZoom : 1.0;
+  if (aParent && !aOpenerFullZoom) {
     nsCOMPtr openerDoc;
     aParent->GetDocument(getter_AddRefs(openerDoc));
     if (openerDoc) {
@@ -2255,8 +2262,30 @@ nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem* aDocShellItem,
   // size and position the window
 
   if (positionSpecified) {
-    treeOwnerAsWin->SetPosition(left * scale, top * scale);
-    // moving the window may have changed its scale factor
+    // Get the scale factor appropriate for the screen we're actually
+    // positioning on.
+    nsCOMPtr screen;
+    nsCOMPtr screenMgr(
+      do_GetService("@mozilla.org/gfx/screenmanager;1"));
+    if (screenMgr) {
+      screenMgr->ScreenForRect(left, top, 1, 1, getter_AddRefs(screen));
+    }
+    if (screen) {
+      screen->GetDefaultCSSScaleFactor(&scale);
+      int32_t screenLeft, screenTop, screenWd, screenHt;
+      screen->GetRectDisplayPix(&screenLeft, &screenTop, &screenWd, &screenHt);
+      // Adjust by desktop-pixel origin of the target screen to convert from
+      // per-screen CSS-px coordinates.
+      treeOwnerAsWin->SetPosition((left - screenLeft) * scale + screenLeft,
+                                  (top - screenTop) * scale + screenTop);
+    } else {
+      // Couldn't find screen? This shouldn't happen.
+      treeOwnerAsWin->SetPosition(left * scale, top * scale);
+    }
+    // This shouldn't be necessary, given the screen check above, but in case
+    // moving the window didn't put it where we expected (e.g. due to issues
+    // at the widget level, or whatever), let's re-fetch the scale factor for
+    // wherever it really ended up
     treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
   }
   if (aSizeSpec.SizeSpecified()) {
diff --git a/embedding/components/windowwatcher/nsWindowWatcher.h b/embedding/components/windowwatcher/nsWindowWatcher.h
index 39ed74a964..71181a3c06 100644
--- a/embedding/components/windowwatcher/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/nsWindowWatcher.h
@@ -85,6 +85,7 @@ protected:
                               nsITabParent* aOpeningTab,
                               nsIArray* argv,
 			      nsIDocShellLoadInfo* aLoadInfo,
+                              float* aOpenerFullZoom,
                               nsIDOMWindow** aResult);
 
   static nsresult URIfromURL(const char* aURL,
@@ -110,7 +111,8 @@ protected:
   static void SizeOpenedDocShellItem(nsIDocShellTreeItem* aDocShellItem,
                                      nsIDOMWindow* aParent,
                                      bool aIsCallerChrome,
-                                     const SizeSpec& aSizeSpec);
+                                     const SizeSpec& aSizeSpec,
+                                     float* aOpenerFullZoom);
   static void GetWindowTreeItem(nsIDOMWindow* aWindow,
                                 nsIDocShellTreeItem** aResult);
   static void GetWindowTreeOwner(nsIDOMWindow* aWindow,
diff --git a/embedding/test/mochitest.ini b/embedding/test/mochitest.ini
index 5d5fbc8423..71aa273451 100644
--- a/embedding/test/mochitest.ini
+++ b/embedding/test/mochitest.ini
@@ -11,4 +11,4 @@ support-files =
 [test_window_open_position_constraint.html]
 skip-if = toolkit == 'android' || buildapp == 'mulet'
 [test_window_open_units.html]
-skip-if = toolkit == 'android' || e10s || buildapp == 'mulet'
+skip-if = toolkit == 'android' || buildapp == 'mulet'
diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
index f9f824dd90..1b6536526d 100644
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -113,6 +113,8 @@
 # ifdef __GNUC__
 #  pragma GCC diagnostic pop // -Wshadow
 # endif
+static const uint32_t kDefaultGlyphCacheSize = -1;
+
 #endif
 
 #if !defined(USE_SKIA) || !defined(USE_SKIA_GPU)
@@ -546,6 +548,28 @@ void RecordingPrefChanged(const char *aPrefName, void *aClosure)
   }
 }
 
+#if defined(USE_SKIA)
+static uint32_t GetSkiaGlyphCacheSize()
+{
+    // Only increase font cache size on non-android to save memory.
+#if !defined(MOZ_WIDGET_ANDROID)
+    // 10mb as the default cache size on desktop due to talos perf tweaking.
+    // Chromium uses 20mb and skia default uses 2mb.
+    // We don't need to change the font cache count since we usually
+    // cache thrash due to asian character sets in talos.
+    // Only increase memory on the content proces
+    uint32_t cacheSize = 10 * 1024 * 1024;
+    if (mozilla::BrowserTabsRemoteAutostart()) {
+      return XRE_IsContentProcess() ? cacheSize : kDefaultGlyphCacheSize;
+    }
+
+    return cacheSize;
+#else
+    return kDefaultGlyphCacheSize;
+#endif // MOZ_WIDGET_ANDROID
+}
+#endif
+
 void
 gfxPlatform::Init()
 {
@@ -726,6 +750,13 @@ gfxPlatform::Init()
         gPlatform->mVsyncSource = gPlatform->CreateHardwareVsyncSource();
       }
     }
+
+#ifdef USE_SKIA
+    uint32_t skiaCacheSize = GetSkiaGlyphCacheSize();
+    if (skiaCacheSize != kDefaultGlyphCacheSize) {
+      SkGraphics::SetFontCacheLimit(skiaCacheSize);
+    }
+#endif
 }
 
 static bool sLayersIPCIsUp = false;
diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp
index b8fdedfb28..c6e01dceb0 100644
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -7,6 +7,7 @@
 
 #include "gfxUserFontSet.h"
 #include "gfxPlatform.h"
+#include "nsContentPolicyUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsNetUtil.h"
 #include "nsIJARChannel.h"
@@ -1201,6 +1202,13 @@ gfxUserFontSet::UserFontCache::GetFont(nsIURI* aSrcURI,
         return nullptr;
     }
 
+    // We have to perform another content policy check here to prevent
+    // cache poisoning. E.g. a.com loads a font into the cache but
+    // b.com has a CSP not allowing any fonts to be loaded.
+    if (!aUserFontEntry->mFontSet->IsFontLoadAllowed(aSrcURI, aPrincipal)) {
+        return nullptr;
+    }
+
     // Ignore principal when looking up a data: URI.
     nsIPrincipal* principal;
     if (IgnorePrincipal(aSrcURI)) {
@@ -1215,12 +1223,14 @@ gfxUserFontSet::UserFontCache::GetFont(nsIURI* aSrcURI,
         return entry->GetFontEntry();
     }
 
+    // The channel is never openend; to be conservative we use the most
+    // restrictive security flag: SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS.
     nsCOMPtr chan;
     if (NS_FAILED(NS_NewChannel(getter_AddRefs(chan),
                                 aSrcURI,
                                 aPrincipal,
-                                nsILoadInfo::SEC_NORMAL,
-                                nsIContentPolicy::TYPE_OTHER))) {
+                                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
+                                nsIContentPolicy::TYPE_FONT))) {
         return nullptr;
     }
 
diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h
index 12d1de4853..7583c82a5e 100644
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -262,6 +262,10 @@ public:
                                    nsIPrincipal** aPrincipal,
                                    bool* aBypassCache) = 0;
 
+    // check whether content policies allow the given URI to load.
+    virtual bool IsFontLoadAllowed(nsIURI* aFontLocation,
+                                   nsIPrincipal* aPrincipal) = 0;
+
     // initialize the process that loads external font data, which upon
     // completion will call FontDataDownloadComplete method
     virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
diff --git a/js/public/Class.h b/js/public/Class.h
index f2b9012d41..1f1a8a7d0e 100644
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -477,23 +477,55 @@ typedef bool
 typedef void
 (* FinalizeOp)(FreeOp* fop, JSObject* obj);
 
-#define JS_CLASS_MEMBERS(FinalizeOpType)                                      \
-    const char*         name;                                                \
-    uint32_t            flags;                                                \
-                                                                              \
-    /* Function pointer members (may be null). */                             \
-    JSAddPropertyOp     addProperty;                                          \
-    JSDeletePropertyOp  delProperty;                                          \
-    JSGetterOp          getProperty;                                          \
-    JSSetterOp          setProperty;                                          \
-    JSEnumerateOp       enumerate;                                            \
-    JSResolveOp         resolve;                                              \
-    JSMayResolveOp      mayResolve;                                           \
-    FinalizeOpType      finalize;                                             \
-    JSNative            call;                                                 \
-    JSHasInstanceOp     hasInstance;                                          \
-    JSNative            construct;                                            \
-    JSTraceOp           trace
+// The special treatment of |finalize| and |trace| is necessary because if we
+// assign either of those hooks to a local variable and then call it -- as is
+// done with the other hooks -- the GC hazard analysis gets confused.
+#define JS_CLASS_MEMBERS(ClassOpsType, FinalizeOpType, FreeOpType) \
+    const char* name; \
+    uint32_t flags; \
+    const ClassOpsType* cOps; \
+    \
+    JSAddPropertyOp    getAddProperty() const { return cOps ? cOps->addProperty : nullptr; } \
+    JSDeletePropertyOp getDelProperty() const { return cOps ? cOps->delProperty : nullptr; } \
+    JSGetterOp         getGetProperty() const { return cOps ? cOps->getProperty : nullptr; } \
+    JSSetterOp         getSetProperty() const { return cOps ? cOps->setProperty : nullptr; } \
+    JSEnumerateOp      getEnumerate()   const { return cOps ? cOps->enumerate   : nullptr; } \
+    JSResolveOp        getResolve()     const { return cOps ? cOps->resolve     : nullptr; } \
+    JSMayResolveOp     getMayResolve()  const { return cOps ? cOps->mayResolve  : nullptr; } \
+    JSNative           getCall()        const { return cOps ? cOps->call        : nullptr; } \
+    JSHasInstanceOp    getHasInstance() const { return cOps ? cOps->hasInstance : nullptr; } \
+    JSNative           getConstruct()   const { return cOps ? cOps->construct   : nullptr; } \
+    \
+    bool hasFinalize() const { return cOps && cOps->finalize; } \
+    bool hasTrace()    const { return cOps && cOps->trace;    } \
+    \
+    bool isTrace(JSTraceOp trace) const { return cOps && cOps->trace == trace; } \
+    \
+    void doFinalize(FreeOpType* fop, JSObject* obj) const { \
+        MOZ_ASSERT(cOps && cOps->finalize); \
+        cOps->finalize(fop, obj); \
+    } \
+    void doTrace(JSTracer* trc, JSObject* obj) const { \
+        MOZ_ASSERT(cOps && cOps->trace); \
+        cOps->trace(trc, obj); \
+    }
+
+struct ClassOps
+{
+    /* Function pointer members (may be null). */
+    JSAddPropertyOp     addProperty;
+    JSDeletePropertyOp  delProperty;
+    JSGetterOp          getProperty;
+    JSSetterOp          setProperty;
+    JSEnumerateOp       enumerate;
+    JSResolveOp         resolve;
+    JSMayResolveOp      mayResolve;
+    FinalizeOp          finalize;
+    JSNative            call;
+    JSHasInstanceOp     hasInstance;
+    JSNative            construct;
+    JSTraceOp           trace;
+};
 
 /** Callback for the creation of constructor and prototype objects. */
 typedef JSObject* (*ClassObjectCreationOp)(JSContext* cx, JSProtoKey key);
@@ -587,12 +619,6 @@ struct ClassSpec
 
 struct ClassExtension
 {
-    /**
-     * isWrappedNative is true only if the class is an XPCWrappedNative.
-     * WeakMaps use this to override the wrapper disposal optimization.
-     */
-    bool                isWrappedNative;
-
     /**
      * If an object is used as a key in a weakmap, it may be desirable for the
      * garbage collector to keep that object around longer than it otherwise
@@ -625,7 +651,7 @@ inline ClassObjectCreationOp DELEGATED_CLASSSPEC(const ClassSpec* spec) {
 }
 
 #define JS_NULL_CLASS_SPEC  nullptr
-#define JS_NULL_CLASS_EXT   {false,nullptr}
+#define JS_NULL_CLASS_EXT   nullptr
 
 struct ObjectOps
 {
@@ -651,16 +677,39 @@ struct ObjectOps
 
 typedef void (*JSClassInternal)();
 
-struct JSClass {
-    JS_CLASS_MEMBERS(JSFinalizeOp);
+struct JSClassOps
+{
+    /* Function pointer members (may be null). */
+    JSAddPropertyOp     addProperty;
+    JSDeletePropertyOp  delProperty;
+    JSGetterOp          getProperty;
+    JSSetterOp          setProperty;
+    JSEnumerateOp       enumerate;
+    JSResolveOp         resolve;
+    JSMayResolveOp      mayResolve;
+    JSFinalizeOp        finalize;
+    JSNative            call;
+    JSHasInstanceOp     hasInstance;
+    JSNative            construct;
+    JSTraceOp           trace;
+};
 
-    void*               reserved[5];
+#define JS_NULL_CLASS_OPS nullptr
+
+struct JSClass {
+    JS_CLASS_MEMBERS(JSClassOps, JSFinalizeOp, JSFreeOp);
+
+    void* reserved[3];
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_DELAY_METADATA_BUILDER  (1<<1)  // class's initialization code
                                                 // will call
                                                 // SetNewObjectMetadata itself
+#define JSCLASS_IS_WRAPPED_NATIVE       (1<<2)  // class is an XPCWrappedNative.
+                                                // WeakMaps use this to override
+                                                // the wrapper disposal
+                                                // mechanism.
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  // private is (nsISupports*)
 #define JSCLASS_IS_DOMJSCLASS           (1<<4)  // objects are DOM
 #define JSCLASS_HAS_XRAYED_CONSTRUCTOR  (1<<5)  // if wrapped by an xray
@@ -755,10 +804,10 @@ namespace js {
 
 struct Class
 {
-    JS_CLASS_MEMBERS(FinalizeOp);
-    const ClassSpec*    spec;
-    ClassExtension      ext;
-    const ObjectOps*    ops;
+    JS_CLASS_MEMBERS(js::ClassOps, FinalizeOp, FreeOp);
+    const ClassSpec* spec;
+    const ClassExtension* ext;
+    const ObjectOps* oOps;
 
     /*
      * Objects of this class aren't native objects. They don't have Shapes that
@@ -787,7 +836,7 @@ struct Class
 
     bool nonProxyCallable() const {
         MOZ_ASSERT(!isProxy());
-        return isJSFunction() || call;
+        return isJSFunction() || getCall();
     }
 
     bool isProxy() const {
@@ -802,6 +851,10 @@ struct Class
         return flags & JSCLASS_DELAY_METADATA_BUILDER;
     }
 
+    bool isWrappedNative() const {
+        return flags & JSCLASS_IS_WRAPPED_NATIVE;
+    }
+
     static size_t offsetOfFlags() { return offsetof(Class, flags); }
 
     bool specDefined()         const { return spec ? spec->defined()   : false; }
@@ -824,49 +877,59 @@ struct Class
     FinishClassInitOp specFinishInitHook()
                                const { return spec ? spec->finishInitHook()          : nullptr; }
 
-    LookupPropertyOp getOpsLookupProperty() const { return ops ? ops->lookupProperty : nullptr; }
-    DefinePropertyOp getOpsDefineProperty() const { return ops ? ops->defineProperty : nullptr; }
-    HasPropertyOp    getOpsHasProperty()    const { return ops ? ops->hasProperty    : nullptr; }
-    GetPropertyOp    getOpsGetProperty()    const { return ops ? ops->getProperty    : nullptr; }
-    SetPropertyOp    getOpsSetProperty()    const { return ops ? ops->setProperty    : nullptr; }
+    JSWeakmapKeyDelegateOp extWeakmapKeyDelegateOp()
+                               const { return ext ? ext->weakmapKeyDelegateOp        : nullptr; }
+    JSObjectMovedOp extObjectMovedOp()
+                               const { return ext ? ext->objectMovedOp               : nullptr; }
+
+    LookupPropertyOp getOpsLookupProperty() const { return oOps ? oOps->lookupProperty : nullptr; }
+    DefinePropertyOp getOpsDefineProperty() const { return oOps ? oOps->defineProperty : nullptr; }
+    HasPropertyOp    getOpsHasProperty()    const { return oOps ? oOps->hasProperty    : nullptr; }
+    GetPropertyOp    getOpsGetProperty()    const { return oOps ? oOps->getProperty    : nullptr; }
+    SetPropertyOp    getOpsSetProperty()    const { return oOps ? oOps->setProperty    : nullptr; }
     GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
-                                            const { return ops ? ops->getOwnPropertyDescriptor
+                                            const { return oOps ? oOps->getOwnPropertyDescriptor
                                                                                      : nullptr; }
-    DeletePropertyOp getOpsDeleteProperty() const { return ops ? ops->deleteProperty : nullptr; }
-    WatchOp          getOpsWatch()          const { return ops ? ops->watch          : nullptr; }
-    UnwatchOp        getOpsUnwatch()        const { return ops ? ops->unwatch        : nullptr; }
-    GetElementsOp    getOpsGetElements()    const { return ops ? ops->getElements    : nullptr; }
-    JSNewEnumerateOp getOpsEnumerate()      const { return ops ? ops->enumerate      : nullptr; }
-    JSFunToStringOp  getOpsFunToString()    const { return ops ? ops->funToString    : nullptr; }
+    DeletePropertyOp getOpsDeleteProperty() const { return oOps ? oOps->deleteProperty : nullptr; }
+    WatchOp          getOpsWatch()          const { return oOps ? oOps->watch          : nullptr; }
+    UnwatchOp        getOpsUnwatch()        const { return oOps ? oOps->unwatch        : nullptr; }
+    GetElementsOp    getOpsGetElements()    const { return oOps ? oOps->getElements    : nullptr; }
+    JSNewEnumerateOp getOpsEnumerate()      const { return oOps ? oOps->enumerate      : nullptr; }
+    JSFunToStringOp  getOpsFunToString()    const { return oOps ? oOps->funToString    : nullptr; }
 };
 
+static_assert(offsetof(JSClassOps, addProperty) == offsetof(ClassOps, addProperty),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, delProperty) == offsetof(ClassOps, delProperty),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, getProperty) == offsetof(ClassOps, getProperty),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, setProperty) == offsetof(ClassOps, setProperty),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, enumerate) == offsetof(ClassOps, enumerate),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, resolve) == offsetof(ClassOps, resolve),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, mayResolve) == offsetof(ClassOps, mayResolve),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, finalize) == offsetof(ClassOps, finalize),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, call) == offsetof(ClassOps, call),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, construct) == offsetof(ClassOps, construct),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, hasInstance) == offsetof(ClassOps, hasInstance),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, trace) == offsetof(ClassOps, trace),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(sizeof(JSClassOps) == sizeof(ClassOps),
+              "ClassOps and JSClassOps must be consistent");
+
 static_assert(offsetof(JSClass, name) == offsetof(Class, name),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, flags) == offsetof(Class, flags),
               "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, addProperty) == offsetof(Class, addProperty),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, delProperty) == offsetof(Class, delProperty),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, getProperty) == offsetof(Class, getProperty),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, setProperty) == offsetof(Class, setProperty),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, enumerate) == offsetof(Class, enumerate),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, resolve) == offsetof(Class, resolve),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, mayResolve) == offsetof(Class, mayResolve),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, finalize) == offsetof(Class, finalize),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, call) == offsetof(Class, call),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, construct) == offsetof(Class, construct),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, hasInstance) == offsetof(Class, hasInstance),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, trace) == offsetof(Class, trace),
+static_assert(offsetof(JSClass, cOps) == offsetof(Class, cOps),
               "Class and JSClass must be consistent");
 static_assert(sizeof(JSClass) == sizeof(Class),
               "Class and JSClass must be consistent");
diff --git a/js/public/GCHashTable.h b/js/public/GCHashTable.h
index 113b8d3a2d..103ba2a95c 100644
--- a/js/public/GCHashTable.h
+++ b/js/public/GCHashTable.h
@@ -26,25 +26,27 @@ struct DefaultMapSweepPolicy {
 // A GCHashMap is a GC-aware HashMap, meaning that it has additional trace and
 // sweep methods that know how to visit all keys and values in the table.
 // HashMaps that contain GC pointers will generally want to use this GCHashMap
-// specialization in lieu of HashMap, either because those pointers must be
-// traced to be kept alive -- in which case, KeyPolicy and/or ValuePolicy
-// should do the appropriate tracing -- or because those pointers are weak and
-// must be swept during a GC -- in which case needsSweep should be set
-// appropriately.
+// specialization instead of HashMap, because this conveniently supports tracing
+// keys and values, and cleaning up weak entries.
 //
-// Most types of GC pointers as keys and values can be traced with no extra
-// infrastructure. For structs, the GCPolicy will call a trace() method on
-// the struct. For other structs and non-gc-pointer members, ensure that there
-// is a specialization of GCPolicy with an appropriate trace() static method
-// available to handle the custom type. Generic helpers can be found in
-// js/public/TracingAPI.h.
+// GCHashMap::trace applies GCPolicy::trace to each entry's key and value.
+// Most types of GC pointers already have appropriate specializations of
+// GCPolicy, so they should just work as keys and values. Any struct type with a
+// default constructor and trace and sweep functions should work as well. If you
+// need to define your own GCPolicy specialization, generic helpers can be found
+// in js/public/TracingAPI.h.
 //
-// Note that this HashMap only knows *how* to trace and sweep (and the tracing
-// can handle keys that move), but it does not itself cause tracing or sweeping
-// to be invoked. For tracing, it must be used with Rooted or PersistentRooted,
-// or barriered and traced manually. For sweeping, currently it requires an
-// explicit call to .sweep().
+// The MapSweepPolicy template parameter controls how the table drops entries
+// when swept. GCHashMap::sweep applies MapSweepPolicy::needsSweep to each table
+// entry; if it returns true, the entry is dropped. The default MapSweepPolicy
+// drops the entry if either the key or value is about to be finalized,
+// according to its GCPolicy::needsSweep method. (This default is almost
+// always fine: it's hard to imagine keeping such an entry around anyway.)
 //
+// Note that this HashMap only knows *how* to trace and sweep, but it does not
+// itself cause tracing or sweeping to be invoked. For tracing, it must be used
+// with Rooted or PersistentRooted, or barriered and traced manually. For
+// sweeping, currently it requires an explicit call to .sweep().
 template ,
@@ -91,6 +93,10 @@ class GCHashMap : public HashMap
 };
 
 // HashMap that supports rekeying.
+//
+// If your keys are pointers to something like JSObject that can be tenured or
+// compacted, prefer to use GCHashMap with MovableCellHasher, which takes
+// advantage of the Zone's stable id table to make rekeying unnecessary.
 template ,
@@ -130,13 +136,13 @@ class GCHashMapOperations
     using Map = GCHashMap;
     using Lookup = typename Map::Lookup;
     using Ptr = typename Map::Ptr;
-    using AddPtr = typename Map::AddPtr;
     using Range = typename Map::Range;
-    using Enum = typename Map::Enum;
 
     const Map& map() const { return static_cast(this)->get(); }
 
   public:
+    using AddPtr = typename Map::AddPtr;
+
     bool initialized() const                   { return map().initialized(); }
     Ptr lookup(const Lookup& l) const          { return map().lookup(l); }
     AddPtr lookupForAdd(const Lookup& l) const { return map().lookupForAdd(l); }
@@ -145,6 +151,12 @@ class GCHashMapOperations
     uint32_t count() const                     { return map().count(); }
     size_t capacity() const                    { return map().capacity(); }
     bool has(const Lookup& l) const            { return map().lookup(l).found(); }
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return map().sizeOfExcludingThis(mallocSizeOf);
+    }
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return mallocSizeOf(this) + map().sizeOfExcludingThis(mallocSizeOf);
+    }
 };
 
 template 
@@ -154,13 +166,14 @@ class MutableGCHashMapOperations
     using Map = GCHashMap;
     using Lookup = typename Map::Lookup;
     using Ptr = typename Map::Ptr;
-    using AddPtr = typename Map::AddPtr;
     using Range = typename Map::Range;
-    using Enum = typename Map::Enum;
 
     Map& map() { return static_cast(this)->get(); }
 
   public:
+    using AddPtr = typename Map::AddPtr;
+    struct Enum : public Map::Enum { explicit Enum(Outer& o) : Map::Enum(o.map()) {} };
+
     bool init(uint32_t len = 16) { return map().init(len); }
     void clear()                 { map().clear(); }
     void finish()                { map().finish(); }
@@ -273,13 +286,14 @@ class GCHashSetOperations
     using Set = GCHashSet;
     using Lookup = typename Set::Lookup;
     using Ptr = typename Set::Ptr;
-    using AddPtr = typename Set::AddPtr;
     using Range = typename Set::Range;
-    using Enum = typename Set::Enum;
 
     const Set& set() const { return static_cast(this)->get(); }
 
   public:
+    using AddPtr = typename Set::AddPtr;
+    using Entry = typename Set::Entry;
+
     bool initialized() const                   { return set().initialized(); }
     Ptr lookup(const Lookup& l) const          { return set().lookup(l); }
     AddPtr lookupForAdd(const Lookup& l) const { return set().lookupForAdd(l); }
@@ -288,6 +302,12 @@ class GCHashSetOperations
     uint32_t count() const                     { return set().count(); }
     size_t capacity() const                    { return set().capacity(); }
     bool has(const Lookup& l) const            { return set().lookup(l).found(); }
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return set().sizeOfExcludingThis(mallocSizeOf);
+    }
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return mallocSizeOf(this) + set().sizeOfExcludingThis(mallocSizeOf);
+    }
 };
 
 template 
@@ -297,13 +317,15 @@ class MutableGCHashSetOperations
     using Set = GCHashSet;
     using Lookup = typename Set::Lookup;
     using Ptr = typename Set::Ptr;
-    using AddPtr = typename Set::AddPtr;
     using Range = typename Set::Range;
-    using Enum = typename Set::Enum;
 
     Set& set() { return static_cast(this)->get(); }
 
   public:
+    using AddPtr = typename Set::AddPtr;
+    using Entry = typename Set::Entry;
+    struct Enum : public Set::Enum { explicit Enum(Outer& o) : Set::Enum(o.set()) {} };
+
     bool init(uint32_t len = 16) { return set().init(len); }
     void clear()                 { set().clear(); }
     void finish()                { set().finish(); }
diff --git a/js/public/GCPolicyAPI.h b/js/public/GCPolicyAPI.h
index 05ab4fbd33..3e1c86316d 100644
--- a/js/public/GCPolicyAPI.h
+++ b/js/public/GCPolicyAPI.h
@@ -8,27 +8,34 @@
 
 // A GCPolicy controls how the GC interacts with both direct pointers to GC
 // things (e.g. JSObject* or JSString*), tagged and/or optional pointers to GC
-// things (e.g.  Value or jsid), and C++ aggregate types (e.g.
+// things (e.g.  Value or jsid), and C++ container types (e.g.
 // JSPropertyDescriptor or GCHashMap).
 //
 // The GCPolicy provides at a minimum:
 //
 //   static T initial()
-//       - Tells the GC how to construct an empty T.
+//       - Construct and return an empty T.
 //
 //   static void trace(JSTracer, T* tp, const char* name)
-//       - Tells the GC how to traverse the edge. In the case of an aggregate,
-//         describe how to trace the children.
+//       - Trace the edge |*tp|, calling the edge |name|. Containers like
+//         GCHashMap and GCHashSet use this method to trace their children.
 //
 //   static bool needsSweep(T* tp)
-//       - Tells the GC how to determine if an edge is about to be finalized,
-//         and potentially updates the edge for moving GC if not. For
-//         aggregates, it determines the weakness semantics of storing the
-//         aggregate inside a weak container of some sort. For example, you
-//         might specialize a weak table's key type GCPolicy to describe
-//         when an entry should be kept during sweeping. This is the primary
-//         reason that GC-supporting weak containers can override the [sweep?]
-//         policy on a per-container basis.
+//       - Return true if |*tp| is about to be finalized. Otherwise, update the
+//         edge for moving GC, and return false. Containers like GCHashMap and
+//         GCHashSet use this method to decide when to remove an entry: if this
+//         function returns true on a key/value/member/etc, its entry is dropped
+//         from the container. Specializing this method is the standard way to
+//         get custom weak behavior from a container type.
+//
+// The default GCPolicy assumes that T has a default constructor and |trace|
+// and |needsSweep| methods, and forwards to them. GCPolicy has appropriate
+// specializations for pointers to GC things and pointer-like types like
+// JS::Heap and mozilla::UniquePtr.
+//
+// There are some stock structs your specializations can inherit from.
+// IgnoreGCPolicy does nothing. StructGCPolicy forwards the methods to the
+// referent type T.
 
 #ifndef GCPolicyAPI_h
 #define GCPolicyAPI_h
@@ -48,8 +55,8 @@ class Symbol;
 
 namespace js {
 
-// Defines a policy for aggregate types with non-GC, i.e. C storage. This
-// policy dispatches to the underlying aggregate for GC interactions.
+// Defines a policy for container types with non-GC, i.e. C storage. This
+// policy dispatches to the underlying struct for GC interactions.
 template 
 struct StructGCPolicy
 {
diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h
index a552d5e524..eac6a809fd 100644
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -737,6 +737,8 @@ struct CompartmentStats
         MOZ_ASSERT(!other.isTotals);
     }
 
+    CompartmentStats(const CompartmentStats&) = delete; // disallow copying
+
     ~CompartmentStats() {
         // |allClasses| is usually deleted and set to nullptr before this
         // destructor runs. But there are failure cases due to OOMs that may
diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h
index 2f1a148c20..8e19efc5a5 100644
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -14,6 +14,7 @@
 #include "js/Utility.h"
 
 struct JSRuntime;
+class JSTracer;
 
 namespace js {
 
@@ -173,6 +174,8 @@ class ProfileEntry
     JS_FRIEND_API(jsbytecode*) pc() const volatile;
     JS_FRIEND_API(void) setPC(jsbytecode* pc) volatile;
 
+    void trace(JSTracer* trc);
+
     // The offset of a pc into a script's code can actually be 0, so to
     // signify a nullptr pc, use a -1 index. This is checked against in
     // pc() and setPC() to set/get the right pc.
diff --git a/js/src/asmjs/WasmModule.cpp b/js/src/asmjs/WasmModule.cpp
index e44dba3c1a..1d59180b1b 100644
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -1632,10 +1632,7 @@ Module::profilingLabel(uint32_t funcIndex) const
     return funcLabels_[funcIndex].get();
 }
 
-const Class WasmModuleObject::class_ = {
-    "WasmModuleObject",
-    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_BUILDER |
-    JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS),
+const ClassOps WasmModuleObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -1650,6 +1647,13 @@ const Class WasmModuleObject::class_ = {
     WasmModuleObject::trace
 };
 
+const Class WasmModuleObject::class_ = {
+    "WasmModuleObject",
+    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_BUILDER |
+    JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS),
+    &WasmModuleObject::classOps_,
+};
+
 bool
 WasmModuleObject::hasModule() const
 {
diff --git a/js/src/asmjs/WasmModule.h b/js/src/asmjs/WasmModule.h
index e42db6db60..9cf19d1fd4 100644
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -657,6 +657,8 @@ ExportedFunctionToIndex(JSFunction* fun);
 class WasmModuleObject : public NativeObject
 {
     static const unsigned MODULE_SLOT = 0;
+    static const ClassOps classOps_;
+
     bool hasModule() const;
     static void finalize(FreeOp* fop, JSObject* obj);
     static void trace(JSTracer* trc, JSObject* obj);
diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp
index 0438425af7..a174c4c906 100644
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -623,9 +623,7 @@ static void collator_finalize(FreeOp* fop, JSObject* obj);
 static const uint32_t UCOLLATOR_SLOT = 0;
 static const uint32_t COLLATOR_SLOTS_COUNT = 1;
 
-static const Class CollatorClass = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
+static const ClassOps CollatorClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -636,6 +634,12 @@ static const Class CollatorClass = {
     collator_finalize
 };
 
+static const Class CollatorClass = {
+    js_Object_str,
+    JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
+    &CollatorClassOps
+};
+
 #if JS_HAS_TOSOURCE
 static bool
 collator_toSource(JSContext* cx, unsigned argc, Value* vp)
@@ -1117,9 +1121,7 @@ static void numberFormat_finalize(FreeOp* fop, JSObject* obj);
 static const uint32_t UNUMBER_FORMAT_SLOT = 0;
 static const uint32_t NUMBER_FORMAT_SLOTS_COUNT = 1;
 
-static const Class NumberFormatClass = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
+static const ClassOps NumberFormatClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -1130,6 +1132,12 @@ static const Class NumberFormatClass = {
     numberFormat_finalize
 };
 
+static const Class NumberFormatClass = {
+    js_Object_str,
+    JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
+    &NumberFormatClassOps
+};
+
 #if JS_HAS_TOSOURCE
 static bool
 numberFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
@@ -1586,9 +1594,7 @@ static void dateTimeFormat_finalize(FreeOp* fop, JSObject* obj);
 static const uint32_t UDATE_FORMAT_SLOT = 0;
 static const uint32_t DATE_TIME_FORMAT_SLOTS_COUNT = 1;
 
-static const Class DateTimeFormatClass = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
+static const ClassOps DateTimeFormatClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -1599,6 +1605,12 @@ static const Class DateTimeFormatClass = {
     dateTimeFormat_finalize
 };
 
+static const Class DateTimeFormatClass = {
+    js_Object_str,
+    JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
+    &DateTimeFormatClassOps
+};
+
 #if JS_HAS_TOSOURCE
 static bool
 dateTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp
index 3a35348074..29b3940832 100644
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -104,9 +104,7 @@ namespace {
 
 } /* anonymous namespace */
 
-const Class MapIteratorObject::class_ = {
-    "Map Iterator",
-    JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
+static const ClassOps MapIteratorObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -117,6 +115,12 @@ const Class MapIteratorObject::class_ = {
     MapIteratorObject::finalize
 };
 
+const Class MapIteratorObject::class_ = {
+    "Map Iterator",
+    JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
+    &MapIteratorObjectClassOps
+};
+
 const JSFunctionSpec MapIteratorObject::methods[] = {
     JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0),
     JS_FS_END
@@ -196,18 +200,6 @@ MapIteratorObject::next(JSContext* cx, Handle mapIterator,
     MOZ_ASSERT(resultPairObj->getDenseInitializedLength() == 2);
     MOZ_ASSERT(resultPairObj->getDenseCapacity() >= 2);
 
-#ifdef DEBUG
-    // The array elements should be null, so that inlined
-    // _GetNextMapEntryForIterator doesn't have to perform pre-barrier.
-    RootedValue val(cx);
-    if (!GetElement(cx, resultPairObj, resultPairObj, 0, &val))
-        return false;
-    MOZ_ASSERT(val.isNull());
-    if (!GetElement(cx, resultPairObj, resultPairObj, 1, &val))
-        return false;
-    MOZ_ASSERT(val.isNull());
-#endif
-
     ValueMap::Range* range = MapIteratorObjectRange(mapIterator);
     if (!range || range->empty()) {
         js_delete(range);
@@ -259,10 +251,7 @@ MapIteratorObject::createResultPair(JSContext* cx)
 
 /*** Map *****************************************************************************************/
 
-const Class MapObject::class_ = {
-    "Map",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
+const ClassOps MapObject::classOps_ = {
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
@@ -277,6 +266,13 @@ const Class MapObject::class_ = {
     mark
 };
 
+const Class MapObject::class_ = {
+    "Map",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
+    &MapObject::classOps_
+};
+
 const JSPropertySpec MapObject::properties[] = {
     JS_PSG("size", size, 0),
     JS_PS_END
@@ -870,9 +866,7 @@ class SetIteratorObject : public NativeObject
 
 } /* anonymous namespace */
 
-const Class SetIteratorObject::class_ = {
-    "Set Iterator",
-    JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
+static const ClassOps SetIteratorObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -883,6 +877,12 @@ const Class SetIteratorObject::class_ = {
     SetIteratorObject::finalize
 };
 
+const Class SetIteratorObject::class_ = {
+    "Set Iterator",
+    JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
+    &SetIteratorObjectClassOps
+};
+
 const JSFunctionSpec SetIteratorObject::methods[] = {
     JS_FN("next", next, 0, 0),
     JS_FS_END
@@ -1006,10 +1006,7 @@ SetIteratorObject::next(JSContext* cx, unsigned argc, Value* vp)
 
 /*** Set *****************************************************************************************/
 
-const Class SetObject::class_ = {
-    "Set",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
+const ClassOps SetObject::classOps_ = {
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
@@ -1024,6 +1021,13 @@ const Class SetObject::class_ = {
     mark
 };
 
+const Class SetObject::class_ = {
+    "Set",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
+    &SetObject::classOps_
+};
+
 const JSPropertySpec SetObject::properties[] = {
     JS_PSG("size", size, 0),
     JS_PS_END
diff --git a/js/src/builtin/MapObject.h b/js/src/builtin/MapObject.h
index 47a23ec4d5..90cd096cd0 100644
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -109,6 +109,8 @@ class MapObject : public NativeObject {
     static bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj, MutableHandleValue iter);
 
   private:
+    static const ClassOps classOps_;
+
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSPropertySpec staticProperties[];
@@ -191,9 +193,12 @@ class SetObject : public NativeObject {
     static bool delete_(JSContext *cx, HandleObject obj, HandleValue key, bool *rval);
 
   private:
+    static const ClassOps classOps_;
+
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSPropertySpec staticProperties[];
+
     ValueSet* getData() { return static_cast(getPrivate()); }
     static ValueSet & extract(HandleObject o);
     static ValueSet & extract(CallReceiver call);
diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
index eefe54f6e0..03300f598f 100644
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -529,11 +529,8 @@ void FunctionDeclaration::trace(JSTracer* trc)
 ///////////////////////////////////////////////////////////////////////////
 // ModuleObject
 
-/* static */ const Class
-ModuleObject::class_ = {
-    "Module",
-    JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
-    JSCLASS_IS_ANONYMOUS,
+/* static */ const ClassOps
+ModuleObject::classOps_ = {
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
@@ -548,6 +545,14 @@ ModuleObject::class_ = {
     ModuleObject::trace
 };
 
+/* static */ const Class
+ModuleObject::class_ = {
+    "Module",
+    JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
+    JSCLASS_IS_ANONYMOUS,
+    &ModuleObject::classOps_
+};
+
 #define DEFINE_ARRAY_SLOT_ACCESSOR(cls, name, slot)                           \
     ArrayObject&                                                              \
     cls::name() const                                                         \
diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h
index a9ed0e2f51..0d2c2e436e 100644
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -264,6 +264,8 @@ class ModuleObject : public NativeObject
                                                   HandleObject exports);
 
   private:
+    static const ClassOps classOps_;
+
     static void trace(JSTracer* trc, JSObject* obj);
     static void finalize(js::FreeOp* fop, JSObject* obj);
 
diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp
index 182825714f..b59913789a 100644
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1231,18 +1231,7 @@ static const ClassSpec PlainObjectClassSpec = {
 const Class PlainObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
-    nullptr,  /* addProperty */
-    nullptr,  /* delProperty */
-    nullptr,  /* getProperty */
-    nullptr,  /* setProperty */
-    nullptr,  /* enumerate */
-    nullptr,  /* resolve */
-    nullptr,  /* mayResolve */
-    nullptr,  /* finalize */
-    nullptr,  /* call */
-    nullptr,  /* hasInstance */
-    nullptr,  /* construct */
-    nullptr,  /* trace */
+    JS_NULL_CLASS_OPS,
     &PlainObjectClassSpec
 };
 
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
index 66a0cde2fc..26bd0fbb33 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -411,18 +411,7 @@ const Class PromiseObject::class_ = {
     "Promise",
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Promise) |
     JSCLASS_HAS_XRAYED_CONSTRUCTOR,
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    JS_NULL_CLASS_OPS,
     &PromiseObjectClassSpec
 };
 
@@ -440,17 +429,6 @@ static const ClassSpec PromiseObjectProtoClassSpec = {
 const Class PromiseObject::protoClass_ = {
     "PromiseProto",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Promise),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace  */
+    JS_NULL_CLASS_OPS,
     &PromiseObjectProtoClassSpec
 };
diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp
index d833b83a7e..a9ac39762f 100644
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -189,9 +189,7 @@ TypedObjectMemory(HandleValue v)
     return reinterpret_cast(obj.typedMem());
 }
 
-const Class SimdTypeDescr::class_ = {
-    "SIMD",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps SimdTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -200,7 +198,13 @@ const Class SimdTypeDescr::class_ = {
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
-    call
+    SimdTypeDescr::call
+};
+
+const Class SimdTypeDescr::class_ = {
+    "SIMD",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &SimdTypeDescrClassOps
 };
 
 namespace {
@@ -424,15 +428,19 @@ SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
 ///////////////////////////////////////////////////////////////////////////
 // SIMD class
 
-const Class SimdObject::class_ = {
-    "SIMD",
-    JSCLASS_HAS_RESERVED_SLOTS(uint32_t(SimdType::Count)),
+static const ClassOps SimdObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
-    resolve  /* resolve */
+    SimdObject::resolve
+};
+
+const Class SimdObject::class_ = {
+    "SIMD",
+    JSCLASS_HAS_RESERVED_SLOTS(uint32_t(SimdType::Count)),
+    &SimdObjectClassOps
 };
 
 bool
diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js
index 8f2a160c62..24987f64df 100644
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -44,7 +44,7 @@ function String_match(regexp) {
     var flags = undefined;
     if (arguments.length > 1) {
         if (IsMatchFlagsArgumentEnabled())
-            flags = arguments[1];
+            flags = ToString(arguments[1]);
         WarnOnceAboutFlagsArgument();
     } else {
         if (isPatternString && IsStringMatchOptimizable()) {
@@ -166,7 +166,7 @@ function String_replace(searchValue, replaceValue) {
     if (arguments.length > 2) {
         WarnOnceAboutFlagsArgument();
         if (IsMatchFlagsArgumentEnabled()) {
-            flags = arguments[2];
+            flags = ToString(arguments[2]);
             var rx = RegExpCreate(RegExpEscapeMetaChars(searchString), flags);
 
             return callContentFunction(GetMethod(rx, std_replace), rx, string, replaceValue);
@@ -257,7 +257,7 @@ function String_search(regexp) {
     var flags = undefined;
     if (arguments.length > 1) {
         if (IsMatchFlagsArgumentEnabled())
-            flags = arguments[1];
+            flags = ToString(arguments[1]);
         WarnOnceAboutFlagsArgument();
     } else {
         if (isPatternString && IsStringSearchOptimizable()) {
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index bbd808152f..545529c341 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -351,7 +351,8 @@ MinorGC(JSContext* cx, unsigned argc, Value* vp)
     _("decommitThreshold",          JSGC_DECOMMIT_THRESHOLD,             true)  \
     _("minEmptyChunkCount",         JSGC_MIN_EMPTY_CHUNK_COUNT,          true)  \
     _("maxEmptyChunkCount",         JSGC_MAX_EMPTY_CHUNK_COUNT,          true)  \
-    _("compactingEnabled",          JSGC_COMPACTING_ENABLED,             true)
+    _("compactingEnabled",          JSGC_COMPACTING_ENABLED,             true)  \
+    _("refreshFrameSlicesEnabled",  JSGC_REFRESH_FRAME_SLICES_ENABLED,   true)
 
 static const struct ParamInfo {
     const char*     name;
@@ -1398,8 +1399,7 @@ finalize_counter_finalize(JSFreeOp* fop, JSObject* obj)
     ++finalizeCount;
 }
 
-static const JSClass FinalizeCounterClass = {
-    "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
+static const JSClassOps FinalizeCounterClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -1410,6 +1410,11 @@ static const JSClass FinalizeCounterClass = {
     finalize_counter_finalize
 };
 
+static const JSClass FinalizeCounterClass = {
+    "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
+    &FinalizeCounterClassOps
+};
+
 static bool
 MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2101,8 +2106,7 @@ class CloneBufferObject : public NativeObject {
     }
 };
 
-const Class CloneBufferObject::class_ = {
-    "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS),
+static const ClassOps CloneBufferObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -2110,7 +2114,12 @@ const Class CloneBufferObject::class_ = {
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
-    Finalize
+    CloneBufferObject::Finalize
+};
+
+const Class CloneBufferObject::class_ = {
+    "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS),
+    &CloneBufferObjectClassOps
 };
 
 const JSPropertySpec CloneBufferObject::props_[] = {
diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp
index eacccef46b..d1a4311b1b 100644
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -207,9 +207,7 @@ const Class js::TypedProto::class_ = {
  * distinguish which scalar type object this actually is.
  */
 
-const Class js::ScalarTypeDescr::class_ = {
-    "Scalar",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps ScalarTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -221,6 +219,12 @@ const Class js::ScalarTypeDescr::class_ = {
     ScalarTypeDescr::call
 };
 
+const Class js::ScalarTypeDescr::class_ = {
+    "Scalar",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &ScalarTypeDescrClassOps
+};
+
 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, JSFUN_HAS_REST),
@@ -304,9 +308,7 @@ ScalarTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
  * reference type object this actually is.
  */
 
-const Class js::ReferenceTypeDescr::class_ = {
-    "Reference",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps ReferenceTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -318,6 +320,12 @@ const Class js::ReferenceTypeDescr::class_ = {
     ReferenceTypeDescr::call
 };
 
+const Class js::ReferenceTypeDescr::class_ = {
+    "Reference",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &ReferenceTypeDescrClassOps
+};
+
 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
@@ -489,9 +497,7 @@ CreatePrototypeObjectForComplexTypeInstance(JSContext* cx, HandleObject ctorProt
     return NewObjectWithGivenProto(cx, ctorPrototypePrototype, SingletonObject);
 }
 
-const Class ArrayTypeDescr::class_ = {
-    "ArrayType",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps ArrayTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -505,6 +511,12 @@ const Class ArrayTypeDescr::class_ = {
     TypedObject::construct
 };
 
+const Class ArrayTypeDescr::class_ = {
+    "ArrayType",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &ArrayTypeDescrClassOps
+};
+
 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
 };
@@ -632,6 +644,9 @@ ArrayMetaTypeDescr::create(JSContext* cx,
     if (!CreateTraceList(cx, obj))
         return nullptr;
 
+    if (!cx->zone()->typeDescrObjects.put(obj))
+        return nullptr;
+
     return obj;
 }
 
@@ -714,9 +729,7 @@ js::IsTypedObjectArray(JSObject& obj)
  * StructType class
  */
 
-const Class StructTypeDescr::class_ = {
-    "StructType",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps StructTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -730,6 +743,12 @@ const Class StructTypeDescr::class_ = {
     TypedObject::construct
 };
 
+const Class StructTypeDescr::class_ = {
+    "StructType",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &StructTypeDescrClassOps
+};
+
 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
 };
@@ -924,21 +943,17 @@ StructMetaTypeDescr::create(JSContext* cx,
                                             TenuredObject);
         if (!fieldNamesVec)
             return nullptr;
-        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES,
-                                     ObjectValue(*fieldNamesVec));
+        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES, ObjectValue(*fieldNamesVec));
     }
 
     // Construct for internal use an array with the type object for each field.
-    {
-        RootedObject fieldTypeVec(cx);
-        fieldTypeVec = NewDenseCopiedArray(cx, fieldTypeObjs.length(),
-                                           fieldTypeObjs.begin(), nullptr,
-                                           TenuredObject);
-        if (!fieldTypeVec)
-            return nullptr;
-        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES,
-                                     ObjectValue(*fieldTypeVec));
-    }
+    RootedObject fieldTypeVec(cx);
+    fieldTypeVec = NewDenseCopiedArray(cx, fieldTypeObjs.length(),
+                                       fieldTypeObjs.begin(), nullptr,
+                                       TenuredObject);
+    if (!fieldTypeVec)
+        return nullptr;
+    descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES, ObjectValue(*fieldTypeVec));
 
     // Construct for internal use an array with the offset for each field.
     {
@@ -948,8 +963,7 @@ StructMetaTypeDescr::create(JSContext* cx,
                                               TenuredObject);
         if (!fieldOffsetsVec)
             return nullptr;
-        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS,
-                                     ObjectValue(*fieldOffsetsVec));
+        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS, ObjectValue(*fieldOffsetsVec));
     }
 
     // Create data properties fieldOffsets and fieldTypes
@@ -986,6 +1000,12 @@ StructMetaTypeDescr::create(JSContext* cx,
     if (!CreateTraceList(cx, descr))
         return nullptr;
 
+    if (!cx->zone()->typeDescrObjects.put(descr) ||
+        !cx->zone()->typeDescrObjects.put(fieldTypeVec))
+    {
+        return nullptr;
+    }
+
     return descr;
 }
 
@@ -1018,13 +1038,6 @@ StructTypeDescr::fieldCount() const
     return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).getDenseInitializedLength();
 }
 
-size_t
-StructTypeDescr::maybeForwardedFieldCount() const
-{
-    ArrayObject& fieldInfo = *MaybeForwarded(&fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES));
-    return fieldInfo.getDenseInitializedLength();
-}
-
 bool
 StructTypeDescr::fieldIndex(jsid id, size_t* out) const
 {
@@ -1054,14 +1067,6 @@ StructTypeDescr::fieldOffset(size_t index) const
     return AssertedCast(fieldOffsets.getDenseElement(index).toInt32());
 }
 
-size_t
-StructTypeDescr::maybeForwardedFieldOffset(size_t index) const
-{
-    ArrayObject& fieldOffsets = *MaybeForwarded(&fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS));
-    MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
-    return AssertedCast(fieldOffsets.getDenseElement(index).toInt32());
-}
-
 TypeDescr&
 StructTypeDescr::fieldDescr(size_t index) const
 {
@@ -1070,14 +1075,6 @@ StructTypeDescr::fieldDescr(size_t index) const
     return fieldDescrs.getDenseElement(index).toObject().as();
 }
 
-TypeDescr&
-StructTypeDescr::maybeForwardedFieldDescr(size_t index) const
-{
-    ArrayObject& fieldDescrs = *MaybeForwarded(&fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES));
-    MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
-    return MaybeForwarded(&fieldDescrs.getDenseElement(index).toObject())->as();
-}
-
 /******************************************************************************
  * Creating the TypedObject "module"
  *
@@ -1175,6 +1172,9 @@ DefineSimpleTypeDescr(JSContext* cx,
     if (!CreateTraceList(cx, descr))
         return false;
 
+    if (!cx->zone()->typeDescrObjects.put(descr))
+        return false;
+
     return true;
 }
 
@@ -1390,19 +1390,6 @@ TypedObject::isAttached() const
     return true;
 }
 
-bool
-TypedObject::maybeForwardedIsAttached() const
-{
-    if (is())
-        return true;
-    if (!as().outOfLineTypedMem())
-        return false;
-    JSObject& owner = *MaybeForwarded(&as().owner());
-    if (owner.is() && owner.as().isDetached())
-        return false;
-    return true;
-}
-
 /* static */ bool
 TypedObject::GetBuffer(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1662,7 +1649,7 @@ OutlineTypedObject::obj_trace(JSTracer* trc, JSObject* object)
         trc->runtime()->gc.nursery.maybeSetForwardingPointer(trc, oldData, newData, /* direct = */ false);
     }
 
-    if (!descr.opaque() || !typedObj.maybeForwardedIsAttached())
+    if (!descr.opaque() || !typedObj.isAttached())
         return;
 
     descr.traceInstances(trc, newData, 1);
@@ -2253,9 +2240,7 @@ const ObjectOps TypedObject::objectOps_ = {
 };
 
 #define DEFINE_TYPEDOBJ_CLASS(Name, Trace, flag)         \
-    const Class Name::class_ = {                         \
-        # Name,                                          \
-        Class::NON_NATIVE | flag,                        \
+    static const ClassOps Name##ClassOps = {             \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
         nullptr,        /* getProperty */                \
@@ -2268,6 +2253,11 @@ const ObjectOps TypedObject::objectOps_ = {
         nullptr,        /* hasInstance */                \
         nullptr,        /* construct   */                \
         Trace,                                           \
+    };                                                   \
+    const Class Name::class_ = {                         \
+        # Name,                                          \
+        Class::NON_NATIVE | flag,                        \
+        &Name##ClassOps,                                 \
         JS_NULL_CLASS_SPEC,                              \
         JS_NULL_CLASS_EXT,                               \
         &TypedObject::objectOps_                         \
@@ -2812,7 +2802,7 @@ visitReferences(TypeDescr& descr,
       case type::Array:
       {
         ArrayTypeDescr& arrayDescr = descr.as();
-        TypeDescr& elementDescr = *MaybeForwarded(&arrayDescr.elementType());
+        TypeDescr& elementDescr = arrayDescr.elementType();
         for (int32_t i = 0; i < arrayDescr.length(); i++) {
             visitReferences(elementDescr, mem, visitor);
             mem += elementDescr.size();
@@ -2823,9 +2813,9 @@ visitReferences(TypeDescr& descr,
       case type::Struct:
       {
         StructTypeDescr& structDescr = descr.as();
-        for (size_t i = 0; i < structDescr.maybeForwardedFieldCount(); i++) {
-            TypeDescr& descr = structDescr.maybeForwardedFieldDescr(i);
-            size_t offset = structDescr.maybeForwardedFieldOffset(i);
+        for (size_t i = 0; i < structDescr.fieldCount(); i++) {
+            TypeDescr& descr = structDescr.fieldDescr(i);
+            size_t offset = structDescr.fieldOffset(i);
             visitReferences(descr, mem + offset, visitor);
         }
         return;
@@ -3038,6 +3028,7 @@ CreateTraceList(JSContext* cx, HandleTypeDescr descr)
 /* static */ void
 TypeDescr::finalize(FreeOp* fop, JSObject* obj)
 {
-    if (obj->as().hasTraceList())
-        js_free(const_cast(obj->as().traceList()));
+    TypeDescr& descr = obj->as();
+    if (descr.hasTraceList())
+        js_free(const_cast(descr.traceList()));
 }
diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h
index 1edeeefa99..1e9dbee7b8 100644
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -446,7 +446,6 @@ class StructTypeDescr : public ComplexTypeDescr
 
     // Returns the number of fields defined in this struct.
     size_t fieldCount() const;
-    size_t maybeForwardedFieldCount() const;
 
     // Set `*out` to the index of the field named `id` and returns true,
     // or return false if no such field exists.
@@ -457,11 +456,9 @@ class StructTypeDescr : public ComplexTypeDescr
 
     // Return the type descr of the field at index `index`.
     TypeDescr& fieldDescr(size_t index) const;
-    TypeDescr& maybeForwardedFieldDescr(size_t index) const;
 
     // Return the offset of the field at index `index`.
     size_t fieldOffset(size_t index) const;
-    size_t maybeForwardedFieldOffset(size_t index) const;
 
   private:
     ArrayObject& fieldInfoObject(size_t slot) const {
@@ -545,7 +542,6 @@ class TypedObject : public JSObject
     uint8_t* typedMem() const;
     uint8_t* typedMemBase() const;
     bool isAttached() const;
-    bool maybeForwardedIsAttached() const;
 
     int32_t size() const {
         return typeDescr().size();
diff --git a/js/src/builtin/WeakMapObject.cpp b/js/src/builtin/WeakMapObject.cpp
index c6be4ce923..4f85bbf3d1 100644
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -111,8 +111,8 @@ js::WeakMap_delete(JSContext* cx, unsigned argc, Value* vp)
 static bool
 TryPreserveReflector(JSContext* cx, HandleObject obj)
 {
-    if (obj->getClass()->ext.isWrappedNative ||
-        (obj->getClass()->flags & JSCLASS_IS_DOMJSCLASS) ||
+    if (obj->getClass()->isWrappedNative() ||
+        obj->getClass()->isDOMClass() ||
         (obj->is() &&
          obj->as().handler()->family() == GetDOMProxyHandlerFamily()))
     {
@@ -147,7 +147,7 @@ SetWeakMapEntryInternal(JSContext* cx, Handle mapObj,
     if (!TryPreserveReflector(cx, key))
         return false;
 
-    if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
+    if (JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp()) {
         RootedObject delegate(cx, op(key));
         if (delegate && !TryPreserveReflector(cx, delegate))
             return false;
@@ -382,10 +382,7 @@ WeakMap_construct(JSContext* cx, unsigned argc, Value* vp)
     return true;
 }
 
-const Class WeakMapObject::class_ = {
-    "WeakMap",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
+static const ClassOps WeakMapObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -400,6 +397,13 @@ const Class WeakMapObject::class_ = {
     WeakMap_mark
 };
 
+const Class WeakMapObject::class_ = {
+    "WeakMap",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
+    &WeakMapObjectClassOps
+};
+
 static const JSFunctionSpec weak_map_methods[] = {
     JS_FN("has",    WeakMap_has, 1, 0),
     JS_FN("get",    WeakMap_get, 1, 0),
diff --git a/js/src/configure.in b/js/src/configure.in
index e11ba5ff44..7808f4c5e8 100644
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1614,13 +1614,7 @@ ia64*-hpux*)
     ;;
 
 *-*linux*)
-    # Note: both GNU_CC and INTEL_CC are set when using Intel's C compiler.
-    # Similarly for GNU_CXX and INTEL_CXX.
-    if test "$INTEL_CC" -o "$INTEL_CXX"; then
-        # -Os has been broken on Intel's C/C++ compilers for quite a
-        # while; Intel recommends against using it.
-        MOZ_OPTIMIZE_FLAGS="-O2"
-    elif test "$GNU_CC" -o "$GNU_CXX"; then
+    if test "$GNU_CC" -o "$GNU_CXX"; then
         MOZ_PGO_OPTIMIZE_FLAGS="-O3"
         MOZ_OPTIMIZE_FLAGS="-O3"
         if test -z "$CLANG_CC"; then
@@ -3422,13 +3416,6 @@ fi
 
 CFLAGS="$_SAVE_CFLAGS"
 
-if test -n "$INTEL_CC"; then
-  PROFILE_GEN_CFLAGS="-prof-gen -prof-dir ."
-  PROFILE_GEN_LDFLAGS=
-  PROFILE_USE_CFLAGS="-prof-use -prof-dir ."
-  PROFILE_USE_LDFLAGS=
-fi
-
 dnl Sun Studio on Solaris
 if test "$SOLARIS_SUNPRO_CC"; then
   PROFILE_GEN_CFLAGS="-xprofile=collect:$_objdir/$enable_application"
@@ -3707,6 +3694,7 @@ AC_SUBST(CROSS_COMPILE)
 AC_SUBST(HOST_CC)
 AC_SUBST(HOST_CXX)
 AC_SUBST(HOST_CFLAGS)
+AC_SUBST(HOST_CPPFLAGS)
 AC_SUBST(HOST_CXXFLAGS)
 AC_SUBST(HOST_LDFLAGS)
 AC_SUBST(HOST_OPTIMIZE_FLAGS)
diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp
index 038602d980..c33412f8e3 100644
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -546,13 +546,16 @@ static const JSClass sCABIClass = {
 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
 // This exists to give said prototypes a class of "CType", and to provide
 // reserved slots for stashing various other prototype objects.
-static const JSClass sCTypeProtoClass = {
-  "CType",
-  JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
+static const JSClassOps sCTypeProtoClassOps = {
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
   ConstructAbstract, nullptr, ConstructAbstract
 };
+static const JSClass sCTypeProtoClass = {
+  "CType",
+  JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
+  &sCTypeProtoClassOps
+};
 
 // Class representing ctypes.CData.prototype and the 'prototype' properties
 // of CTypes. This exists to give said prototypes a class of "CData".
@@ -561,30 +564,39 @@ static const JSClass sCDataProtoClass = {
   0
 };
 
-static const JSClass sCTypeClass = {
-  "CType",
-  JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
+static const JSClassOps sCTypeClassOps = {
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CType::Finalize,
   CType::ConstructData, CType::HasInstance, CType::ConstructData,
   CType::Trace
 };
+static const JSClass sCTypeClass = {
+  "CType",
+  JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
+  &sCTypeClassOps
+};
 
-static const JSClass sCDataClass = {
-  "CData",
-  JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
+static const JSClassOps sCDataClassOps = {
   nullptr, nullptr, ArrayType::Getter, ArrayType::Setter,
   nullptr, nullptr, nullptr, CData::Finalize,
   FunctionType::Call, nullptr, FunctionType::Call
 };
+static const JSClass sCDataClass = {
+  "CData",
+  JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
+  &sCDataClassOps
+};
 
-static const JSClass sCClosureClass = {
-  "CClosure",
-  JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
+static const JSClassOps sCClosureClassOps = {
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CClosure::Finalize,
   nullptr, nullptr, nullptr, CClosure::Trace
 };
+static const JSClass sCClosureClass = {
+  "CClosure",
+  JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
+  &sCClosureClassOps
+};
 
 /*
  * Class representing the prototype of CDataFinalizer.
@@ -600,11 +612,14 @@ static const JSClass sCDataFinalizerProtoClass = {
  * Instances of CDataFinalizer have both private data (with type
  * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
  */
+static const JSClassOps sCDataFinalizerClassOps = {
+  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, CDataFinalizer::Finalize
+};
 static const JSClass sCDataFinalizerClass = {
   "CDataFinalizer",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, CDataFinalizer::Finalize
+  &sCDataFinalizerClassOps
 };
 
 
@@ -786,18 +801,21 @@ static const JSClass sUInt64ProtoClass = {
   0
 };
 
+static const JSClassOps sInt64ClassOps = {
+  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, Int64Base::Finalize
+};
+
 static const JSClass sInt64Class = {
   "Int64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, Int64Base::Finalize
+  &sInt64ClassOps
 };
 
 static const JSClass sUInt64Class = {
   "UInt64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, Int64Base::Finalize
+  &sInt64ClassOps
 };
 
 static const JSFunctionSpec sInt64StaticFunctions[] = {
diff --git a/js/src/ctypes/Library.cpp b/js/src/ctypes/Library.cpp
index 5ddaa68a39..df38d22577 100644
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -32,11 +32,15 @@ namespace Library
 
 typedef Rooted    RootedFlatString;
 
+static const JSClassOps sLibraryClassOps = {
+  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, Library::Finalize
+};
+
 static const JSClass sLibraryClass = {
   "Library",
   JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, Library::Finalize
+  &sLibraryClassOps
 };
 
 #define CTYPESFN_FLAGS \
diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js
index 18baf9bf08..d0477c94b3 100644
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -68,8 +68,8 @@ var ignoreClasses = {
 // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
 // a function pointer field named FIELD.
 var ignoreCallees = {
-    "js::Class.trace" : true,
-    "js::Class.finalize" : true,
+    "js::ClassOps.trace" : true,
+    "js::ClassOps.finalize" : true,
     "JSRuntime.destroyPrincipals" : true,
     "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC
     "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC.
@@ -210,6 +210,7 @@ var ignoreFunctions = {
 
     "uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
     "uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
+    "void js::Nursery::freeMallocedBuffers()" : true,
 };
 
 function isProtobuf(name)
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
index 8b83edfded..74f558c69d 100644
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -46,7 +46,7 @@ class FullParseHandler
      * was previously lazily parsed, that lazy function and the current index
      * into its inner functions. We do not want to reparse the inner functions.
      */
-    LazyScript * const lazyOuterFunction_;
+    const Rooted lazyOuterFunction_;
     size_t lazyInnerFunctionIndex;
 
     const TokenPos& pos() {
@@ -103,7 +103,7 @@ class FullParseHandler
                      LazyScript* lazyOuterFunction)
       : allocator(cx, alloc),
         tokenStream(tokenStream),
-        lazyOuterFunction_(lazyOuterFunction),
+        lazyOuterFunction_(cx, lazyOuterFunction),
         lazyInnerFunctionIndex(0),
         syntaxParser(syntaxParser)
     {}
diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h
index 02aff832fb..b9bf028d3a 100644
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -125,6 +125,9 @@ struct MovingTracer : JS::CallbackTracer
 
     void onObjectEdge(JSObject** objp) override;
     void onShapeEdge(Shape** shapep) override;
+    void onStringEdge(JSString** stringp) override;
+    void onScriptEdge(JSScript** scriptp) override;
+    void onLazyScriptEdge(LazyScript** lazyp) override;
     void onChild(const JS::GCCellPtr& thing) override {
         MOZ_ASSERT(!RelocationOverlay::isCellForwarded(thing.asCell()));
     }
diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h
index 0007d831a9..2dada9b2e7 100644
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -147,6 +147,11 @@ class GCSchedulingTunables
      */
     bool dynamicMarkSliceEnabled_;
 
+    /*
+     * Controls whether painting can trigger IGC slices.
+     */
+    bool refreshFrameSlicesEnabled_;
+
     /*
      * Controls the number of empty chunks reserved for future allocation.
      */
@@ -167,6 +172,7 @@ class GCSchedulingTunables
         highFrequencyHeapGrowthMin_(1.5),
         lowFrequencyHeapGrowth_(1.5),
         dynamicMarkSliceEnabled_(false),
+        refreshFrameSlicesEnabled_(true),
         minEmptyChunkCount_(1),
         maxEmptyChunkCount_(30)
     {}
@@ -183,6 +189,7 @@ class GCSchedulingTunables
     double highFrequencyHeapGrowthMin() const { return highFrequencyHeapGrowthMin_; }
     double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
     bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
+    bool areRefreshFrameSlicesEnabled() const { return refreshFrameSlicesEnabled_; }
     unsigned minEmptyChunkCount(const AutoLockGC&) const { return minEmptyChunkCount_; }
     unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
 
@@ -568,10 +575,7 @@ class ChainedIter
     T operator->() const { return get(); }
 };
 
-typedef js::HashMap,
-                    js::SystemAllocPolicy> RootedValueMap;
+typedef HashMap, SystemAllocPolicy> RootedValueMap;
 
 class GCRuntime
 {
@@ -660,7 +664,7 @@ class GCRuntime
 
   public:
     // Internal public interface
-    js::gc::State state() const { return incrementalState; }
+    State state() const { return incrementalState; }
     bool isHeapCompacting() const { return state() == COMPACT; }
     bool isForegroundSweeping() const { return state() == SWEEP; }
     bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
@@ -738,7 +742,7 @@ class GCRuntime
     void disallowIncrementalGC() { incrementalAllowed = false; }
 
     bool isIncrementalGCEnabled() const { return mode == JSGC_MODE_INCREMENTAL && incrementalAllowed; }
-    bool isIncrementalGCInProgress() const { return state() != gc::NO_INCREMENTAL; }
+    bool isIncrementalGCInProgress() const { return state() != NO_INCREMENTAL; }
 
     bool isGenerationalGCEnabled() const { return generationalDisabled == 0; }
     void disableGenerationalGC();
@@ -965,6 +969,7 @@ class GCRuntime
     void sweepZoneAfterCompacting(Zone* zone);
     bool relocateArenas(Zone* zone, JS::gcreason::Reason reason, Arena*& relocatedListOut,
                         SliceBudget& sliceBudget);
+    void updateTypeDescrObjects(MovingTracer* trc, Zone* zone);
     void updateAllCellPointers(MovingTracer* trc, Zone* zone);
     void updatePointersToRelocatedCells(Zone* zone);
     void protectAndHoldArenas(Arena* arenaList);
@@ -992,14 +997,14 @@ class GCRuntime
     JS::Zone* systemZone;
 
     /* List of compartments and zones (protected by the GC lock). */
-    js::gc::ZoneVector zones;
+    ZoneVector zones;
 
-    js::Nursery nursery;
-    js::gc::StoreBuffer storeBuffer;
+    Nursery nursery;
+    StoreBuffer storeBuffer;
 
-    js::gcstats::Statistics stats;
+    gcstats::Statistics stats;
 
-    js::GCMarker marker;
+    GCMarker marker;
 
     /* Track heap usage for this runtime. */
     HeapUsage usage;
@@ -1034,7 +1039,7 @@ class GCRuntime
     size_t maxMallocBytes;
 
     // An incrementing id used to assign unique ids to cells that require one.
-    mozilla::Atomic nextCellUniqueId_;
+    mozilla::Atomic nextCellUniqueId_;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
@@ -1134,7 +1139,7 @@ class GCRuntime
      * The current incremental GC phase. This is also used internally in
      * non-incremental GC.
      */
-    js::gc::State incrementalState;
+    State incrementalState;
 
     /* Indicates that the last incremental slice exhausted the mark stack. */
     bool lastMarkSlice;
@@ -1155,7 +1160,7 @@ class GCRuntime
      * Free LIFO blocks are transferred to this allocator before being freed on
      * the background GC thread.
      */
-    js::LifoAlloc freeLifoAlloc;
+    LifoAlloc freeLifoAlloc;
 
     /* Index of current zone group (for stats). */
     unsigned zoneGroupIndex;
@@ -1180,17 +1185,17 @@ class GCRuntime
     /*
      * List head of arenas allocated during the sweep phase.
      */
-    js::gc::Arena* arenasAllocatedDuringSweep;
+    Arena* arenasAllocatedDuringSweep;
 
     /*
      * Incremental compacting state.
      */
     bool startedCompacting;
-    js::gc::ZoneList zonesToMaybeCompact;
+    ZoneList zonesToMaybeCompact;
     Arena* relocatedArenasToRelease;
 
 #ifdef JS_GC_ZEAL
-    js::gc::MarkingValidator* markingValidator;
+    MarkingValidator* markingValidator;
 #endif
 
     /*
@@ -1204,7 +1209,7 @@ class GCRuntime
     int64_t defaultTimeBudget_;
 
     /*
-     * We disable incremental GC if we encounter a js::Class with a trace hook
+     * We disable incremental GC if we encounter a Class with a trace hook
      * that does not implement write barriers.
      */
     bool incrementalAllowed;
@@ -1277,7 +1282,7 @@ class GCRuntime
     bool deterministicOnly;
     int incrementalLimit;
 
-    js::Vector selectedForMarking;
+    Vector selectedForMarking;
 #endif
 
     bool fullCompartmentChecks;
@@ -1341,8 +1346,8 @@ class GCRuntime
     SortedArenaList incrementalSweepList;
 
     friend class js::GCHelperState;
-    friend class js::gc::MarkingValidator;
-    friend class js::gc::AutoTraceSession;
+    friend class MarkingValidator;
+    friend class AutoTraceSession;
     friend class AutoEnterIteration;
 };
 
diff --git a/js/src/gc/Iteration.cpp b/js/src/gc/Iteration.cpp
index 5512ebfbf5..07462d03c7 100644
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -101,14 +101,14 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment,
     AutoPrepareForTracing prep(rt, SkipAtoms);
 
     if (compartment) {
-        for (ZoneCellIter i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
+        for (ZoneCellIterUnderGC i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript* script = i.get();
             if (script->compartment() == compartment)
                 scriptCallback(rt, data, script);
         }
     } else {
         for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-            for (ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next())
+            for (ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next())
                 scriptCallback(rt, data, i.get());
         }
     }
@@ -121,7 +121,7 @@ js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data)
     AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms);
 
     for (auto thingKind : ObjectAllocKinds()) {
-        for (ZoneCellIter i(zone, thingKind); !i.done(); i.next()) {
+        for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
             JSObject* obj = i.get();
             if (obj->asTenured().isMarked(GRAY))
                 cellCallback(data, JS::GCCellPtr(obj));
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
index cabc0dd04c..d2284eb1ff 100644
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -910,8 +910,8 @@ GCMarker::traverse(AccessorShape* thing) {
 } // namespace js
 
 template 
-void
-js::GCMarker::traverseEdge(S source, T* target)
+static void
+CheckTraversedEdge(S source, T* target)
 {
     // Atoms and Symbols do not have or mark their internal pointers, respectively.
     MOZ_ASSERT(!ThingIsPermanentAtomOrWellKnownSymbol(source));
@@ -927,7 +927,13 @@ js::GCMarker::traverseEdge(S source, T* target)
     // If we have access to a compartment pointer for both things, they must match.
     MOZ_ASSERT_IF(source->maybeCompartment() && target->maybeCompartment(),
                   source->maybeCompartment() == target->maybeCompartment());
+}
 
+template 
+void
+js::GCMarker::traverseEdge(S source, T* target)
+{
+    CheckTraversedEdge(source, target);
     traverse(target);
 }
 
@@ -1031,7 +1037,14 @@ js::GCMarker::eagerlyMarkChildren(Shape* shape)
 {
     MOZ_ASSERT(shape->isMarked(this->markColor()));
     do {
-        traverseEdge(shape, shape->base());
+        // Special case: if a base shape has a shape table then all its pointers
+        // must point to this shape or an anscestor.  Since these pointers will
+        // be traced by this loop they do not need to be traced here as well.
+        BaseShape* base = shape->base();
+        CheckTraversedEdge(shape, base);
+        if (mark(base))
+            base->traceChildrenSkipShapeTable(this);
+
         traverseEdge(shape, shape->propidRef().get());
 
         // When triggered between slices on belhalf of a barrier, these
@@ -1266,10 +1279,10 @@ CallTraceHook(Functor f, JSTracer* trc, JSObject* obj, CheckGeneration check, Ar
     MOZ_ASSERT(clasp);
     MOZ_ASSERT(obj->isNative() == clasp->isNative());
 
-    if (!clasp->trace)
+    if (!clasp->hasTrace())
         return &obj->as();
 
-    if (clasp->trace == InlineTypedObject::obj_trace) {
+    if (clasp->isTrace(InlineTypedObject::obj_trace)) {
         Shape** pshape = obj->as().addressOfShapeFromGC();
         f(pshape, mozilla::Forward(args)...);
 
@@ -1299,7 +1312,7 @@ CallTraceHook(Functor f, JSTracer* trc, JSObject* obj, CheckGeneration check, Ar
         return nullptr;
     }
 
-    clasp->trace(trc, obj);
+    clasp->doTrace(trc, obj);
 
     if (!clasp->isNative())
         return nullptr;
@@ -2280,7 +2293,7 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
             tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
         } else if (src->is()) {
             tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
-        } else if (JSObjectMovedOp op = dst->getClass()->ext.objectMovedOp) {
+        } else if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp()) {
             op(dst, src);
         } else {
             // Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp
index 4d33ef145e..735225f51f 100644
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -175,7 +175,7 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const
      * heap. The finalizers for these classes must do nothing except free data
      * which was allocated via Nursery::allocateBuffer.
      */
-    MOZ_ASSERT_IF(clasp->finalize, clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE);
+    MOZ_ASSERT_IF(clasp->hasFinalize(), clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE);
 
     /* Make the object allocation. */
     JSObject* obj = static_cast(allocate(size));
diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp
index d4e32cb356..bab858ad02 100644
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -323,6 +323,8 @@ js::gc::GCRuntime::markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark)
 
     jit::MarkJitActivations(rt, trc);
 
+    rt->spsProfiler.trace(trc);
+
     if (!rt->isHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING);
 
diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp
index ed89278d8b..f564d2ae9b 100644
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -225,10 +225,6 @@ gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape)
     } while (shape);
 }
 
-void
-TraceObjectGroupCycleCollectorChildrenCallback(JS::CallbackTracer* trc,
-                                               void** thingp, JS::TraceKind kind);
-
 // Object groups can point to other object groups via an UnboxedLayout or the
 // the original unboxed group link. There can potentially be deep or cyclic
 // chains of such groups to trace through without going through a thing that
@@ -250,6 +246,12 @@ struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer
 void
 ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing)
 {
+    if (thing.is()) {
+        // The CC does not care about BaseShapes, and no additional GC things
+        // will be reached by following this edge.
+        return;
+    }
+
     if (thing.is() || thing.is()) {
         // Invoke the inner cycle collector callback on this child. It will not
         // recurse back into TraceChildren.
diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp
index 1c2e34288f..e92ba99e57 100644
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -8,6 +8,7 @@
 
 #include "jsgc.h"
 
+#include "gc/Policy.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/JitCompartment.h"
@@ -30,6 +31,7 @@ JS::Zone::Zone(JSRuntime* rt)
     types(this),
     compartments(),
     gcGrayRoots(),
+    typeDescrObjects(this, SystemAllocPolicy()),
     gcMallocBytes(0),
     gcMallocGCTriggered(false),
     usage(&rt->gc.usage),
@@ -56,6 +58,8 @@ JS::Zone::Zone(JSRuntime* rt)
 
 Zone::~Zone()
 {
+    MOZ_ASSERT_IF(typeDescrObjects.initialized(), typeDescrObjects.empty());
+
     JSRuntime* rt = runtimeFromMainThread();
     if (this == rt->gc.systemZone)
         rt->gc.systemZone = nullptr;
@@ -67,7 +71,10 @@ Zone::~Zone()
 bool Zone::init(bool isSystemArg)
 {
     isSystem = isSystemArg;
-    return uniqueIds_.init() && gcZoneGroupEdges.init() && gcWeakKeys.init();
+    return uniqueIds_.init() &&
+           gcZoneGroupEdges.init() &&
+           gcWeakKeys.init() &&
+           typeDescrObjects.init();
 }
 
 void
@@ -146,7 +153,7 @@ Zone::sweepBreakpoints(FreeOp* fop)
      */
 
     MOZ_ASSERT(isGCSweepingOrCompacting());
-    for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript* script = i.get();
         if (!script->hasAnyBreakpointsOrStepMode())
             continue;
diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h
index 8fda18b9b3..7c6cfd7867 100644
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -327,6 +327,15 @@ struct Zone : public JS::shadow::Zone,
     // can't be determined by examining this zone by itself.
     ZoneSet gcZoneGroupEdges;
 
+    // Keep track of all TypeDescr and related objects in this compartment.
+    // This is used by the GC to trace them all first when compacting, since the
+    // TypedObject trace hook may access these objects.
+    using TypeDescrObjectSet = js::GCHashSet,
+                                             js::SystemAllocPolicy>;
+    JS::WeakCache typeDescrObjects;
+
+
     // Malloc counter to measure memory pressure for GC scheduling. It runs from
     // gcMaxMallocBytes down to zero. This counter should be used only when it's
     // not possible to know the size of a free.
diff --git a/js/src/gdb/gdb-tests.cpp b/js/src/gdb/gdb-tests.cpp
index 414cf8a186..3d701fd584 100644
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -13,15 +13,19 @@
 
 using namespace JS;
 
-/* The class of the global object. */
-const JSClass global_class = {
-    "global", JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps global_classOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
+/* The class of the global object. */
+static const JSClass global_class = {
+    "global", JSCLASS_GLOBAL_FLAGS,
+    &global_classOps
+};
+
 template
 static inline T*
 checkPtr(T* ptr)
diff --git a/js/src/gdb/tests/test-Interpreter.cpp b/js/src/gdb/tests/test-Interpreter.cpp
index 5953d8bb9e..9e206ff6ad 100644
--- a/js/src/gdb/tests/test-Interpreter.cpp
+++ b/js/src/gdb/tests/test-Interpreter.cpp
@@ -72,16 +72,16 @@ FRAGMENT(Interpreter, Regs) {
 FRAGMENT(Interpreter, AbstractFramePtr) {
 
     js::AbstractFramePtr sfidptr;
-    GDBTestInitAbstractFramePtr(sfidptr, (js::ScriptFrameIter::Data*) 0xdeeb0);
+    GDBTestInitAbstractFramePtr(sfidptr, (js::ScriptFrameIter::Data*) uintptr_t(0xdeeb0));
 
     js::AbstractFramePtr ifptr;
-    GDBTestInitAbstractFramePtr(ifptr, (js::InterpreterFrame*) 0x8badf00);
+    GDBTestInitAbstractFramePtr(ifptr, (js::InterpreterFrame*) uintptr_t(0x8badf00));
 
     js::AbstractFramePtr bfptr;
-    GDBTestInitAbstractFramePtr(bfptr, (js::jit::BaselineFrame*) 0xbadcafe0);
+    GDBTestInitAbstractFramePtr(bfptr, (js::jit::BaselineFrame*) uintptr_t(0xbadcafe0));
 
     js::AbstractFramePtr rfptr;
-    GDBTestInitAbstractFramePtr(rfptr, (js::jit::RematerializedFrame*) 0xdabbad00);
+    GDBTestInitAbstractFramePtr(rfptr, (js::jit::RematerializedFrame*) uintptr_t(0xdabbad00));
 
     breakpoint();
 
diff --git a/js/src/jit-test/tests/atomics/basic-tests.js b/js/src/jit-test/tests/atomics/basic-tests.js
index e22f25f3a8..710822982e 100644
--- a/js/src/jit-test/tests/atomics/basic-tests.js
+++ b/js/src/jit-test/tests/atomics/basic-tests.js
@@ -376,7 +376,7 @@ function adHocExchange() {
 // ie, it must return false for n=8.
 //
 // SpiderMonkey has isLockFree(1), isLockFree(2), isLockFree(4) on all
-// supported platforms, though this is not guaranteed by the spec.
+// supported platforms, only the last is guaranteed by the spec.
 
 var sizes   = [    1,     2,     3,     4,     5,     6,     7,  8,
                    9,    10,    11,    12];
diff --git a/js/src/jit-test/tests/auto-regress/bug1263857.js b/js/src/jit-test/tests/auto-regress/bug1263857.js
new file mode 100644
index 0000000000..906e92740e
--- /dev/null
+++ b/js/src/jit-test/tests/auto-regress/bug1263857.js
@@ -0,0 +1,4 @@
+// |jit-test| allow-oom; allow-unhandlable-oom
+gcparam("maxBytes", gcparam("gcBytes") + 1);
+fullcompartmentchecks(true);
+/x/g[Symbol.replace]("        x".repeat(32768), "");
diff --git a/js/src/jit-test/tests/auto-regress/bug1263865.js b/js/src/jit-test/tests/auto-regress/bug1263865.js
new file mode 100644
index 0000000000..313d6d9c74
--- /dev/null
+++ b/js/src/jit-test/tests/auto-regress/bug1263865.js
@@ -0,0 +1,12 @@
+if (!('oomTest' in this))
+    quit();
+
+loadFile("");
+loadFile("");
+loadFile("Array.prototype.splice.call(1)");
+function loadFile(lfVarx) {
+    parseInt("1");
+    oomTest(function() {
+        eval(lfVarx);
+    });
+}
diff --git a/js/src/jit-test/tests/auto-regress/bug1263879.js b/js/src/jit-test/tests/auto-regress/bug1263879.js
new file mode 100644
index 0000000000..96ce00eae9
--- /dev/null
+++ b/js/src/jit-test/tests/auto-regress/bug1263879.js
@@ -0,0 +1,21 @@
+if (!('oomTest' in this))
+    quit();
+
+var lines = `
+
+
+
+"".replace([[2], 3])
+`.split('\n');
+var code = "";
+while (true) {
+    var line = lines.shift();
+    if (line == null)
+        break;
+    loadFile();
+    code += line + "\n";
+}
+loadFile(code);
+function loadFile(code) {
+    oomTest(() => eval(code));
+}
diff --git a/js/src/jit-test/tests/auto-regress/bug1264561.js b/js/src/jit-test/tests/auto-regress/bug1264561.js
new file mode 100644
index 0000000000..b5cc40bb22
--- /dev/null
+++ b/js/src/jit-test/tests/auto-regress/bug1264561.js
@@ -0,0 +1,6 @@
+var r = RegExp("");
+var s = "";
+s += "".replace(r, Function("x"));
+for (var x = 0; x < 5; x++) {
+    s += "".replace(r, this);
+}
diff --git a/js/src/jit-test/tests/auto-regress/bug1264823.js b/js/src/jit-test/tests/auto-regress/bug1264823.js
new file mode 100644
index 0000000000..67856c6c97
--- /dev/null
+++ b/js/src/jit-test/tests/auto-regress/bug1264823.js
@@ -0,0 +1,11 @@
+if (!('oomTest' in this))
+    quit();
+
+loadFile("");
+loadFile("");
+loadFile(` function lalala() {}
+    new Map([[1, 2]]).forEach(lalala)
+    `);
+function loadFile(lfVarx) oomTest(function() {
+    eval(lfVarx)
+})
diff --git a/js/src/jit-test/tests/baseline/bug1258301.js b/js/src/jit-test/tests/baseline/bug1258301.js
new file mode 100644
index 0000000000..ce510a0e0b
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug1258301.js
@@ -0,0 +1,5 @@
+x = new WeakMap;
+x.__proto__ = null;
+for (var i = 0; i < 3; i++)
+    x.someprop;
+gc();
diff --git a/js/src/jit-test/tests/debug/bug1254578.js b/js/src/jit-test/tests/debug/bug1254578.js
new file mode 100644
index 0000000000..828faf1869
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1254578.js
@@ -0,0 +1,23 @@
+// |jit-test| error:ReferenceError; slow
+
+if (!('oomTest' in this))
+  throw (new ReferenceError);
+
+var g = newGlobal();
+g.debuggeeGlobal = this;
+g.eval("(" + function() {
+    dbg = new Debugger(debuggeeGlobal);
+    dbg.onExceptionUnwind = function(frame, exc) {
+        var s = '!';
+        for (var f = frame; f; f = f.older)
+            debuggeeGlobal.log += s;
+    };
+} + ")();");
+var dbg = new Debugger;
+dbg.onNewGlobalObject = function(global) {
+    get.seen = true;
+};
+oomTest(function() {
+    newGlobal({
+    })
+});
diff --git a/js/src/jit-test/tests/gc/bug-1254108.js b/js/src/jit-test/tests/gc/bug-1254108.js
index c30d48069d..bf18e798ae 100644
--- a/js/src/jit-test/tests/gc/bug-1254108.js
+++ b/js/src/jit-test/tests/gc/bug-1254108.js
@@ -1,3 +1,4 @@
+|jit-test| error: Error
 function testChangeParam(key) {
     gcparam(key, 0x22222222);
 }
diff --git a/js/src/jit-test/tests/gc/bug-1258407.js b/js/src/jit-test/tests/gc/bug-1258407.js
new file mode 100644
index 0000000000..9338c981f2
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1258407.js
@@ -0,0 +1,10 @@
+|jit-test| error: Error
+gcparam("lowFrequencyHeapGrowth", 0.1);
+gcparam("lowFrequencyHeapGrowth", 2);
+let ok = false;
+try {
+    gcparam("lowFrequencyHeapGrowth", 0x22222222);
+} catch (e) {
+    ok = true;
+}
+assertEq(ok, true);
diff --git a/js/src/jit-test/tests/gc/bug-1259306.js b/js/src/jit-test/tests/gc/bug-1259306.js
new file mode 100644
index 0000000000..58788aaae6
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1259306.js
@@ -0,0 +1,15 @@
+if (!('oomTest' in this))
+    quit();
+
+oomTest(() => {
+    var lfGlobal = newGlobal();
+    var lfVarx = `
+        gczeal(8, 1);
+        try {
+            (5).replace(r, () => {});
+        } catch (x) {}
+        gczeal(0);
+    `;
+    lfGlobal.offThreadCompileScript(lfVarx);
+    lfGlobal.runOffThreadScript();
+});
diff --git a/js/src/jit-test/tests/gc/bug-1259490.js b/js/src/jit-test/tests/gc/bug-1259490.js
new file mode 100644
index 0000000000..89d09b3e75
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1259490.js
@@ -0,0 +1,4 @@
+gczeal(8);
+for (var k = 0; k < 99; ++k) {
+    uneval(-(0 ** (Object | 0 * Object)))
+}
diff --git a/js/src/jit-test/tests/ion/bug1247909.js b/js/src/jit-test/tests/ion/bug1247909.js
new file mode 100644
index 0000000000..7dd7efd0f7
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1247909.js
@@ -0,0 +1,12 @@
+// |jit-test| --ion-pgo=on;
+
+function test() {
+  foo(startTest("", c(""),
+    test([{ 0 : c(), 0 : toString("", c(), [], tab([])) }])
+  ));
+  function f() {};
+}
+
+try {
+  test();
+} catch(e) {}
diff --git a/js/src/jit-test/tests/ion/bug1261326.js b/js/src/jit-test/tests/ion/bug1261326.js
new file mode 100644
index 0000000000..fb4f43de8f
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1261326.js
@@ -0,0 +1,11 @@
+x = x = "";
+function Obj1(x) {
+    this.x = x;
+}
+function f() {
+    var o = {};
+    for (var i = 0; i < 1500; i++)
+        new Obj1(o);
+    Obj1('');
+}
+f();
diff --git a/js/src/jit-test/tests/ion/bug1265159.js b/js/src/jit-test/tests/ion/bug1265159.js
new file mode 100644
index 0000000000..30d3443f18
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1265159.js
@@ -0,0 +1,17 @@
+
+var thrown = false;
+try {
+    x = [0];
+    for (var i = 0; i < 5; ++i) {
+        if (i == 3)
+            Object.freeze(x);
+        else
+            x.pop();
+    }
+} catch (e) {
+    thrown = true;
+    assertEq(e instanceof TypeError, true);
+}
+
+assertEq(thrown, true);
+
diff --git a/js/src/jit/AtomicOperations.h b/js/src/jit/AtomicOperations.h
index d9edafa7e2..16196342a2 100644
--- a/js/src/jit/AtomicOperations.h
+++ b/js/src/jit/AtomicOperations.h
@@ -299,8 +299,11 @@ AtomicOperations::isLockfree(int32_t size)
 
     switch (size) {
       case 1:
+        return true;
       case 2:
+        return true;
       case 4:
+        // The spec requires Atomics.isLockFree(4) to return true.
         return true;
       case 8:
         // The spec requires Atomics.isLockFree(n) to return false
diff --git a/js/src/jit/BaselineCacheIR.cpp b/js/src/jit/BaselineCacheIR.cpp
index 51ab229a14..120040d9a0 100644
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -742,6 +742,51 @@ BaselineCacheIRCompiler::emitGuardProto()
     return true;
 }
 
+bool
+BaselineCacheIRCompiler::emitGuardClass()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    const Class* clasp = nullptr;
+    switch (reader.guardClassKind()) {
+      case GuardClassKind::Array:
+        clasp = &ArrayObject::class_;
+        break;
+      case GuardClassKind::UnboxedArray:
+        clasp = &UnboxedArrayObject::class_;
+        break;
+      case GuardClassKind::MappedArguments:
+        clasp = &MappedArgumentsObject::class_;
+        break;
+      case GuardClassKind::UnmappedArguments:
+        clasp = &UnmappedArgumentsObject::class_;
+        break;
+    }
+
+    MOZ_ASSERT(clasp);
+    masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, clasp, failure->label());
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitGuardSpecificObject()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    Address addr(stubAddress(reader.stubOffset()));
+    masm.branchPtr(Assembler::NotEqual, addr, obj, failure->label());
+    return true;
+}
+
 bool
 BaselineCacheIRCompiler::emitGuardNoUnboxedExpando()
 {
@@ -810,6 +855,69 @@ BaselineCacheIRCompiler::emitLoadUndefinedResult()
     return true;
 }
 
+bool
+BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+    masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch);
+
+    // Guard length fits in an int32.
+    masm.branchTest32(Assembler::Signed, scratch, scratch, failure->label());
+    masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
+
+    // The int32 type was monitored when attaching the stub, so we can
+    // just return.
+    emitReturnFromIC();
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg());
+    masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
+
+    // The int32 type was monitored when attaching the stub, so we can
+    // just return.
+    emitReturnFromIC();
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitLoadArgumentsObjectLengthResult()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // Get initial length value.
+    masm.unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()), scratch);
+
+    // Test if length has been overridden.
+    masm.branchTest32(Assembler::NonZero,
+                      scratch,
+                      Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
+                      failure->label());
+
+    // Shift out arguments length and return it. No need to type monitor
+    // because this stub always returns int32.
+    masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratch);
+    masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
+    emitReturnFromIC();
+    return true;
+}
+
 bool
 BaselineCacheIRCompiler::emitLoadObject()
 {
@@ -1008,7 +1116,8 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer, Cache
             return nullptr;
 
         CacheIRStubKey key(stubInfo);
-        jitCompartment->putCacheIRStubCode(lookup, key, code);
+        if (!jitCompartment->putCacheIRStubCode(lookup, key, code))
+            return nullptr;
     }
 
     // We got our shared stub code and stub info. Time to allocate and attach a
@@ -1043,13 +1152,16 @@ jit::TraceBaselineCacheIRStub(JSTracer* trc, ICStub* stub, const CacheIRStubInfo
           case StubField::GCType::NoGCThing:
             break;
           case StubField::GCType::Shape:
-            TraceEdge(trc, &stubInfo->getStubField(stub, field), "baseline-cacheir-shape");
+            TraceNullableEdge(trc, &stubInfo->getStubField(stub, field),
+                              "baseline-cacheir-shape");
             break;
           case StubField::GCType::ObjectGroup:
-            TraceEdge(trc, &stubInfo->getStubField(stub, field), "baseline-cacheir-group");
+            TraceNullableEdge(trc, &stubInfo->getStubField(stub, field),
+                              "baseline-cacheir-group");
             break;
           case StubField::GCType::JSObject:
-            TraceEdge(trc, &stubInfo->getStubField(stub, field), "baseline-cacheir-object");
+            TraceNullableEdge(trc, &stubInfo->getStubField(stub, field),
+                              "baseline-cacheir-object");
             break;
           case StubField::GCType::Limit:
             return; // Done.
diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp
index d2e02ff7de..caffb452c5 100644
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -774,7 +774,7 @@ IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape,
 
     // Watch out for resolve or addProperty hooks.
     if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj) ||
-        obj->getClass()->addProperty)
+        obj->getClass()->getAddProperty())
     {
         return false;
     }
diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp
index c16fa3c1f5..a48e2b23b0 100644
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -805,8 +805,6 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
             // Either an object or magic arguments.
             return MIRType_Value;
 
-          case ICStub::GetProp_ArrayLength:
-          case ICStub::GetProp_UnboxedArrayLength:
           case ICStub::GetProp_Unboxed:
           case ICStub::GetProp_TypedObject:
           case ICStub::GetProp_CallScripted:
@@ -814,7 +812,6 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
           case ICStub::GetProp_CallDOMProxyNative:
           case ICStub::GetProp_CallDOMProxyWithGenerationNative:
           case ICStub::GetProp_DOMProxyShadowed:
-          case ICStub::GetProp_ModuleNamespace:
           case ICStub::GetElem_NativeSlotName:
           case ICStub::GetElem_NativeSlotSymbol:
           case ICStub::GetElem_NativePrototypeSlotName:
diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp
index 9545af2f26..83e6b1f700 100644
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -56,10 +56,14 @@ GetPropIRGenerator::tryAttachStub(Maybe& writer)
         RootedObject obj(cx_, &val_.toObject());
         ObjOperandId objId = writer->guardIsObject(valId);
 
+        if (!emitted_ && !tryAttachObjectLength(*writer, obj, objId))
+            return false;
         if (!emitted_ && !tryAttachNative(*writer, obj, objId))
             return false;
         if (!emitted_ && !tryAttachUnboxedExpando(*writer, obj, objId))
             return false;
+        if (!emitted_ && !tryAttachModuleNamespace(*writer, obj, objId))
+            return false;
     }
 
     return true;
@@ -274,3 +278,77 @@ GetPropIRGenerator::tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject
     EmitReadSlotResult(writer, obj, obj, shape, objId);
     return true;
 }
+
+bool
+GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
+{
+    MOZ_ASSERT(!emitted_);
+
+    if (name_ != cx_->names().length)
+        return true;
+
+    if (obj->is()) {
+        // Make sure int32 is added to the TypeSet before we attach a stub, so
+        // the stub can return int32 values without monitoring the result.
+        if (obj->as().length() > INT32_MAX)
+            return true;
+
+        writer.guardClass(objId, GuardClassKind::Array);
+        writer.loadInt32ArrayLengthResult(objId);
+        emitted_ = true;
+        return true;
+    }
+
+    if (obj->is()) {
+        writer.guardClass(objId, GuardClassKind::UnboxedArray);
+        writer.loadUnboxedArrayLengthResult(objId);
+        emitted_ = true;
+        return true;
+    }
+
+    if (obj->is() && !obj->as().hasOverriddenLength()) {
+        if (obj->is()) {
+            writer.guardClass(objId, GuardClassKind::MappedArguments);
+        } else {
+            MOZ_ASSERT(obj->is());
+            writer.guardClass(objId, GuardClassKind::UnmappedArguments);
+        }
+        writer.loadArgumentsObjectLengthResult(objId);
+        emitted_ = true;
+        return true;
+    }
+
+    return true;
+}
+
+bool
+GetPropIRGenerator::tryAttachModuleNamespace(CacheIRWriter& writer, HandleObject obj,
+                                             ObjOperandId objId)
+{
+    MOZ_ASSERT(!emitted_);
+
+    if (!obj->is())
+        return true;
+
+    Rooted ns(cx_, &obj->as());
+    RootedModuleEnvironmentObject env(cx_);
+    RootedShape shape(cx_);
+    if (!ns->bindings().lookup(NameToId(name_), env.address(), shape.address()))
+        return true;
+
+    // Don't emit a stub until the target binding has been initialized.
+    if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
+        return true;
+
+    if (IsIonEnabled(cx_))
+        EnsureTrackPropertyTypes(cx_, env, shape->propid());
+
+    emitted_ = true;
+
+    // Check for the specific namespace object.
+    writer.guardSpecificObject(objId, ns);
+
+    ObjOperandId envId = writer.loadObject(env);
+    EmitLoadSlotResult(writer, envId, env, shape);
+    return true;
+}
diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h
index 67a78093bf..26396aed7a 100644
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -82,6 +82,8 @@ class ObjOperandId : public OperandId
     _(GuardShape)                         \
     _(GuardGroup)                         \
     _(GuardProto)                         \
+    _(GuardClass)                         \
+    _(GuardSpecificObject)                \
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
     _(LoadObject)                         \
@@ -89,6 +91,9 @@ class ObjOperandId : public OperandId
     _(LoadUnboxedExpando)                 \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
+    _(LoadInt32ArrayLengthResult)         \
+    _(LoadUnboxedArrayLengthResult)       \
+    _(LoadArgumentsObjectLengthResult)    \
     _(LoadUndefinedResult)
 
 enum class CacheOp {
@@ -114,6 +119,16 @@ struct StubField {
     {}
 };
 
+// We use this enum as GuardClass operand, instead of storing Class* pointers
+// in the IR, to keep the IR compact and the same size on all platforms.
+enum class GuardClassKind
+{
+    Array,
+    UnboxedArray,
+    MappedArguments,
+    UnmappedArguments,
+};
+
 // Class to record CacheIR + some additional metadata for code generation.
 class MOZ_RAII CacheIRWriter
 {
@@ -240,6 +255,15 @@ class MOZ_RAII CacheIRWriter
         writeOpWithOperandId(CacheOp::GuardProto, obj);
         addStubWord(uintptr_t(proto), StubField::GCType::JSObject);
     }
+    void guardClass(ObjOperandId obj, GuardClassKind kind) {
+        MOZ_ASSERT(uint32_t(kind) <= UINT8_MAX);
+        writeOpWithOperandId(CacheOp::GuardClass, obj);
+        buffer_.writeByte(uint32_t(kind));
+    }
+    void guardSpecificObject(ObjOperandId obj, JSObject* expected) {
+        writeOpWithOperandId(CacheOp::GuardSpecificObject, obj);
+        addStubWord(uintptr_t(expected), StubField::GCType::JSObject);
+    }
     void guardNoUnboxedExpando(ObjOperandId obj) {
         writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj);
     }
@@ -280,6 +304,15 @@ class MOZ_RAII CacheIRWriter
         writeOpWithOperandId(CacheOp::LoadDynamicSlotResult, obj);
         addStubWord(offset, StubField::GCType::NoGCThing);
     }
+    void loadInt32ArrayLengthResult(ObjOperandId obj) {
+        writeOpWithOperandId(CacheOp::LoadInt32ArrayLengthResult, obj);
+    }
+    void loadUnboxedArrayLengthResult(ObjOperandId obj) {
+        writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj);
+    }
+    void loadArgumentsObjectLengthResult(ObjOperandId obj) {
+        writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj);
+    }
 };
 
 class CacheIRStubInfo;
@@ -313,9 +346,9 @@ class MOZ_RAII CacheIRReader
     ObjOperandId objOperandId() {
         return ObjOperandId(buffer_.readByte());
     }
-    uint32_t stubOffset() {
-        return buffer_.readByte();
-    }
+
+    uint32_t stubOffset() { return buffer_.readByte(); }
+    GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); }
 
     bool matchOp(CacheOp op) {
         const uint8_t* pos = buffer_.currentPosition();
@@ -356,6 +389,8 @@ class MOZ_RAII GetPropIRGenerator
 
     bool tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
     bool tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
+    bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
+    bool tryAttachModuleNamespace(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
 
     GetPropIRGenerator(const GetPropIRGenerator&) = delete;
     GetPropIRGenerator& operator=(const GetPropIRGenerator&) = delete;
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 69a3e17cbc..e0c04c7555 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1536,6 +1536,11 @@ JitCompartment::generateRegExpMatcherStub(JSContext* cx)
     masm.createGCObject(object, temp2, templateObject, gc::DefaultHeap, &matchResultFallback);
     masm.bind(&matchResultJoin);
 
+    // Initialize slots of result object.
+    masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
+    masm.storeValue(templateObject->getSlot(0), Address(temp2, 0));
+    masm.storeValue(templateObject->getSlot(1), Address(temp2, sizeof(Value)));
+
     size_t elementsOffset = NativeObject::offsetOfFixedElements();
 
 #ifdef DEBUG
@@ -2097,9 +2102,9 @@ CodeGenerator::visitRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
 
     masm.loadJSContext(temp);
     masm.loadPtr(Address(temp, JSContext::offsetOfCompartment()), temp);
-    masm.loadPtr(Address(temp, JSCompartment::offsetOfRegExps()), temp);
-    masm.loadPtr(Address(temp, RegExpCompartment::offsetOfOptimizableRegExpPrototypeShape()),
-                 temp);
+    size_t offset = JSCompartment::offsetOfRegExps() +
+                    RegExpCompartment::offsetOfOptimizableRegExpPrototypeShape();
+    masm.loadPtr(Address(temp, offset), temp);
 
     masm.loadPtr(Address(object, JSObject::offsetOfShape()), output);
     masm.branchPtr(Assembler::NotEqual, output, temp, ool->entry());
@@ -2166,9 +2171,9 @@ CodeGenerator::visitRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
 
     masm.loadJSContext(temp);
     masm.loadPtr(Address(temp, JSContext::offsetOfCompartment()), temp);
-    masm.loadPtr(Address(temp, JSCompartment::offsetOfRegExps()), temp);
-    masm.loadPtr(Address(temp, RegExpCompartment::offsetOfOptimizableRegExpInstanceShape()),
-                 temp);
+    size_t offset = JSCompartment::offsetOfRegExps() +
+                    RegExpCompartment::offsetOfOptimizableRegExpInstanceShape();
+    masm.loadPtr(Address(temp, offset), temp);
 
     masm.loadPtr(Address(object, JSObject::offsetOfShape()), output);
     masm.branchPtr(Assembler::NotEqual, output, temp, ool->entry());
@@ -2283,6 +2288,7 @@ CodeGenerator::visitBinarySharedStub(LBinarySharedStub* lir)
       case JSOP_MUL:
       case JSOP_DIV:
       case JSOP_MOD:
+      case JSOP_POW:
         emitSharedStub(ICStub::Kind::BinaryArith_Fallback, lir);
         break;
       case JSOP_LT:
@@ -6021,8 +6027,12 @@ CodeGenerator::visitGetNextMapEntryForIterator(LGetNextMapEntryForIterator* lir)
 
         Address keyAddress(front, ValueMap::Entry::offsetOfKey());
         Address valueAddress(front, ValueMap::Entry::offsetOfValue());
-        masm.storeValue(keyAddress, Address(result, elementsOffset), temp);
-        masm.storeValue(valueAddress, Address(result, elementsOffset + sizeof(Value)), temp);
+        Address keyElemAddress(result, elementsOffset);
+        Address valueElemAddress(result, elementsOffset + sizeof(Value));
+        masm.patchableCallPreBarrier(keyElemAddress, MIRType_Value);
+        masm.patchableCallPreBarrier(valueElemAddress, MIRType_Value);
+        masm.storeValue(keyAddress, keyElemAddress, temp);
+        masm.storeValue(valueAddress, valueElemAddress, temp);
 
         Label keyIsNotObject, valueIsNotNurseryObject, emitBarrier;
         masm.branchTestObject(Assembler::NotEqual, keyAddress, &keyIsNotObject);
@@ -7522,25 +7532,35 @@ CodeGenerator::visitNotV(LNotV* lir)
 void
 CodeGenerator::visitBoundsCheck(LBoundsCheck* lir)
 {
-    if (lir->index()->isConstant()) {
+    const LAllocation* index = lir->index();
+    const LAllocation* length = lir->length();
+    LSnapshot* snapshot = lir->snapshot();
+
+    if (index->isConstant()) {
         // Use uint32 so that the comparison is unsigned.
-        uint32_t index = ToInt32(lir->index());
-        if (lir->length()->isConstant()) {
-            uint32_t length = ToInt32(lir->length());
-            if (index < length)
+        uint32_t idx = ToInt32(index);
+        if (length->isConstant()) {
+            uint32_t len = ToInt32(lir->length());
+            if (idx < len)
                 return;
-            bailout(lir->snapshot());
-        } else {
-            bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(index),
-                         lir->snapshot());
+            bailout(snapshot);
+            return;
         }
-    } else if (lir->length()->isConstant()) {
-        bailoutCmp32(Assembler::AboveOrEqual, ToRegister(lir->index()),
-                     Imm32(ToInt32(lir->length())), lir->snapshot());
-    } else {
-        bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()),
-                     ToRegister(lir->index()), lir->snapshot());
+
+        if (length->isRegister())
+            bailoutCmp32(Assembler::BelowOrEqual, ToRegister(length), Imm32(idx), snapshot);
+        else
+            bailoutCmp32(Assembler::BelowOrEqual, ToAddress(length), Imm32(idx), snapshot);
+        return;
     }
+
+    Register indexReg = ToRegister(index);
+    if (length->isConstant())
+        bailoutCmp32(Assembler::AboveOrEqual, indexReg, Imm32(ToInt32(length)), snapshot);
+    else if (length->isRegister())
+        bailoutCmp32(Assembler::BelowOrEqual, ToRegister(length), indexReg, snapshot);
+    else
+        bailoutCmp32(Assembler::BelowOrEqual, ToAddress(length), indexReg, snapshot);
 }
 
 void
@@ -7550,13 +7570,17 @@ CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir)
     int32_t max = lir->mir()->maximum();
     MOZ_ASSERT(max >= min);
 
+    const LAllocation* length = lir->length();
+    LSnapshot* snapshot = lir->snapshot();
     Register temp = ToRegister(lir->getTemp(0));
     if (lir->index()->isConstant()) {
         int32_t nmin, nmax;
         int32_t index = ToInt32(lir->index());
         if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) {
-            bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(nmax),
-                         lir->snapshot());
+            if (length->isRegister())
+                bailoutCmp32(Assembler::BelowOrEqual, ToRegister(length), Imm32(nmax), snapshot);
+            else
+                bailoutCmp32(Assembler::BelowOrEqual, ToAddress(length), Imm32(nmax), snapshot);
             return;
         }
         masm.mov(ImmWord(index), temp);
@@ -7571,10 +7595,10 @@ CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir)
         if (min != 0) {
             Label bail;
             masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail);
-            bailoutFrom(&bail, lir->snapshot());
+            bailoutFrom(&bail, snapshot);
         }
 
-        bailoutCmp32(Assembler::LessThan, temp, Imm32(0), lir->snapshot());
+        bailoutCmp32(Assembler::LessThan, temp, Imm32(0), snapshot);
 
         if (min != 0) {
             int32_t diff;
@@ -7594,13 +7618,16 @@ CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir)
         if (max < 0) {
             Label bail;
             masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail);
-            bailoutFrom(&bail, lir->snapshot());
+            bailoutFrom(&bail, snapshot);
         } else {
             masm.add32(Imm32(max), temp);
         }
     }
 
-    bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), temp, lir->snapshot());
+    if (length->isRegister())
+        bailoutCmp32(Assembler::BelowOrEqual, ToRegister(length), temp, snapshot);
+    else
+        bailoutCmp32(Assembler::BelowOrEqual, ToAddress(length), temp, snapshot);
 }
 
 void
@@ -8052,6 +8079,17 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
     if (mir->maybeUndefined()) {
         Label notEmpty;
         masm.branchTest32(Assembler::NonZero, lengthTemp, lengthTemp, ¬Empty);
+
+        // According to the spec we need to set the length 0 (which is already 0).
+        // This is observable when the array length is made non-writable.
+        // Handle this case in the OOL. When freezing an unboxed array it is converted
+        // to an normal array.
+        if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
+            Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
+            Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
+            masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
+        }
+
         masm.moveValue(UndefinedValue(), out.valueReg());
         masm.jump(&done);
         masm.bind(¬Empty);
@@ -10171,29 +10209,38 @@ CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole* lir)
     Scalar::Type arrayType = lir->mir()->arrayType();
     int width = Scalar::byteSize(arrayType);
 
+    const LAllocation* index = lir->index();
+    const LAllocation* length = lir->length();
+
     bool guardLength = true;
-    if (lir->index()->isConstant() && lir->length()->isConstant()) {
-        uint32_t idx = ToInt32(lir->index());
-        uint32_t len = ToInt32(lir->length());
+    if (index->isConstant() && length->isConstant()) {
+        uint32_t idx = ToInt32(index);
+        uint32_t len = ToInt32(length);
         if (idx >= len)
             return;
         guardLength = false;
     }
     Label skip;
-    if (lir->index()->isConstant()) {
-        uint32_t idx = ToInt32(lir->index());
-        if (guardLength)
-            masm.branch32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(idx), &skip);
+    if (index->isConstant()) {
+        uint32_t idx = ToInt32(index);
+        if (guardLength) {
+            if (length->isRegister())
+                masm.branch32(Assembler::BelowOrEqual, ToRegister(length), Imm32(idx), &skip);
+            else
+                masm.branch32(Assembler::BelowOrEqual, ToAddress(length), Imm32(idx), &skip);
+        }
         Address dest(elements, idx * width);
         StoreToTypedArray(masm, arrayType, value, dest);
     } else {
-        Register idxReg = ToRegister(lir->index());
+        Register idxReg = ToRegister(index);
         MOZ_ASSERT(guardLength);
-        if (lir->length()->isConstant())
-            masm.branch32(Assembler::AboveOrEqual, idxReg, Imm32(ToInt32(lir->length())), &skip);
+        if (length->isConstant())
+            masm.branch32(Assembler::AboveOrEqual, idxReg, Imm32(ToInt32(length)), &skip);
+        else if (length->isRegister())
+            masm.branch32(Assembler::BelowOrEqual, ToRegister(length), idxReg, &skip);
         else
-            masm.branch32(Assembler::BelowOrEqual, ToOperand(lir->length()), idxReg, &skip);
-        BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
+            masm.branch32(Assembler::BelowOrEqual, ToAddress(length), idxReg, &skip);
+        BaseIndex dest(elements, ToRegister(index), ScaleFromElemWidth(width));
         StoreToTypedArray(masm, arrayType, value, dest);
     }
     if (guardLength)
@@ -10207,7 +10254,10 @@ CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir)
     Register output = ToRegister(lir->output());
 
     // Keep this in sync with isLockfree() in jit/AtomicOperations.h.
-    MOZ_ASSERT(!AtomicOperations::isLockfree(8));
+    MOZ_ASSERT(AtomicOperations::isLockfree(1));  // Implementation artifact
+    MOZ_ASSERT(AtomicOperations::isLockfree(2));  // Implementation artifact
+    MOZ_ASSERT(AtomicOperations::isLockfree(4));  // Spec requirement
+    MOZ_ASSERT(!AtomicOperations::isLockfree(8)); // Implementation invariant, for now
 
     Label Ldone, Lfailed;
     masm.move32(Imm32(1), output);
@@ -10668,19 +10718,29 @@ CodeGenerator::visitIsCallable(LIsCallable* ins)
     OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
     addOutOfLineCode(ool, ins->mir());
 
-    Label notFunction, done;
+    Label notFunction, hasCOps, done;
     masm.loadObjClass(object, output);
 
     // Just skim proxies off. Their notion of isCallable() is more complicated.
     masm.branchTestClassIsProxy(true, output, ool->entry());
 
-    // An object is callable iff (is() || getClass()->call.
+    // An object is callable iff:
+    //   is() || (getClass()->cOps && getClass()->cOps->call).
     masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), ¬Function);
     masm.move32(Imm32(1), output);
     masm.jump(&done);
 
     masm.bind(¬Function);
-    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::Class, call)), ImmPtr(nullptr), output);
+    masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
+                   ImmPtr(nullptr), &hasCOps);
+    masm.move32(Imm32(0), output);
+    masm.jump(&done);
+
+    masm.bind(&hasCOps);
+    masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
+    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, call)),
+                   ImmPtr(nullptr), output);
+
     masm.bind(&done);
     masm.bind(ool->rejoin());
 }
@@ -10730,7 +10790,7 @@ CodeGenerator::visitIsConstructor(LIsConstructor* ins)
     OutOfLineIsConstructor* ool = new(alloc()) OutOfLineIsConstructor(ins);
     addOutOfLineCode(ool, ins->mir());
 
-    Label notFunction, notConstructor, done;
+    Label notFunction, notConstructor, hasCOps, done;
     masm.loadObjClass(object, output);
 
     // Just skim proxies off. Their notion of isConstructor() is more complicated.
@@ -10738,7 +10798,7 @@ CodeGenerator::visitIsConstructor(LIsConstructor* ins)
 
     // An object is constructor iff
     //  ((is() && as().isConstructor) ||
-    //   getClass()->construct).
+    //   (getClass()->cOps && getClass()->cOps->construct)).
     masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), ¬Function);
     masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
     masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
@@ -10750,7 +10810,16 @@ CodeGenerator::visitIsConstructor(LIsConstructor* ins)
     masm.jump(&done);
 
     masm.bind(¬Function);
-    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::Class, construct)), ImmPtr(nullptr), output);
+    masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
+                   ImmPtr(nullptr), &hasCOps);
+    masm.move32(Imm32(0), output);
+    masm.jump(&done);
+
+    masm.bind(&hasCOps);
+    masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
+    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, construct)),
+                   ImmPtr(nullptr), output);
+
     masm.bind(&done);
     masm.bind(ool->rejoin());
 }
diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
index 9d26ab784d..96b5832bb2 100644
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -590,7 +590,7 @@ JitRuntime::Mark(JSTracer* trc)
 {
     MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
     Zone* zone = trc->runtime()->atomsCompartment()->zone();
-    for (gc::ZoneCellIter i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) {
+    for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) {
         JitCode* code = i.get();
         TraceRoot(trc, &code, "wrapper");
     }
@@ -1329,7 +1329,7 @@ jit::ToggleBarriers(JS::Zone* zone, bool needs)
     if (!rt->hasJitRuntime())
         return;
 
-    for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
+    for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript* script = i.get();
         if (script->hasIonScript())
             script->ionScript()->toggleBarriers(needs);
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 65c27461d5..c3ddf6b203 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -103,13 +103,13 @@ jit::NewBaselineFrameInspector(TempAllocator* temp, BaselineFrame* frame, Compil
         }
     }
 
-    if (!inspector->varTypes.reserve(frame->script()->nfixed()))
+    if (!inspector->varTypes.reserve(frame->numValueSlots()))
         return nullptr;
-    for (size_t i = 0; i < frame->script()->nfixed(); i++) {
+    for (size_t i = 0; i < frame->numValueSlots(); i++) {
         if (info->isSlotAliasedAtOsr(i + info->firstLocalSlot())) {
             inspector->varTypes.infallibleAppend(TypeSet::UndefinedType());
         } else {
-            TypeSet::Type type = TypeSet::GetMaybeUntrackedValueType(frame->unaliasedLocal(i));
+            TypeSet::Type type = TypeSet::GetMaybeUntrackedValueType(*frame->valueSlot(i));
             inspector->varTypes.infallibleAppend(type);
         }
     }
@@ -1067,10 +1067,6 @@ IonBuilder::buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoi
         current->initSlot(info().argSlot(i), arg);
     }
 
-    // Initialize the scope chain now that args are initialized.
-    if (!initScopeChain(callInfo.fun()))
-        return false;
-
     JitSpew(JitSpew_Inlining, "Initializing %u local slots; fixed lexicals begin at %u",
             info().nlocals(), info().fixedLexicalBegin());
 
@@ -1089,6 +1085,11 @@ IonBuilder::buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoi
 
     insertRecompileCheck();
 
+    // Initialize the scope chain now that all resume points operands are
+    // initialized.
+    if (!initScopeChain(callInfo.fun()))
+        return false;
+
     if (!traverseBytecode())
         return false;
 
@@ -7878,6 +7879,11 @@ IonBuilder::newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool
         // in which case this may avoid restarts of loop analysis or bailouts
         // during the OSR itself.
 
+        MOZ_ASSERT(info().firstLocalSlot() - info().firstArgSlot() ==
+                   baselineFrame_->argTypes.length());
+        MOZ_ASSERT(block->stackDepth() - info().firstLocalSlot() ==
+                   baselineFrame_->varTypes.length());
+
         // Unbox the MOsrValue if it is known to be unboxable.
         for (uint32_t i = info().startArgSlot(); i < block->stackDepth(); i++) {
 
@@ -7886,11 +7892,6 @@ IonBuilder::newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool
             if (info().isSlotAliasedAtOsr(i))
                 continue;
 
-            // Don't bother with expression stack values. The stack should be
-            // empty except for let variables (not Ion-compiled) or iterators.
-            if (i >= info().firstStackSlot())
-                continue;
-
             MPhi* phi = block->getSlot(i)->toPhi();
 
             // Get the type from the baseline frame.
@@ -12202,9 +12203,13 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName
         }
 
         // Monomorphic load from an unboxed object.
-        obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
+        ObjectGroup* group = receivers[0].group;
+        if (obj->resultTypeSet() && !obj->resultTypeSet()->hasType(TypeSet::ObjectType(group)))
+            return true;
 
-        const UnboxedLayout::Property* property = receivers[0].group->unboxedLayout().lookup(name);
+        obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
+
+        const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
         MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types);
         current->push(load);
 
@@ -12958,6 +12963,9 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
         spew("Inlining monomorphic unboxed SETPROP");
 
         ObjectGroup* group = receivers[0].group;
+        if (!objTypes->hasType(TypeSet::ObjectType(group)))
+            return true;
+
         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
 
         const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp
index 3c959fbe29..d24efc63df 100644
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -484,7 +484,7 @@ IsCacheableNoProperty(JSObject* obj, JSObject* holder, Shape* shape, jsbytecode*
 
     // Just because we didn't find the property on the object doesn't mean it
     // won't magically appear through various engine hacks.
-    if (obj->getClass()->getProperty)
+    if (obj->getClass()->getGetProperty())
         return false;
 
     // Don't generate missing property ICs if we skipped a non-native object, as
@@ -3294,7 +3294,7 @@ IsPropertyAddInlineable(JSContext* cx, NativeObject* obj, HandleId id, ConstantO
         return false;
 
     // Likewise for an addProperty hook, since we'll need to invoke it.
-    if (obj->getClass()->addProperty)
+    if (obj->getClass()->getAddProperty())
         return false;
 
     if (!obj->nonProxyIsExtensible() || !shape->writable())
diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp
index fb9c781644..083aeeaf65 100644
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1173,7 +1173,7 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
 void
 MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject)
 {
-    const UnboxedLayout& layout = templateObject->layout();
+    const UnboxedLayout& layout = templateObject->layoutDontCheckGeneration();
 
     // Initialize reference fields of the object, per UnboxedPlainObject::create.
     if (const int32_t* list = layout.traceList()) {
diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h
index 579d4861c0..cca5d4cfc8 100644
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -817,8 +817,8 @@ class MacroAssembler : public MacroAssemblerSpecific
         DEFINED_ON(x86_shared);
     inline void branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
 
-    inline void branch32(Condition cond, const Operand& lhs, Register rhs, Label* label) PER_SHARED_ARCH;
-    inline void branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
+    inline void branch32(Condition cond, const Operand& lhs, Register rhs, Label* label) DEFINED_ON(x86_shared);
+    inline void branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label) DEFINED_ON(x86_shared);
 
     inline void branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
diff --git a/js/src/jit/Registers.h b/js/src/jit/Registers.h
index 300e25c6eb..c162df1a5f 100644
--- a/js/src/jit/Registers.h
+++ b/js/src/jit/Registers.h
@@ -154,9 +154,9 @@ class MachineState
   public:
     MachineState() {
 #ifndef JS_CODEGEN_NONE
-        for (unsigned i = 0; i < Registers::Total; i++)
+        for (uintptr_t i = 0; i < Registers::Total; i++)
             regs_[i] = reinterpret_cast(i + 0x100);
-        for (unsigned i = 0; i < FloatRegisters::Total; i++)
+        for (uintptr_t i = 0; i < FloatRegisters::Total; i++)
             fpregs_[i] = reinterpret_cast(i + 0x200);
 #endif
     }
diff --git a/js/src/jit/RematerializedFrame.cpp b/js/src/jit/RematerializedFrame.cpp
index 22c08bb75f..115bc67db8 100644
--- a/js/src/jit/RematerializedFrame.cpp
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -78,7 +78,8 @@ RematerializedFrame::RematerializeInlineFrames(JSContext* cx, uint8_t* top,
                                                MaybeReadFallback& fallback,
                                                Vector& frames)
 {
-    if (!frames.resize(iter.frameCount()))
+    Vector tempFrames(cx);
+    if (!tempFrames.resize(iter.frameCount()))
         return false;
 
     while (true) {
@@ -91,13 +92,14 @@ RematerializedFrame::RematerializeInlineFrames(JSContext* cx, uint8_t* top,
                 return false;
         }
 
-        frames[frameNo] = frame;
+        tempFrames[frameNo] = frame;
 
         if (!iter.more())
             break;
         ++iter;
     }
 
+    frames = Move(tempFrames);
     return true;
 }
 
diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
index 62ef2aee8f..a2609fefc0 100644
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -460,12 +460,6 @@ ICStub::trace(JSTracer* trc)
         TraceEdge(trc, &callStub->getter(), "baseline-getpropcallnativeglobal-stub-getter");
         break;
       }
-      case ICStub::GetProp_ModuleNamespace: {
-        ICGetProp_ModuleNamespace* nsStub = toGetProp_ModuleNamespace();
-        TraceEdge(trc, &nsStub->getNamespace(), "baseline-getprop-modulenamespace-stub-namespace");
-        TraceEdge(trc, &nsStub->environment(), "baseline-getprop-modulenamespace-stub-environment");
-        break;
-      }
       case ICStub::SetProp_Native: {
         ICSetProp_Native* propStub = toSetProp_Native();
         TraceEdge(trc, &propStub->shape(), "baseline-setpropnative-stub-shape");
@@ -2117,51 +2111,6 @@ TryAttachLengthStub(JSContext* cx, SharedStubInfo* info,
         return true;
     }
 
-    if (!val.isObject())
-        return true;
-
-    RootedObject obj(cx, &val.toObject());
-
-    if (obj->is() && res.isInt32()) {
-        JitSpew(JitSpew_BaselineIC, "  Generating GetProp(Array.length) stub");
-        ICGetProp_ArrayLength::Compiler compiler(cx, info->engine());
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
-        if (!newStub)
-            return false;
-
-        *attached = true;
-        stub->addNewStub(newStub);
-        return true;
-    }
-
-    if (obj->is() && res.isInt32()) {
-        JitSpew(JitSpew_BaselineIC, "  Generating GetProp(UnboxedArray.length) stub");
-        ICGetProp_UnboxedArrayLength::Compiler compiler(cx, info->engine());
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
-        if (!newStub)
-            return false;
-
-        *attached = true;
-        stub->addNewStub(newStub);
-        return true;
-    }
-
-    if (obj->is() && res.isInt32()) {
-        JitSpew(JitSpew_BaselineIC, "  Generating GetProp(ArgsObj.length %s) stub",
-                obj->is() ? "Mapped" : "Unmapped");
-        ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Mapped;
-        if (obj->is())
-            which = ICGetProp_ArgumentsLength::Unmapped;
-        ICGetProp_ArgumentsLength::Compiler compiler(cx, info->engine(), which);
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
-        if (!newStub)
-            return false;
-
-        *attached = true;
-        stub->addNewStub(newStub);
-        return true;
-    }
-
     return true;
 }
 
@@ -2687,48 +2636,6 @@ TryAttachTypedObjectGetPropStub(JSContext* cx, SharedStubInfo* info,
     return true;
 }
 
-static bool
-TryAttachModuleNamespaceGetPropStub(JSContext* cx, SharedStubInfo* info,
-                                    ICGetProp_Fallback* stub, HandlePropertyName name,
-                                    HandleValue val, bool* attached)
-{
-    MOZ_ASSERT(!*attached);
-
-    if (!ModuleNamespaceObject::isInstance(val))
-        return true;
-
-    Rooted ns(cx, &val.toObject().as());
-
-    RootedModuleEnvironmentObject env(cx);
-    RootedShape shape(cx);
-    if (!ns->bindings().lookup(NameToId(name), env.address(), shape.address()))
-        return true;
-
-    // Don't emit a stub until the target binding has been initialized.
-    if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
-        return true;
-
-    ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-
-    bool isFixedSlot;
-    uint32_t offset;
-    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
-
-    // Instantiate this property for singleton holders, for use during Ion compilation.
-    if (IsIonEnabled(cx))
-        EnsureTrackPropertyTypes(cx, env, shape->propid());
-
-    ICGetProp_ModuleNamespace::Compiler compiler(cx, info->engine(), monitorStub,
-                                                 ns, env, isFixedSlot, offset);
-    ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
-    if (!newStub)
-        return false;
-    stub->addNewStub(newStub);
-
-    *attached = true;
-    return true;
-}
-
 static bool
 TryAttachPrimitiveGetPropStub(JSContext* cx, SharedStubInfo* info,
                               ICGetProp_Fallback* stub, HandlePropertyName name,
@@ -2797,7 +2704,7 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name,
                 return false;
             if (curObj->as().contains(cx, NameToId(name)))
                 return false;
-            if (curObj->getClass()->getProperty)
+            if (curObj->getClass()->getGetProperty())
                 return false;
         } else if (curObj != obj) {
             // Non-native objects are only handled as the original receiver.
@@ -2972,11 +2879,6 @@ DoGetPropFallback(JSContext* cx, void* payload, ICGetProp_Fallback* stub_,
     if (attached)
         return true;
 
-    if (!TryAttachModuleNamespaceGetPropStub(cx, &info, stub, name, val, &attached))
-        return false;
-    if (attached)
-        return true;
-
     if (val.isString() || val.isNumber() || val.isBoolean()) {
         if (!TryAttachPrimitiveGetPropStub(cx, &info, stub, name, val, res, &attached))
             return false;
@@ -3048,58 +2950,6 @@ ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<
     }
 }
 
-bool
-ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-
-    Register scratch = R1.scratchReg();
-
-    // Unbox R0 and guard it's an array.
-    Register obj = masm.extractObject(R0, ExtractTemp0);
-    masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &ArrayObject::class_, &failure);
-
-    // Load obj->elements->length.
-    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
-    masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch);
-
-    // Guard length fits in an int32.
-    masm.branchTest32(Assembler::Signed, scratch, scratch, &failure);
-
-    masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-bool
-ICGetProp_UnboxedArrayLength::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-
-    Register scratch = R1.scratchReg();
-
-    // Unbox R0 and guard it's an unboxed array.
-    Register obj = masm.extractObject(R0, ExtractTemp0);
-    masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &UnboxedArrayObject::class_, &failure);
-
-    // Load obj->length.
-    masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), scratch);
-
-    masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 bool
 ICGetProp_StringLength::Compiler::generateStubCode(MacroAssembler& masm)
 {
@@ -3807,53 +3657,22 @@ ICGetProp_DOMProxyShadowed::Compiler::generateStubCode(MacroAssembler& masm)
 bool
 ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler& masm)
 {
+    MOZ_ASSERT(which_ == ICGetProp_ArgumentsLength::Magic);
+
     Label failure;
-    if (which_ == ICGetProp_ArgumentsLength::Magic) {
-        // Ensure that this is lazy arguments.
-        masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
 
-        // Ensure that frame has not loaded different arguments object since.
-        masm.branchTest32(Assembler::NonZero,
-                          Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
-                          Imm32(BaselineFrame::HAS_ARGS_OBJ),
-                          &failure);
+    // Ensure that this is lazy arguments.
+    masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
 
-        Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
-        masm.loadPtr(actualArgs, R0.scratchReg());
-        masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
-        EmitReturnFromIC(masm);
-
-        masm.bind(&failure);
-        EmitStubGuardFailure(masm);
-        return true;
-    }
-    MOZ_ASSERT(which_ == ICGetProp_ArgumentsLength::Mapped ||
-               which_ == ICGetProp_ArgumentsLength::Unmapped);
-
-    const Class* clasp = (which_ == ICGetProp_ArgumentsLength::Mapped)
-                         ? &MappedArgumentsObject::class_
-                         : &UnmappedArgumentsObject::class_;
-
-    Register scratchReg = R1.scratchReg();
-
-    // Guard on input being an arguments object.
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure);
-
-    // Get initial length value.
-    masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg);
-
-    // Test if length has been overridden.
+    // Ensure that frame has not loaded different arguments object since.
     masm.branchTest32(Assembler::NonZero,
-                      scratchReg,
-                      Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
+                      Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
+                      Imm32(BaselineFrame::HAS_ARGS_OBJ),
                       &failure);
 
-    // Nope, shift out arguments length and return it.
-    // No need to type monitor because this stub always returns Int32.
-    masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg);
-    masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+    Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
+    masm.loadPtr(actualArgs, R0.scratchReg());
+    masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
     EmitReturnFromIC(masm);
 
     masm.bind(&failure);
@@ -4080,41 +3899,6 @@ ICGetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm)
     return true;
 }
 
-bool
-ICGetProp_ModuleNamespace::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
-
-    Register scratch = regs.takeAnyExcluding(ICTailCallReg);
-
-    // Guard on namespace object.
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-    Register object = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(ICStubReg, ICGetProp_ModuleNamespace::offsetOfNamespace()), scratch);
-    masm.branchPtr(Assembler::NotEqual, object, scratch, &failure);
-
-    // Determine base pointer for load.
-    Register loadBase = regs.takeAnyExcluding(ICTailCallReg);
-    masm.loadPtr(Address(ICStubReg, ICGetProp_ModuleNamespace::offsetOfEnvironment()), loadBase);
-    if (!isFixedSlot_)
-        masm.loadPtr(Address(loadBase, NativeObject::offsetOfSlots()), loadBase);
-
-    // Load the property.
-    masm.load32(Address(ICStubReg, ICGetProp_ModuleNamespace::offsetOfOffset()), scratch);
-    masm.loadValue(BaseIndex(loadBase, scratch, TimesOne), R0);
-
-    // Enter type monitor IC to type-check result.
-    EmitEnterTypeMonitorIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-
-    return true;
-}
-
 void
 BaselineScript::noteAccessedGetter(uint32_t pcOffset)
 {
diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h
index e6fca65a34..7be779f002 100644
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -2373,54 +2373,6 @@ class ICGetProp_Generic : public ICMonitoredStub
     };
 };
 
-// Stub for accessing a dense array's length.
-class ICGetProp_ArrayLength : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICGetProp_ArrayLength(JitCode* stubCode)
-      : ICStub(GetProp_ArrayLength, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICStubCompiler {
-        bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        explicit Compiler(JSContext* cx, Engine engine)
-          : ICStubCompiler(cx, ICStub::GetProp_ArrayLength, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            return newStub(space, getStubCode());
-        }
-    };
-};
-
-// Stub for accessing an unboxed array's length.
-class ICGetProp_UnboxedArrayLength : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICGetProp_UnboxedArrayLength(JitCode* stubCode)
-      : ICStub(GetProp_UnboxedArrayLength, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICStubCompiler {
-        bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        explicit Compiler(JSContext* cx, Engine engine)
-          : ICStubCompiler(cx, ICStub::GetProp_UnboxedArrayLength, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            return newStub(space, getStubCode());
-        }
-    };
-};
-
 // Stub for accessing a property on a primitive's prototype.
 class ICGetProp_Primitive : public ICMonitoredStub
 {
@@ -2772,73 +2724,6 @@ class ICGetProp_TypedObject : public ICMonitoredStub
     };
 };
 
-class ICGetProp_ModuleNamespace : public ICMonitoredStub
-{
-    friend class ICStubSpace;
-
-    HeapPtrModuleNamespaceObject namespace_;
-    HeapPtrModuleEnvironmentObject environment_;
-    uint32_t offset_;
-
-    ICGetProp_ModuleNamespace(JitCode* stubCode, ICStub* firstMonitorStub,
-                              HandleModuleNamespaceObject ns, HandleModuleEnvironmentObject env,
-                              uint32_t offset)
-      : ICMonitoredStub(ICStub::GetProp_ModuleNamespace, stubCode, firstMonitorStub),
-        namespace_(ns), environment_(env), offset_(offset)
-    {
-        (void) offset_; // Silence clang warning.
-    }
-
-  public:
-    HeapPtrModuleNamespaceObject& getNamespace() {
-        return namespace_;
-    }
-    HeapPtrModuleEnvironmentObject& environment() {
-        return environment_;
-    }
-
-    static size_t offsetOfNamespace() {
-        return offsetof(ICGetProp_ModuleNamespace, namespace_);
-    }
-    static size_t offsetOfEnvironment() {
-        return offsetof(ICGetProp_ModuleNamespace, environment_);
-    }
-    static size_t offsetOfOffset() {
-        return offsetof(ICGetProp_ModuleNamespace, offset_);
-    }
-
-    class Compiler : public ICStubCompiler {
-      protected:
-        ICStub* firstMonitorStub_;
-        RootedModuleNamespaceObject namespace_;
-        RootedModuleEnvironmentObject environment_;
-        bool isFixedSlot_;
-        uint32_t offset_;
-
-        bool generateStubCode(MacroAssembler& masm);
-
-        virtual int32_t getKey() const {
-            return static_cast(engine_) |
-                  (static_cast(kind) << 1) |
-                  (static_cast(isFixedSlot_) << 17);
-        }
-
-      public:
-        Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub,
-                 HandleModuleNamespaceObject ns, HandleModuleEnvironmentObject env, bool isFixedSlot,
-                 uint32_t offset)
-          : ICStubCompiler(cx, ICStub::GetProp_ModuleNamespace, engine),
-            firstMonitorStub_(firstMonitorStub),
-            namespace_(cx, ns), environment_(cx, env), isFixedSlot_(isFixedSlot), offset_(offset)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            return newStub(space, getStubCode(), firstMonitorStub_,
-                                                      namespace_, environment_, offset_);
-        }
-    };
-};
-
 class ICGetPropCallGetter : public ICMonitoredStub
 {
     friend class ICStubSpace;
@@ -3228,7 +3113,7 @@ class ICGetProp_ArgumentsLength : public ICStub
 {
   friend class ICStubSpace;
   public:
-    enum Which { Mapped, Unmapped, Magic };
+    enum Which { Magic };
 
   protected:
     explicit ICGetProp_ArgumentsLength(JitCode* stubCode)
diff --git a/js/src/jit/SharedICList.h b/js/src/jit/SharedICList.h
index 19cc1a6322..1a67dfba5e 100644
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -35,8 +35,6 @@ namespace jit {
     _(Compare_Int32WithBoolean)                  \
                                                  \
     _(GetProp_Fallback)                          \
-    _(GetProp_ArrayLength)                       \
-    _(GetProp_UnboxedArrayLength)                \
     _(GetProp_Primitive)                         \
     _(GetProp_StringLength)                      \
     _(GetProp_Unboxed)                           \
@@ -49,7 +47,6 @@ namespace jit {
     _(GetProp_DOMProxyShadowed)                  \
     _(GetProp_ArgumentsLength)                   \
     _(GetProp_ArgumentsCallee)                   \
-    _(GetProp_ModuleNamespace)                   \
     _(GetProp_Generic)                           \
                                                  \
     _(CacheIR_Monitored)                         \
diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp
index 8e2c53effb..8188cfaf93 100644
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -87,8 +87,10 @@ CodeGeneratorARM::visitCompare(LCompare* comp)
 
     if (right->isConstant())
         masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)));
+    else if (right->isRegister())
+        masm.ma_cmp(ToRegister(left), ToRegister(right));
     else
-        masm.ma_cmp(ToRegister(left), ToOperand(right));
+        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)));
     masm.ma_mov(Imm32(0), ToRegister(def));
     masm.ma_mov(Imm32(1), ToRegister(def), cond);
 }
@@ -97,10 +99,15 @@ void
 CodeGeneratorARM::visitCompareAndBranch(LCompareAndBranch* comp)
 {
     Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop());
-    if (comp->right()->isConstant())
-        masm.ma_cmp(ToRegister(comp->left()), Imm32(ToInt32(comp->right())));
+    const LAllocation* left = comp->left();
+    const LAllocation* right = comp->right();
+
+    if (right->isConstant())
+        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)));
+    else if (right->isRegister())
+        masm.ma_cmp(ToRegister(left), ToRegister(right));
     else
-        masm.ma_cmp(ToRegister(comp->left()), ToOperand(comp->right()));
+        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)));
     emitBranch(cond, comp->ifTrue(), comp->ifFalse());
 }
 
@@ -338,8 +345,10 @@ CodeGeneratorARM::visitAddI(LAddI* ins)
 
     if (rhs->isConstant())
         masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC);
+    else if (rhs->isRegister())
+        masm.ma_add(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), SetCC);
     else
-        masm.ma_add(ToRegister(lhs), ToOperand(rhs), ToRegister(dest), SetCC);
+        masm.ma_add(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC);
 
     if (ins->snapshot())
         bailoutIf(Assembler::Overflow, ins->snapshot());
@@ -354,8 +363,10 @@ CodeGeneratorARM::visitSubI(LSubI* ins)
 
     if (rhs->isConstant())
         masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC);
+    else if (rhs->isRegister())
+        masm.ma_sub(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), SetCC);
     else
-        masm.ma_sub(ToRegister(lhs), ToOperand(rhs), ToRegister(dest), SetCC);
+        masm.ma_sub(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC);
 
     if (ins->snapshot())
         bailoutIf(Assembler::Overflow, ins->snapshot());
@@ -454,7 +465,6 @@ CodeGeneratorARM::visitMulI(LMulI* ins)
     } else {
         Assembler::Condition c = Assembler::Overflow;
 
-        // masm.imull(ToOperand(rhs), ToRegister(lhs));
         if (mul->canOverflow())
             c = masm.ma_check_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), c);
         else
diff --git a/js/src/jit/arm/MacroAssembler-arm-inl.h b/js/src/jit/arm/MacroAssembler-arm-inl.h
index 1203c3be62..9013029b65 100644
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -481,32 +481,6 @@ MacroAssembler::branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label*
     branch32(cond, scratch2, rhs, label);
 }
 
-
-void
-MacroAssembler::branch32(Condition cond, const Operand& lhs, Register rhs, Label* label)
-{
-    if (lhs.getTag() == Operand::OP2) {
-        branch32(cond, lhs.toReg(), rhs, label);
-    } else {
-        ScratchRegisterScope scratch(*this);
-        ma_ldr(lhs.toAddress(), scratch);
-        branch32(cond, scratch, rhs, label);
-    }
-}
-
-void
-MacroAssembler::branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label)
-{
-    if (lhs.getTag() == Operand::OP2) {
-        branch32(cond, lhs.toReg(), rhs, label);
-    } else {
-        // branch32 will use ScratchRegister.
-        AutoRegisterScope scratch(*this, secondScratchReg_);
-        ma_ldr(lhs.toAddress(), scratch);
-        branch32(cond, scratch, rhs, label);
-    }
-}
-
 void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
 {
diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp
index 458eae66a4..22eda930ff 100644
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2362,19 +2362,6 @@ MacroAssemblerARMCompat::cmp32(Register lhs, Imm32 rhs)
     ma_cmp(lhs, rhs);
 }
 
-void
-MacroAssemblerARMCompat::cmp32(const Operand& lhs, Register rhs)
-{
-    ma_cmp(lhs.toReg(), rhs);
-}
-
-void
-MacroAssemblerARMCompat::cmp32(const Operand& lhs, Imm32 rhs)
-{
-    MOZ_ASSERT(lhs.toReg() != ScratchRegister);
-    ma_cmp(lhs.toReg(), rhs);
-}
-
 void
 MacroAssemblerARMCompat::cmp32(Register lhs, Register rhs)
 {
diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h
index 8b0ec13965..bd68990b87 100644
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1315,8 +1315,12 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
 
     void cmp32(Register lhs, Imm32 rhs);
     void cmp32(Register lhs, Register rhs);
-    void cmp32(const Operand& lhs, Imm32 rhs);
-    void cmp32(const Operand& lhs, Register rhs);
+    void cmp32(const Address& lhs, Imm32 rhs) {
+        MOZ_CRASH("NYI");
+    }
+    void cmp32(const Address& lhs, Register rhs) {
+        MOZ_CRASH("NYI");
+    }
 
     void cmpPtr(Register lhs, Register rhs);
     void cmpPtr(Register lhs, ImmWord rhs);
diff --git a/js/src/jit/arm64/CodeGenerator-arm64.h b/js/src/jit/arm64/CodeGenerator-arm64.h
index b5d03a623a..5200d72da5 100644
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -28,18 +28,6 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
   protected:
     NonAssertingLabel deoptLabel_;
 
-    // FIXME: VIXL Operand does not match the platform-agnostic Operand,
-    // which is just a union of possible arguments.
-    inline Operand ToOperand(const LAllocation& a) {
-        MOZ_CRASH("ToOperand");
-    }
-    inline Operand ToOperand(const LAllocation* a) {
-        return ToOperand(*a);
-    }
-    inline Operand ToOperand(const LDefinition* def) {
-        return ToOperand(def->output());
-    }
-
     MoveOperand toMoveOperand(const LAllocation a) const;
 
     void bailoutIf(Assembler::Condition condition, LSnapshot* snapshot);
diff --git a/js/src/jit/arm64/MacroAssembler-arm64-inl.h b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
index 2ccc1d4fda..47b8840a2f 100644
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -534,23 +534,6 @@ MacroAssembler::branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label*
     branch32(cond, scratch32.asUnsized(), rhs, label);
 }
 
-
-void
-MacroAssembler::branch32(Condition cond, const Operand& lhs, Register rhs, Label* label)
-{
-    // since rhs is an operand, do the compare backwards
-    Cmp(ARMRegister(rhs, 32), lhs);
-    B(label, Assembler::InvertCmpCondition(cond));
-}
-
-void
-MacroAssembler::branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label)
-{
-    ARMRegister l = lhs.reg();
-    Cmp(l, Operand(rhs.value));
-    B(label, cond);
-}
-
 void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
 {
diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h
index 7637257abe..005f076acf 100644
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -1031,6 +1031,12 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
     void cmp32(Register a, Register b) {
         Cmp(ARMRegister(a, 32), Operand(ARMRegister(b, 32)));
     }
+    void cmp32(const Address& lhs, Imm32 rhs) {
+        cmp32(Operand(lhs.base, lhs.offset), rhs);
+    }
+    void cmp32(const Address& lhs, Register rhs) {
+        cmp32(Operand(lhs.base, lhs.offset), rhs);
+    }
     void cmp32(const Operand& lhs, Imm32 rhs) {
         vixl::UseScratchRegisterScope temps(this);
         const ARMRegister scratch32 = temps.AcquireW();
diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.cpp b/js/src/jit/mips-shared/Assembler-mips-shared.cpp
index 12460e1962..9258c3b63c 100644
--- a/js/src/jit/mips-shared/Assembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.cpp
@@ -1303,6 +1303,36 @@ AssemblerMIPSShared::as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft
     return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ule_fmt).encode());
 }
 
+// FP conditional move.
+BufferOffset
+AssemblerMIPSShared::as_movt(FloatFormat fmt, FloatRegister fd, FloatRegister fs, FPConditionBit fcc)
+{
+    RSField rs = fmt == DoubleFloat ? rs_d : rs_s;
+    Register rt = Register::FromCode(fcc << 2 | 1);
+    return writeInst(InstReg(op_cop1, rs, rt, fs, fd, ff_movf_fmt).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_movf(FloatFormat fmt, FloatRegister fd, FloatRegister fs, FPConditionBit fcc)
+{
+    RSField rs = fmt == DoubleFloat ? rs_d : rs_s;
+    Register rt = Register::FromCode(fcc << 2 | 0);
+    return writeInst(InstReg(op_cop1, rs, rt, fs, fd, ff_movf_fmt).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_movz(FloatFormat fmt, FloatRegister fd, FloatRegister fs, Register rt)
+{
+    RSField rs = fmt == DoubleFloat ? rs_d : rs_s;
+    return writeInst(InstReg(op_cop1, rs, rt, fs, fd, ff_movz_fmt).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_movn(FloatFormat fmt, FloatRegister fd, FloatRegister fs, Register rt)
+{
+    RSField rs = fmt == DoubleFloat ? rs_d : rs_s;
+    return writeInst(InstReg(op_cop1, rs, rt, fs, fd, ff_movn_fmt).encode());
+}
 
 void
 AssemblerMIPSShared::bind(Label* label, BufferOffset boff)
diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.h b/js/src/jit/mips-shared/Assembler-mips-shared.h
index 7ab5a52ee5..92fd02df95 100644
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -425,6 +425,10 @@ enum FunctionField {
     ff_ceil_w_fmt  = 14,
     ff_floor_w_fmt = 15,
 
+    ff_movf_fmt    = 17,
+    ff_movz_fmt    = 18,
+    ff_movn_fmt    = 19,
+
     ff_cvt_s_fmt   = 32,
     ff_cvt_d_fmt   = 33,
     ff_cvt_w_fmt   = 36,
@@ -1062,6 +1066,14 @@ class AssemblerMIPSShared : public AssemblerShared
     BufferOffset as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                          FPConditionBit fcc = FCC0);
 
+    // FP conditional move.
+    BufferOffset as_movt(FloatFormat fmt, FloatRegister fd, FloatRegister fs,
+                         FPConditionBit fcc = FCC0);
+    BufferOffset as_movf(FloatFormat fmt, FloatRegister fd, FloatRegister fs,
+                         FPConditionBit fcc = FCC0);
+    BufferOffset as_movz(FloatFormat fmt, FloatRegister fd, FloatRegister fs, Register rt);
+    BufferOffset as_movn(FloatFormat fmt, FloatRegister fd, FloatRegister fs, Register rt);
+
     // label operations
     void bind(Label* label, BufferOffset boff = BufferOffset());
     void bindLater(Label* label, wasm::JumpTarget target);
diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
index dbc9840479..1192993452 100644
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -34,24 +34,6 @@ using mozilla::NegativeInfinity;
 using JS::GenericNaN;
 using JS::ToInt32;
 
-// inline
-Address
-CodeGeneratorMIPSShared::ToAddress(const LAllocation& a)
-{
-    MOZ_ASSERT(a.isMemory());
-    int32_t offset = ToStackOffset(&a);
-
-    return Address(StackPointer, offset);
-}
-
-// inline
-Address
-CodeGeneratorMIPSShared::ToAddress(const LAllocation* a)
-{
-    return ToAddress(*a);
-}
-
-
 // shared
 CodeGeneratorMIPSShared::CodeGeneratorMIPSShared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
   : CodeGeneratorShared(gen, graph, masm)
@@ -918,6 +900,42 @@ CodeGeneratorMIPSShared::visitClzI(LClzI* ins)
     masm.as_clz(output, input);
 }
 
+void
+CodeGeneratorMIPSShared::visitCtzI(LCtzI* ins)
+{
+    Register input = ToRegister(ins->input());
+    Register output = ToRegister(ins->output());
+
+    masm.ma_ctz(output, input);
+}
+
+void
+CodeGeneratorMIPSShared::visitPopcntI(LPopcntI* ins)
+{
+    Register input = ToRegister(ins->input());
+    Register output = ToRegister(ins->output());
+
+    // Equivalent to GCC output of mozilla::CountPopulation32()
+    Register tmp = ToRegister(ins->temp());
+
+    masm.ma_move(output, input);
+    masm.ma_sra(tmp, input, Imm32(1));
+    masm.ma_and(tmp, Imm32(0x55555555));
+    masm.ma_subu(output, tmp);
+    masm.ma_sra(tmp, output, Imm32(2));
+    masm.ma_and(output, Imm32(0x33333333));
+    masm.ma_and(tmp, Imm32(0x33333333));
+    masm.ma_addu(output, tmp);
+    masm.ma_srl(tmp, output, Imm32(4));
+    masm.ma_addu(output, tmp);
+    masm.ma_and(output, Imm32(0xF0F0F0F));
+    masm.ma_sll(tmp, output, Imm32(8));
+    masm.ma_addu(output, tmp);
+    masm.ma_sll(tmp, output, Imm32(16));
+    masm.ma_addu(output, tmp);
+    masm.ma_sra(output, output, Imm32(24));
+}
+
 void
 CodeGeneratorMIPSShared::visitPowHalfD(LPowHalfD* ins)
 {
@@ -1904,6 +1922,46 @@ CodeGeneratorMIPSShared::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins)
     }
 }
 
+void
+CodeGeneratorMIPSShared::visitAsmSelect(LAsmSelect* ins)
+{
+    MIRType mirType = ins->mir()->type();
+
+    Register cond = ToRegister(ins->condExpr());
+    const LAllocation* falseExpr = ins->falseExpr();
+
+    if (mirType == MIRType_Int32) {
+        Register out = ToRegister(ins->output());
+        MOZ_ASSERT(ToRegister(ins->trueExpr()) == out, "true expr input is reused for output");
+        masm.as_movz(out, ToRegister(falseExpr), cond);
+        return;
+    }
+
+    FloatRegister out = ToFloatRegister(ins->output());
+    MOZ_ASSERT(ToFloatRegister(ins->trueExpr()) == out, "true expr input is reused for output");
+
+    if (falseExpr->isFloatReg()) {
+        if (mirType == MIRType_Float32)
+            masm.as_movz(Assembler::SingleFloat, out, ToFloatRegister(falseExpr), cond);
+        else if (mirType == MIRType_Double)
+            masm.as_movz(Assembler::DoubleFloat, out, ToFloatRegister(falseExpr), cond);
+        else
+            MOZ_CRASH("unhandled type in visitAsmSelect!");
+    } else {
+        Label done;
+        masm.ma_b(cond, cond, &done, Assembler::NonZero, ShortJump);
+
+        if (mirType == MIRType_Float32)
+            masm.loadFloat32(ToAddress(falseExpr), out);
+        else if (mirType == MIRType_Double)
+            masm.loadDouble(ToAddress(falseExpr), out);
+        else
+            MOZ_CRASH("unhandled type in visitAsmSelect!");
+
+        masm.bind(&done);
+    }
+}
+
 void
 CodeGeneratorMIPSShared::visitUDivOrMod(LUDivOrMod* ins)
 {
diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
index b8ad6fb434..8b8d84838d 100644
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
@@ -26,9 +26,6 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
   protected:
     NonAssertingLabel deoptLabel_;
 
-    inline Address ToAddress(const LAllocation& a);
-    inline Address ToAddress(const LAllocation* a);
-
     MoveOperand toMoveOperand(LAllocation a) const;
 
     template 
@@ -38,15 +35,6 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
         bailoutFrom(&bail, snapshot);
     }
     template
-    void bailoutCmp32(Assembler::Condition c, Operand lhs, T rhs, LSnapshot* snapshot) {
-        if (lhs.getTag() == Operand::REG)
-            bailoutCmp32(c, lhs.toReg(), rhs, snapshot);
-        else if (lhs.getTag() == Operand::MEM)
-            bailoutCmp32(c, lhs.toAddress(), rhs, snapshot);
-        else
-            MOZ_CRASH("Invalid operand tag.");
-    }
-    template
     void bailoutTest32(Assembler::Condition c, Register lhs, T rhs, LSnapshot* snapshot) {
         Label bail;
         masm.branchTest32(c, lhs, rhs, &bail);
@@ -146,6 +134,8 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
     virtual void visitUrshD(LUrshD* ins);
 
     virtual void visitClzI(LClzI* ins);
+    virtual void visitCtzI(LCtzI* ins);
+    virtual void visitPopcntI(LPopcntI* ins);
 
     virtual void visitTestIAndBranch(LTestIAndBranch* test);
     virtual void visitCompare(LCompare* comp);
@@ -209,6 +199,7 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
 
     void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
+    void visitAsmSelect(LAsmSelect* ins);
 
     void visitMemoryBarrier(LMemoryBarrier* ins);
     void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir);
diff --git a/js/src/jit/mips-shared/Lowering-mips-shared.cpp b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
index 81bafb5a3a..30f4431ada 100644
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -273,6 +273,27 @@ LIRGeneratorMIPSShared::visitAsmJSNeg(MAsmJSNeg* ins)
     }
 }
 
+void
+LIRGeneratorMIPSShared::visitAsmSelect(MAsmSelect* ins)
+{
+    if (ins->type() == MIRType_Int64) {
+        auto* lir = new(alloc()) LAsmSelectI64(useInt64RegisterAtStart(ins->trueExpr()),
+                                               useInt64(ins->falseExpr()),
+                                               useRegister(ins->condExpr())
+                                              );
+
+        defineInt64ReuseInput(lir, ins, LAsmSelectI64::TrueExprIndex);
+        return;
+    }
+
+    auto* lir = new(alloc()) LAsmSelect(useRegisterAtStart(ins->trueExpr()),
+                                        use(ins->falseExpr()),
+                                        useRegister(ins->condExpr())
+                                       );
+
+    defineReuseInput(lir, ins, LAsmSelect::TrueExprIndex);
+}
+
 void
 LIRGeneratorMIPSShared::lowerUDiv(MDiv* div)
 {
diff --git a/js/src/jit/mips-shared/Lowering-mips-shared.h b/js/src/jit/mips-shared/Lowering-mips-shared.h
index f7ada64f60..b0445a06e2 100644
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -70,6 +70,7 @@ class LIRGeneratorMIPSShared : public LIRGeneratorShared
     void lowerUMod(MMod* mod);
     void visitPowHalf(MPowHalf* ins);
     void visitAsmJSNeg(MAsmJSNeg* ins);
+    void visitAsmSelect(MAsmSelect* ins);
 
     LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                   MTableSwitch* ins);
diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
index d67901e5af..2ce400ffca 100644
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
@@ -242,24 +242,6 @@ MacroAssembler::branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label*
     ma_b(SecondScratchReg, rhs, label, cond);
 }
 
-void
-MacroAssembler::branch32(Condition cond, const Operand& lhs, Register rhs, Label* label)
-{
-    if (lhs.getTag() == Operand::REG)
-        ma_b(lhs.toReg(), rhs, label, cond);
-    else
-        branch32(cond, lhs.toAddress(), rhs, label);
-}
-
-void
-MacroAssembler::branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label)
-{
-    if (lhs.getTag() == Operand::REG)
-        ma_b(lhs.toReg(), rhs, label, cond);
-    else
-        branch32(cond, lhs.toAddress(), rhs, label);
-}
-
 void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress addr, Imm32 imm, Label* label)
 {
diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
index 48336e79f9..ff64f82118 100644
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -182,6 +182,17 @@ MacroAssemblerMIPSShared::ma_xor(Register rd, Register rs, Imm32 imm)
     }
 }
 
+void
+MacroAssemblerMIPSShared::ma_ctz(Register rd, Register rs)
+{
+    ma_negu(ScratchRegister, rs);
+    as_and(rd, ScratchRegister, rs);
+    as_clz(rd, rd);
+    ma_negu(SecondScratchReg, rd);
+    ma_addu(SecondScratchReg, Imm32(0x1f));
+    as_movn(rd, SecondScratchReg, ScratchRegister);
+}
+
 // Arithmetic-based ops.
 
 // Add.
diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared.h b/js/src/jit/mips-shared/MacroAssembler-mips-shared.h
index c763391044..4e7597e14d 100644
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.h
@@ -95,6 +95,8 @@ class MacroAssemblerMIPSShared : public Assembler
     void ma_xor(Register rd, Imm32 imm);
     void ma_xor(Register rd, Register rs, Imm32 imm);
 
+    void ma_ctz(Register rd, Register rs);
+
     // load
     void ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord,
                  LoadStoreExtension extension = SignExtend);
diff --git a/js/src/jit/mips64/CodeGenerator-mips64.cpp b/js/src/jit/mips64/CodeGenerator-mips64.cpp
index 88847a6e12..02806024e9 100644
--- a/js/src/jit/mips64/CodeGenerator-mips64.cpp
+++ b/js/src/jit/mips64/CodeGenerator-mips64.cpp
@@ -177,23 +177,48 @@ CodeGeneratorMIPS64::visitUnbox(LUnbox* unbox)
                      unbox->snapshot());
     }
 
-    Operand input = ToOperand(unbox->getOperand(LUnbox::Input));
+    LAllocation* input = unbox->getOperand(LUnbox::Input);
     Register result = ToRegister(unbox->output());
+    if (input->isRegister()) {
+        Register inputReg = ToRegister(input);
+        switch (mir->type()) {
+          case MIRType_Int32:
+            masm.unboxInt32(inputReg, result);
+            break;
+          case MIRType_Boolean:
+            masm.unboxBoolean(inputReg, result);
+            break;
+          case MIRType_Object:
+            masm.unboxObject(inputReg, result);
+            break;
+          case MIRType_String:
+            masm.unboxString(inputReg, result);
+            break;
+          case MIRType_Symbol:
+            masm.unboxSymbol(inputReg, result);
+            break;
+          default:
+            MOZ_CRASH("Given MIRType cannot be unboxed.");
+        }
+        return;
+    }
+
+    Address inputAddr = ToAddress(input);
     switch (mir->type()) {
       case MIRType_Int32:
-        masm.unboxInt32(input, result);
+        masm.unboxInt32(inputAddr, result);
         break;
       case MIRType_Boolean:
-        masm.unboxBoolean(input, result);
+        masm.unboxBoolean(inputAddr, result);
         break;
       case MIRType_Object:
-        masm.unboxObject(input, result);
+        masm.unboxObject(inputAddr, result);
         break;
       case MIRType_String:
-        masm.unboxString(input, result);
+        masm.unboxString(inputAddr, result);
         break;
       case MIRType_Symbol:
-        masm.unboxSymbol(input, result);
+        masm.unboxSymbol(inputAddr, result);
         break;
       default:
         MOZ_CRASH("Given MIRType cannot be unboxed.");
@@ -278,6 +303,27 @@ CodeGeneratorMIPS64::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
     emitBranch(lhs.valueReg(), rhs.valueReg(), cond, lir->ifTrue(), lir->ifFalse());
 }
 
+void
+CodeGeneratorMIPS64::visitAsmSelectI64(LAsmSelectI64* lir)
+{
+    MOZ_ASSERT(lir->mir()->type() == MIRType_Int64);
+
+    Register cond = ToRegister(lir->condExpr());
+    const LAllocation* falseExpr = lir->falseExpr();
+
+    Register out = ToRegister(lir->output());
+    MOZ_ASSERT(ToRegister(lir->trueExpr()) == out, "true expr is reused for input");
+
+    if (falseExpr->isRegister()) {
+        masm.as_movz(out, ToRegister(falseExpr), cond);
+    } else {
+        Label done;
+        masm.ma_b(cond, cond, &done, Assembler::NonZero, ShortJump);
+        masm.loadPtr(ToAddress(falseExpr), out);
+        masm.bind(&done);
+    }
+}
+
 void
 CodeGeneratorMIPS64::setReturnDoubleRegs(LiveRegisterSet* regs)
 {
diff --git a/js/src/jit/mips64/CodeGenerator-mips64.h b/js/src/jit/mips64/CodeGenerator-mips64.h
index c1ed1e62c5..fdb8db936d 100644
--- a/js/src/jit/mips64/CodeGenerator-mips64.h
+++ b/js/src/jit/mips64/CodeGenerator-mips64.h
@@ -44,6 +44,7 @@ class CodeGeneratorMIPS64 : public CodeGeneratorMIPSShared
     virtual void visitCompareBAndBranch(LCompareBAndBranch* lir);
     virtual void visitCompareBitwise(LCompareBitwise* lir);
     virtual void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
+    virtual void visitAsmSelectI64(LAsmSelectI64* ins);
 
     // Out of line visitors.
     void visitOutOfLineBailout(OutOfLineBailout* ool);
diff --git a/js/src/jit/mips64/MacroAssembler-mips64.cpp b/js/src/jit/mips64/MacroAssembler-mips64.cpp
index b5f70078d0..cdb3a84eee 100644
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -1276,21 +1276,10 @@ MacroAssemblerMIPS64Compat::unboxInt32(const ValueOperand& operand, Register des
 }
 
 void
-MacroAssemblerMIPS64Compat::unboxInt32(const Operand& operand, Register dest)
+MacroAssemblerMIPS64Compat::unboxInt32(Register src, Register dest)
 {
-    switch(operand.getTag()) {
-    case Operand::REG:
-        ma_dsll(dest, operand.toReg(), Imm32(32));
-        ma_dsra(dest, dest, Imm32(32));
-        break;
-    case Operand::MEM:
-        unboxInt32(operand.toAddress(), dest);
-        break;
-    case Operand::FREG:
-    default:
-        MOZ_CRASH("unexpected operand kind");
-        break;
-    }
+    ma_dsll(dest, src, Imm32(32));
+    ma_dsra(dest, dest, Imm32(32));
 }
 
 void
@@ -1313,20 +1302,9 @@ MacroAssemblerMIPS64Compat::unboxBoolean(const ValueOperand& operand, Register d
 }
 
 void
-MacroAssemblerMIPS64Compat::unboxBoolean(const Operand& operand, Register dest)
+MacroAssemblerMIPS64Compat::unboxBoolean(Register src, Register dest)
 {
-    switch(operand.getTag()) {
-    case Operand::REG:
-        ma_dext(dest, operand.toReg(), Imm32(0), Imm32(32));
-        break;
-    case Operand::MEM:
-        unboxBoolean(operand.toAddress(), dest);
-        break;
-    case Operand::FREG:
-    default:
-        MOZ_CRASH("unexpected operand kind");
-        break;
-    }
+    ma_dext(dest, src, Imm32(0), Imm32(32));
 }
 
 void
@@ -1361,20 +1339,9 @@ MacroAssemblerMIPS64Compat::unboxString(const ValueOperand& operand, Register de
 }
 
 void
-MacroAssemblerMIPS64Compat::unboxString(const Operand& operand, Register dest)
+MacroAssemblerMIPS64Compat::unboxString(Register src, Register dest)
 {
-    switch(operand.getTag()) {
-    case Operand::REG:
-        ma_dext(dest, operand.toReg(), Imm32(0), Imm32(JSVAL_TAG_SHIFT));
-        break;
-    case Operand::MEM:
-        unboxNonDouble(operand.toAddress(), dest);
-        break;
-    case Operand::FREG:
-    default:
-        MOZ_CRASH("unexpected operand kind");
-        break;
-    }
+    ma_dext(dest, src, Imm32(0), Imm32(JSVAL_TAG_SHIFT));
 }
 
 void
@@ -1384,20 +1351,9 @@ MacroAssemblerMIPS64Compat::unboxString(const Address& src, Register dest)
 }
 
 void
-MacroAssemblerMIPS64Compat::unboxSymbol(const Operand& operand, Register dest)
+MacroAssemblerMIPS64Compat::unboxSymbol(Register src, Register dest)
 {
-    switch(operand.getTag()) {
-    case Operand::REG:
-        ma_dext(dest, operand.toReg(), Imm32(0), Imm32(JSVAL_TAG_SHIFT));
-        break;
-    case Operand::MEM:
-        unboxNonDouble(operand.toAddress(), dest);
-        break;
-    case Operand::FREG:
-    default:
-        MOZ_CRASH("unexpected operand kind");
-        break;
-    }
+    ma_dext(dest, src, Imm32(0), Imm32(JSVAL_TAG_SHIFT));
 }
 
 void
@@ -1413,20 +1369,9 @@ MacroAssemblerMIPS64Compat::unboxObject(const ValueOperand& src, Register dest)
 }
 
 void
-MacroAssemblerMIPS64Compat::unboxObject(const Operand& src, Register dest)
+MacroAssemblerMIPS64Compat::unboxObject(Register src, Register dest)
 {
-    switch(src.getTag()) {
-    case Operand::REG:
-        ma_dext(dest, src.toReg(), Imm32(0), Imm32(JSVAL_TAG_SHIFT));
-        break;
-    case Operand::MEM:
-        unboxNonDouble(src.toAddress(), dest);
-        break;
-    case Operand::FREG:
-    default:
-        MOZ_CRASH("unexpected operand kind");
-        break;
-    }
+    ma_dext(dest, src, Imm32(0), Imm32(JSVAL_TAG_SHIFT));
 }
 
 void
diff --git a/js/src/jit/mips64/MacroAssembler-mips64.h b/js/src/jit/mips64/MacroAssembler-mips64.h
index 6ac001f4f4..2fc748c401 100644
--- a/js/src/jit/mips64/MacroAssembler-mips64.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64.h
@@ -342,23 +342,24 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64
     void unboxNonDouble(const Address& src, Register dest);
     void unboxNonDouble(const BaseIndex& src, Register dest);
     void unboxInt32(const ValueOperand& operand, Register dest);
-    void unboxInt32(const Operand& operand, Register dest);
+    void unboxInt32(Register src, Register dest);
     void unboxInt32(const Address& src, Register dest);
     void unboxInt32(const BaseIndex& src, Register dest);
     void unboxBoolean(const ValueOperand& operand, Register dest);
-    void unboxBoolean(const Operand& operand, Register dest);
+    void unboxBoolean(Register src, Register dest);
     void unboxBoolean(const Address& src, Register dest);
     void unboxBoolean(const BaseIndex& src, Register dest);
     void unboxDouble(const ValueOperand& operand, FloatRegister dest);
+    void unboxDouble(Register src, Register dest);
     void unboxDouble(const Address& src, FloatRegister dest);
     void unboxString(const ValueOperand& operand, Register dest);
-    void unboxString(const Operand& operand, Register dest);
+    void unboxString(Register src, Register dest);
     void unboxString(const Address& src, Register dest);
     void unboxSymbol(const ValueOperand& src, Register dest);
-    void unboxSymbol(const Operand& src, Register dest);
+    void unboxSymbol(Register src, Register dest);
     void unboxSymbol(const Address& src, Register dest);
     void unboxObject(const ValueOperand& src, Register dest);
-    void unboxObject(const Operand& src, Register dest);
+    void unboxObject(Register src, Register dest);
     void unboxObject(const Address& src, Register dest);
     void unboxObject(const BaseIndex& src, Register dest) { unboxNonDouble(src, dest); }
     void unboxValue(const ValueOperand& src, AnyRegister dest);
diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h
index f6a320d15f..c3b213f66e 100644
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -557,7 +557,7 @@ class CodeLocationJump
         raw_ = nullptr;
         setUninitialized();
 #ifdef JS_SMALL_BRANCH
-        jumpTableEntry_ = (uint8_t*) 0xdeadab1e;
+        jumpTableEntry_ = (uint8_t*) uintptr_t(0xdeadab1e);
 #endif
     }
     CodeLocationJump(JitCode* code, CodeOffsetJump base) {
diff --git a/js/src/jit/shared/CodeGenerator-shared-inl.h b/js/src/jit/shared/CodeGenerator-shared-inl.h
index 7bdb8b9082..04d7d0b6f4 100644
--- a/js/src/jit/shared/CodeGenerator-shared-inl.h
+++ b/js/src/jit/shared/CodeGenerator-shared-inl.h
@@ -244,26 +244,17 @@ CodeGeneratorShared::ToStackOffset(const LAllocation* a) const
     return ToStackOffset(*a);
 }
 
-Operand
-CodeGeneratorShared::ToOperand(const LAllocation& a)
+Address
+CodeGeneratorShared::ToAddress(const LAllocation& a)
 {
-    if (a.isGeneralReg())
-        return Operand(a.toGeneralReg()->reg());
-    if (a.isFloatReg())
-        return Operand(a.toFloatReg()->reg());
-    return Operand(masm.getStackPointer(), ToStackOffset(&a));
+    MOZ_ASSERT(a.isMemory());
+    return Address(masm.getStackPointer(), ToStackOffset(&a));
 }
 
-Operand
-CodeGeneratorShared::ToOperand(const LAllocation* a)
+Address
+CodeGeneratorShared::ToAddress(const LAllocation* a)
 {
-    return ToOperand(*a);
-}
-
-Operand
-CodeGeneratorShared::ToOperand(const LDefinition* def)
-{
-    return ToOperand(def->output());
+    return ToAddress(*a);
 }
 
 void
diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp
index 71e83c42d7..6ef85e4d8e 100644
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -624,7 +624,8 @@ CodeGeneratorShared::assignBailoutId(LSnapshot* snapshot)
     unsigned bailoutId = bailouts_.length();
     snapshot->setBailoutId(bailoutId);
     JitSpew(JitSpew_IonSnapshots, "Assigned snapshot bailout id %u", bailoutId);
-    return bailouts_.append(snapshot->snapshotOffset());
+    masm.propagateOOM(bailouts_.append(snapshot->snapshotOffset()));
+    return true;
 }
 
 bool
diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h
index 01e0d4c1bc..c25f55c8bf 100644
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -210,14 +210,13 @@ class CodeGeneratorShared : public LElementVisitor
     inline int32_t ToStackOffset(LAllocation a) const;
     inline int32_t ToStackOffset(const LAllocation* a) const;
 
+    inline Address ToAddress(const LAllocation& a);
+    inline Address ToAddress(const LAllocation* a);
+
     uint32_t frameSize() const {
         return frameClass_ == FrameSizeClass::None() ? frameDepth_ : frameClass_.frameSize();
     }
 
-    inline Operand ToOperand(const LAllocation& a);
-    inline Operand ToOperand(const LAllocation* a);
-    inline Operand ToOperand(const LDefinition* def);
-
   protected:
 #ifdef CHECK_OSIPOINT_REGISTERS
     void resetOsiPointRegs(LSafepoint* safepoint);
diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
index d56bb29adb..3026d42ee7 100644
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -1764,6 +1764,28 @@ CodeGeneratorX86Shared::visitUrshD(LUrshD* ins)
     masm.convertUInt32ToDouble(lhs, out);
 }
 
+Operand
+CodeGeneratorX86Shared::ToOperand(const LAllocation& a)
+{
+    if (a.isGeneralReg())
+        return Operand(a.toGeneralReg()->reg());
+    if (a.isFloatReg())
+        return Operand(a.toFloatReg()->reg());
+    return Operand(masm.getStackPointer(), ToStackOffset(&a));
+}
+
+Operand
+CodeGeneratorX86Shared::ToOperand(const LAllocation* a)
+{
+    return ToOperand(*a);
+}
+
+Operand
+CodeGeneratorX86Shared::ToOperand(const LDefinition* def)
+{
+    return ToOperand(def->output());
+}
+
 MoveOperand
 CodeGeneratorX86Shared::toMoveOperand(LAllocation a) const
 {
diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
index e9188d348c..7f58813fbf 100644
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -116,6 +116,10 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
 
     NonAssertingLabel deoptLabel_;
 
+    Operand ToOperand(const LAllocation& a);
+    Operand ToOperand(const LAllocation* a);
+    Operand ToOperand(const LDefinition* def);
+
     MoveOperand toMoveOperand(LAllocation a) const;
 
     void bailoutIf(Assembler::Condition condition, LSnapshot* snapshot);
diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
index 7a26b48124..f8e65dd188 100644
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
@@ -152,6 +152,12 @@ class MacroAssemblerX86Shared : public Assembler
     void cmp32(Register lhs, Register rhs) {
         cmpl(rhs, lhs);
     }
+    void cmp32(const Address& lhs, Register rhs) {
+        cmp32(Operand(lhs), rhs);
+    }
+    void cmp32(const Address& lhs, Imm32 rhs) {
+        cmp32(Operand(lhs), rhs);
+    }
     void cmp32(const Operand& lhs, Imm32 rhs) {
         cmpl(rhs, lhs);
     }
diff --git a/js/src/js-config.in b/js/src/js-config.in
index fc6df0a28c..9cd41d613d 100644
--- a/js/src/js-config.in
+++ b/js/src/js-config.in
@@ -108,7 +108,7 @@ if test "$echo_libdir" = "yes"; then
 fi
 
 if test "$echo_cflags" = "yes"; then
-    echo "-std=gnu++0x -include $includedir/$JS_LIBRARY_NAME/js/RequiredDefines.h -I$includedir/$JS_LIBRARY_NAME $NSPR_CFLAGS"
+    echo "-std=gnu++11 -include $includedir/$JS_LIBRARY_NAME/js/RequiredDefines.h -I$includedir/$JS_LIBRARY_NAME $NSPR_CFLAGS"
 fi
 
 if test "$echo_libs" = "yes"; then
diff --git a/js/src/jsapi-tests/testAddPropertyPropcache.cpp b/js/src/jsapi-tests/testAddPropertyPropcache.cpp
index a079c8c437..b9e109f9ca 100644
--- a/js/src/jsapi-tests/testAddPropertyPropcache.cpp
+++ b/js/src/jsapi-tests/testAddPropertyPropcache.cpp
@@ -16,10 +16,14 @@ AddProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValu
     return true;
 }
 
+static const JSClassOps AddPropertyClassOps = {
+    AddProperty
+};
+
 static const JSClass AddPropertyClass = {
     "AddPropertyTester",
     0,
-    AddProperty
+    &AddPropertyClassOps
 };
 
 BEGIN_TEST(testAddPropertyHook)
diff --git a/js/src/jsapi-tests/testBug604087.cpp b/js/src/jsapi-tests/testBug604087.cpp
index 27009d678c..42540079af 100644
--- a/js/src/jsapi-tests/testBug604087.cpp
+++ b/js/src/jsapi-tests/testBug604087.cpp
@@ -14,14 +14,14 @@
 
 #include "vm/ProxyObject.h"
 
-const js::Class OuterWrapperClass =
-    PROXY_CLASS_WITH_EXT(
-        "Proxy",
-        0, /* additional class flags */
-        PROXY_MAKE_EXT(
-            false,   /* isWrappedNative */
-            nullptr  /* objectMoved */
-        ));
+static const js::ClassExtension OuterWrapperClassExtension = PROXY_MAKE_EXT(
+    nullptr  /* objectMoved */
+);
+
+const js::Class OuterWrapperClass = PROXY_CLASS_WITH_EXT(
+    "Proxy",
+    0, /* additional class flags */
+    &OuterWrapperClassExtension);
 
 static JSObject*
 wrap(JSContext* cx, JS::HandleObject toWrap, JS::HandleObject target)
diff --git a/js/src/jsapi-tests/testChromeBuffer.cpp b/js/src/jsapi-tests/testChromeBuffer.cpp
index ae729f9366..9585667f8e 100644
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -8,9 +8,7 @@
 
 static TestJSPrincipals system_principals(1);
 
-static const JSClass global_class = {
-    "global",
-    JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps global_classOps = {
     nullptr,
     nullptr,
     nullptr,
@@ -25,6 +23,12 @@ static const JSClass global_class = {
     JS_GlobalObjectTraceHook
 };
 
+static const JSClass global_class = {
+    "global",
+    JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
+    &global_classOps
+};
+
 static JS::PersistentRootedObject trusted_glob;
 static JS::PersistentRootedObject trusted_fun;
 
diff --git a/js/src/jsapi-tests/testClassGetter.cpp b/js/src/jsapi-tests/testClassGetter.cpp
index ef21848a88..64b4ee5bf3 100644
--- a/js/src/jsapi-tests/testClassGetter.cpp
+++ b/js/src/jsapi-tests/testClassGetter.cpp
@@ -21,15 +21,19 @@ static bool test_prop_get( JSContext* cx, JS::HandleObject obj, JS::HandleId id,
 static bool
 PTest(JSContext* cx, unsigned argc, JS::Value* vp);
 
-static const JSClass ptestClass = {
-    "PTest",
-    JSCLASS_HAS_PRIVATE,
+static const JSClassOps ptestClassOps = {
     nullptr, // addProperty
     nullptr, // delProperty
     test_prop_get,
     nullptr // setProperty
 };
 
+static const JSClass ptestClass = {
+    "PTest",
+    JSCLASS_HAS_PRIVATE,
+    &ptestClassOps
+};
+
 static bool
 PTest(JSContext* cx, unsigned argc, JS::Value* vp)
 {
diff --git a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
index 9faf3f4638..e9744a24a2 100644
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -21,14 +21,18 @@ GlobalResolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolv
 
 BEGIN_TEST(testRedefineGlobalEval)
 {
-    static const JSClass cls = {
-        "global", JSCLASS_GLOBAL_FLAGS,
+    static const JSClassOps clsOps = {
         nullptr, nullptr, nullptr, nullptr,
         GlobalEnumerate, GlobalResolve, nullptr, nullptr,
         nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
+    static const JSClass cls = {
+        "global", JSCLASS_GLOBAL_FLAGS,
+        &clsOps
+    };
+
     /* Create the global object. */
     JS::CompartmentOptions options;
     JS::Rooted g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
diff --git a/js/src/jsapi-tests/testLookup.cpp b/js/src/jsapi-tests/testLookup.cpp
index 178d1cf520..22fb16185f 100644
--- a/js/src/jsapi-tests/testLookup.cpp
+++ b/js/src/jsapi-tests/testLookup.cpp
@@ -73,12 +73,16 @@ document_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* res
     return true;
 }
 
-static const JSClass document_class = {
-    "document", 0,
+static const JSClassOps document_classOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, document_resolve, nullptr
 };
 
+static const JSClass document_class = {
+    "document", 0,
+    &document_classOps
+};
+
 BEGIN_TEST(testLookup_bug570195)
 {
     JS::RootedObject obj(cx, JS_NewObject(cx, &document_class));
diff --git a/js/src/jsapi-tests/testNewObject.cpp b/js/src/jsapi-tests/testNewObject.cpp
index 4289313f59..ef39ca67a3 100644
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -98,13 +98,16 @@ BEGIN_TEST(testNewObject_1)
     CHECK(v.isInt32(N - 1));
 
     // With JSClass.construct.
-    static const JSClass cls = {
-        "testNewObject_1",
-        0,
+    static const JSClassOps clsOps = {
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, constructHook
     };
+    static const JSClass cls = {
+        "testNewObject_1",
+        0,
+        &clsOps
+    };
     JS::RootedObject ctor(cx, JS_NewObject(cx, &cls));
     CHECK(ctor);
     JS::RootedValue rt2(cx, JS::ObjectValue(*ctor));
diff --git a/js/src/jsapi-tests/testPersistentRooted.cpp b/js/src/jsapi-tests/testPersistentRooted.cpp
index 4543872b5f..9dbdbd3465 100644
--- a/js/src/jsapi-tests/testPersistentRooted.cpp
+++ b/js/src/jsapi-tests/testPersistentRooted.cpp
@@ -20,21 +20,25 @@ struct BarkWhenTracedClass {
 int BarkWhenTracedClass::finalizeCount;
 int BarkWhenTracedClass::traceCount;
 
+static const JSClassOps BarkWhenTracedClassClassOps = {
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    BarkWhenTracedClass::finalize,
+    nullptr,
+    nullptr,
+    nullptr,
+    BarkWhenTracedClass::trace
+};
+
 const JSClass BarkWhenTracedClass::class_ = {
     "BarkWhenTracedClass",
     0,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    finalize,
-    nullptr,
-    nullptr,
-    nullptr,
-    trace
+    &BarkWhenTracedClassClassOps
 };
 
 struct Kennel {
diff --git a/js/src/jsapi-tests/testPropCache.cpp b/js/src/jsapi-tests/testPropCache.cpp
index 8c802f96b6..e36163ecfd 100644
--- a/js/src/jsapi-tests/testPropCache.cpp
+++ b/js/src/jsapi-tests/testPropCache.cpp
@@ -16,10 +16,14 @@ CounterAdd(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue
     return true;
 }
 
+static const JSClassOps CounterClassOps = {
+    CounterAdd
+};
+
 static const JSClass CounterClass = {
     "Counter",  /* name */
     0,  /* flags */
-    CounterAdd
+    &CounterClassOps
 };
 
 BEGIN_TEST(testPropCache_bug505798)
diff --git a/js/src/jsapi-tests/testResolveRecursion.cpp b/js/src/jsapi-tests/testResolveRecursion.cpp
index 29ddab99cd..5c8ee734aa 100644
--- a/js/src/jsapi-tests/testResolveRecursion.cpp
+++ b/js/src/jsapi-tests/testResolveRecursion.cpp
@@ -13,9 +13,7 @@
  */
 BEGIN_TEST(testResolveRecursion)
 {
-    static const JSClass my_resolve_class = {
-        "MyResolve",
-        JSCLASS_HAS_PRIVATE,
+    static const JSClassOps my_resolve_classOps = {
         nullptr, // add
         nullptr, // delete
         nullptr, // get
@@ -24,6 +22,12 @@ BEGIN_TEST(testResolveRecursion)
         my_resolve
     };
 
+    static const JSClass my_resolve_class = {
+        "MyResolve",
+        JSCLASS_HAS_PRIVATE,
+        &my_resolve_classOps
+    };
+
     obj1.init(cx, JS_NewObject(cx, &my_resolve_class));
     CHECK(obj1);
     obj2.init(cx, JS_NewObject(cx, &my_resolve_class));
@@ -150,9 +154,7 @@ BEGIN_TEST(testResolveRecursion_InitStandardClasses)
 }
 
 const JSClass* getGlobalClass() override {
-    static const JSClass myGlobalClass = {
-        "testResolveRecursion_InitStandardClasses_myGlobalClass",
-        JSCLASS_GLOBAL_FLAGS,
+    static const JSClassOps myGlobalClassOps = {
         nullptr, // add
         nullptr, // delete
         nullptr, // get
@@ -167,6 +169,12 @@ const JSClass* getGlobalClass() override {
         JS_GlobalObjectTraceHook
     };
 
+    static const JSClass myGlobalClass = {
+        "testResolveRecursion_InitStandardClasses_myGlobalClass",
+        JSCLASS_GLOBAL_FLAGS,
+        &myGlobalClassOps
+    };
+
     return &myGlobalClass;
 }
 
diff --git a/js/src/jsapi-tests/testSetProperty.cpp b/js/src/jsapi-tests/testSetProperty.cpp
index a3c65c6b8e..e4ba0174e3 100644
--- a/js/src/jsapi-tests/testSetProperty.cpp
+++ b/js/src/jsapi-tests/testSetProperty.cpp
@@ -68,7 +68,7 @@ BEGIN_TEST(testSetProperty_InheritedGlobalSetter)
     // This is a JSAPI test because jsapi-test globals do not have a resolve
     // hook and therefore can use the property cache in some cases where the
     // shell can't.
-    MOZ_RELEASE_ASSERT(!JS_GetClass(global)->resolve);
+    MOZ_RELEASE_ASSERT(!JS_GetClass(global)->getResolve());
 
     CHECK(JS_DefineProperty(cx, global, "HOTLOOP", 8, 0));
     EXEC("var n = 0;\n"
diff --git a/js/src/jsapi-tests/testWeakMap.cpp b/js/src/jsapi-tests/testWeakMap.cpp
index c12c240235..70145aa358 100644
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -152,26 +152,16 @@ static JSObject* GetKeyDelegate(JSObject* obj)
 
 JSObject* newKey()
 {
+    static const js::ClassExtension keyClassExtension = {
+        GetKeyDelegate
+    };
+
     static const js::Class keyClass = {
-        "keyWithDelgate",
+        "keyWithDelegate",
         JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
-        nullptr, /* addProperty */
-        nullptr, /* delProperty */
-        nullptr, /* getProperty */
-        nullptr, /* setProperty */
-        nullptr, /* enumerate */
-        nullptr, /* resolve */
-        nullptr, /* mayResolve */
-        nullptr, /* finalize */
-        nullptr, /* call */
-        nullptr, /* hasInstance */
-        nullptr, /* construct */
-        nullptr, /* trace */
+        JS_NULL_CLASS_OPS,
         JS_NULL_CLASS_SPEC,
-        {
-            false,
-            GetKeyDelegate
-        },
+        &keyClassExtension,
         JS_NULL_OBJECT_OPS
     };
 
@@ -206,9 +196,7 @@ JSObject* newCCW(JS::HandleObject sourceZone, JS::HandleObject destZone)
 
 JSObject* newDelegate()
 {
-    static const js::Class delegateClass = {
-        "delegate",
-        JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
+    static const js::ClassOps delegateClassOps = {
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
@@ -221,12 +209,19 @@ JSObject* newDelegate()
         nullptr, /* hasInstance */
         nullptr, /* construct */
         JS_GlobalObjectTraceHook,
+    };
+
+    static const js::ClassExtension delegateClassExtension = {
+        nullptr,
+        DelegateObjectMoved
+    };
+
+    static const js::Class delegateClass = {
+        "delegate",
+        JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
+        &delegateClassOps,
         JS_NULL_CLASS_SPEC,
-        {
-            false,
-            nullptr,
-            DelegateObjectMoved
-        },
+        &delegateClassExtension,
         JS_NULL_OBJECT_OPS
     };
 
diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h
index 9a48931504..96fd537293 100644
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -223,13 +223,16 @@ class JSAPITest
     JSAPITestString messages() const { return msgs; }
 
     static const JSClass * basicGlobalClass() {
-        static const JSClass c = {
-            "global", JSCLASS_GLOBAL_FLAGS,
+        static const JSClassOps cOps = {
             nullptr, nullptr, nullptr, nullptr,
             nullptr, nullptr, nullptr, nullptr,
             nullptr, nullptr, nullptr,
             JS_GlobalObjectTraceHook
         };
+        static const JSClass c = {
+            "global", JSCLASS_GLOBAL_FLAGS,
+            &cOps
+        };
         return &c;
     }
 
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 72b856ffec..970a666922 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2208,9 +2208,9 @@ DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue val
     // setProperty ops".
     if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
         if (!getter)
-            getter = obj->getClass()->getProperty;
+            getter = obj->getClass()->getGetProperty();
         if (!setter)
-            setter = obj->getClass()->setProperty;
+            setter = obj->getClass()->getSetProperty();
     }
     if (getter == JS_PropertyStub)
         getter = nullptr;
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 129169f2d8..26be774cdc 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1839,6 +1839,9 @@ typedef enum JSGCParamKey {
 
     /** Whether compacting GC is enabled. */
     JSGC_COMPACTING_ENABLED = 23,
+
+    /** If true, painting can trigger IGC slices. */
+    JSGC_REFRESH_FRAME_SLICES_ENABLED = 24,
 } JSGCParamKey;
 
 extern JS_PUBLIC_API(void)
@@ -6002,6 +6005,13 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject
 extern JS_PUBLIC_API(bool)
 BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp, size_t indent = 0);
 
+/**
+ * Return true iff the given object is either a SavedFrame object or wrapper
+ * around a SavedFrame object, and it is not the SavedFrame.prototype object.
+ */
+extern JS_PUBLIC_API(bool)
+IsSavedFrame(JSObject* obj);
+
 } /* namespace JS */
 
 
diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp
index e45dbdcad6..0d5843aaab 100644
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -498,7 +498,8 @@ array_length_setter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleV
         // we're here, do an impression of SetPropertyByDefining.
         const Class* clasp = obj->getClass();
         return DefineProperty(cx, obj, cx->names().length, vp,
-                              clasp->getProperty, clasp->setProperty, JSPROP_ENUMERATE, result);
+                              clasp->getGetProperty(), clasp->getSetProperty(),
+                              JSPROP_ENUMERATE, result);
     }
 
     Rooted arr(cx, &obj->as());
@@ -933,6 +934,8 @@ ArraySpeciesCreate(JSContext* cx, HandleObject origArray, uint32_t length, Mutab
 {
     RootedId createId(cx, NameToId(cx->names().ArraySpeciesCreate));
     RootedFunction create(cx, JS::GetSelfHostedFunction(cx, "ArraySpeciesCreate", createId, 2));
+    if (!create)
+        return false;
 
     FixedInvokeArgs<2> args(cx);
 
@@ -2694,7 +2697,7 @@ GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint32_t begin, uin
     // properties.
     JSObject* pobj = obj;
     do {
-        if (!pobj->isNative() || pobj->getClass()->resolve || pobj->getOpsLookupProperty())
+        if (!pobj->isNative() || pobj->getClass()->getResolve() || pobj->getOpsLookupProperty())
             return true;
     } while ((pobj = pobj->getProto()));
 
@@ -3286,6 +3289,21 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
     return DefineProperty(cx, proto, id, value, nullptr, nullptr, JSPROP_READONLY);
 }
 
+static const ClassOps ArrayObjectClassOps = {
+    array_addProperty,
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    nullptr, /* finalize */
+    nullptr, /* call */
+    nullptr, /* hasInstance */
+    nullptr, /* construct */
+    nullptr, /* trace */
+};
+
 static const ClassSpec ArrayObjectClassSpec = {
     GenericCreateConstructor,
     CreateArrayPrototype,
@@ -3299,18 +3317,7 @@ static const ClassSpec ArrayObjectClassSpec = {
 const Class ArrayObject::class_ = {
     "Array",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_BUILDER,
-    array_addProperty,
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    &ArrayObjectClassOps,
     &ArrayObjectClassSpec
 };
 
diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp
index 72273528a2..04bbe7f2ba 100644
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -66,6 +66,8 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
     detachedTypedObjects(0),
     objectMetadataState(ImmediateMetadata()),
     propertyTree(thisForCtor()),
+    baseShapes(zone, BaseShapeSet()),
+    initialShapes(zone, InitialShapeSet()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     lazyArrayBuffers(nullptr),
@@ -77,7 +79,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
     debugScriptMap(nullptr),
     debugScopes(nullptr),
     enumerators(nullptr),
-    compartmentStats(nullptr),
+    compartmentStats_(nullptr),
     scheduledForDestruction(false),
     maybeAlive(true),
     jitCompartment_(nullptr),
@@ -630,8 +632,7 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t
     // structures. It uses a HashMap instead of a WeakMap, so that we can keep
     // the data alive for the JSScript::finalize call. Thus, we do not trace the
     // keys of the HashMap to avoid adding a strong reference to the JSScript
-    // pointers. Additionally, we assert that the JSScripts have not been moved
-    // in JSCompartment::fixupAfterMovingGC.
+    // pointers.
     //
     // If the code coverage is either enabled with the --dump-bytecode command
     // line option, or with the PCCount JSFriend API functions, then we mark the
@@ -817,18 +818,8 @@ JSCompartment::fixupAfterMovingGC()
     fixupGlobal();
     fixupInitialShapeTable();
     objectGroups.fixupTablesAfterMovingGC();
-
-#ifdef DEBUG
-    // Assert that none of the JSScript pointers, which are used as key of the
-    // scriptCountsMap HashMap are moved. We do not mark these keys because we
-    // need weak references. We do not use a WeakMap because these entries would
-    // be collected before the JSScript::finalize calls which is used to
-    // summarized the content of the code coverage.
-    if (scriptCountsMap) {
-        for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront())
-            MOZ_ASSERT(!IsForwarded(r.front().key()));
-    }
-#endif
+    dtoaCache.purge();
+    fixupScriptMapsAfterMovingGC();
 }
 
 void
@@ -839,6 +830,59 @@ JSCompartment::fixupGlobal()
         global_.set(MaybeForwarded(global));
 }
 
+void
+JSCompartment::fixupScriptMapsAfterMovingGC()
+{
+    // Map entries are removed by JSScript::finalize, but we need to update the
+    // script pointers here in case they are moved by the GC.
+
+    if (scriptCountsMap) {
+        for (ScriptCountsMap::Enum e(*scriptCountsMap); !e.empty(); e.popFront()) {
+            JSScript* script = e.front().key();
+            if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
+                e.rekeyFront(script);
+        }
+    }
+
+    if (debugScriptMap) {
+        for (DebugScriptMap::Enum e(*debugScriptMap); !e.empty(); e.popFront()) {
+            JSScript* script = e.front().key();
+            if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
+                e.rekeyFront(script);
+        }
+    }
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+void
+JSCompartment::checkScriptMapsAfterMovingGC()
+{
+    if (scriptCountsMap) {
+        for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
+            JSScript* script = r.front().key();
+            CheckGCThingAfterMovingGC(script);
+            auto ptr = scriptCountsMap->lookup(script);
+            MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
+        }
+    }
+
+    if (debugScriptMap) {
+        for (auto r = debugScriptMap->all(); !r.empty(); r.popFront()) {
+            JSScript* script = r.front().key();
+            CheckGCThingAfterMovingGC(script);
+            DebugScript* ds = r.front().value();
+            for (uint32_t i = 0; i < ds->numSites; i++) {
+                BreakpointSite* site = ds->breakpoints[i];
+                if (site)
+                    CheckGCThingAfterMovingGC(site->script);
+            }
+            auto ptr = debugScriptMap->lookup(script);
+            MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
+        }
+    }
+}
+#endif
+
 void
 JSCompartment::purge()
 {
diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h
index 532c8d4c58..69a314704f 100644
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -63,6 +63,10 @@ class DtoaCache {
         this->d = d;
         this->s = s;
     }
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+    void checkCacheAfterMovingGC() { MOZ_ASSERT(!s || !IsForwarded(s)); }
+#endif
 };
 
 struct CrossCompartmentKey
@@ -434,12 +438,10 @@ struct JSCompartment
     js::PropertyTree             propertyTree;
 
     /* Set of all unowned base shapes in the compartment. */
-    js::BaseShapeSet             baseShapes;
-    void sweepBaseShapeTable();
+    JS::WeakCache baseShapes;
 
     /* Set of initial shapes in the compartment. */
-    js::InitialShapeSet          initialShapes;
-    void sweepInitialShapeTable();
+    JS::WeakCache initialShapes;
 
     // Object group tables and other state in the compartment.
     js::ObjectGroupCompartment   objectGroups;
@@ -448,6 +450,7 @@ struct JSCompartment
     void checkInitialShapesTableAfterMovingGC();
     void checkWrapperMapAfterMovingGC();
     void checkBaseShapeTableAfterMovingGC();
+    void checkScriptMapsAfterMovingGC();
 #endif
 
     /*
@@ -600,6 +603,7 @@ struct JSCompartment
     void fixupInitialShapeTable();
     void fixupAfterMovingGC();
     void fixupGlobal();
+    void fixupScriptMapsAfterMovingGC();
 
     bool hasAllocationMetadataBuilder() const { return allocationMetadataBuilder; }
     const js::AllocationMetadataBuilder* getAllocationMetadataBuilder() const {
@@ -752,8 +756,28 @@ struct JSCompartment
      */
     js::NativeIterator* enumerators;
 
+  private:
     /* Used by memory reporters and invalid otherwise. */
-    void*              compartmentStats;
+    JS::CompartmentStats* compartmentStats_;
+
+  public:
+    // This should only be called when it is non-null, i.e. during memory
+    // reporting.
+    JS::CompartmentStats& compartmentStats() {
+        // We use MOZ_RELEASE_ASSERT here because in bug 1132502 there was some
+        // (inconclusive) evidence that compartmentStats_ can be nullptr
+        // unexpectedly.
+        MOZ_RELEASE_ASSERT(compartmentStats_);
+        return *compartmentStats_;
+    }
+    void nullCompartmentStats() {
+        MOZ_ASSERT(compartmentStats_);
+        compartmentStats_ = nullptr;
+    }
+    void setCompartmentStats(JS::CompartmentStats* newStats) {
+        MOZ_ASSERT(!compartmentStats_ && newStats);
+        compartmentStats_ = newStats;
+    }
 
     // These flags help us to discover if a compartment that shouldn't be alive
     // manages to outlive a GC.
diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
index 4b0c910eaa..32af332ffe 100644
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3275,18 +3275,7 @@ const Class DateObject::class_ = {
     js_Date_str,
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    JS_NULL_CLASS_OPS,
     &DateObjectClassSpec
 };
 
@@ -3304,18 +3293,7 @@ static const ClassSpec DateObjectProtoClassSpec = {
 const Class DateObject::protoClass_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace  */
+    JS_NULL_CLASS_OPS,
     &DateObjectProtoClassSpec
 };
 
diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp
index 4d588a1dc3..0de3cdcb04 100644
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -65,23 +65,27 @@ static const JSFunctionSpec exception_methods[] = {
     JS_FS_END
 };
 
+static const ClassOps ErrorObjectClassOps = {
+    nullptr,                 /* addProperty */
+    nullptr,                 /* delProperty */
+    nullptr,                 /* getProperty */
+    nullptr,                 /* setProperty */
+    nullptr,                 /* enumerate */
+    nullptr,                 /* resolve */
+    nullptr,                 /* mayResolve */
+    exn_finalize,
+    nullptr,                 /* call        */
+    nullptr,                 /* hasInstance */
+    nullptr,                 /* construct   */
+    nullptr,                 /* trace       */
+};
+
 #define IMPLEMENT_ERROR_CLASS(name, classSpecPtr) \
     { \
         js_Error_str, /* yes, really */ \
         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), \
-        nullptr,                 /* addProperty */ \
-        nullptr,                 /* delProperty */ \
-        nullptr,                 /* getProperty */ \
-        nullptr,                 /* setProperty */ \
-        nullptr,                 /* enumerate */ \
-        nullptr,                 /* resolve */ \
-        nullptr,                 /* mayResolve */ \
-        exn_finalize, \
-        nullptr,                 /* call        */ \
-        nullptr,                 /* hasInstance */ \
-        nullptr,                 /* construct   */ \
-        nullptr,                 /* trace       */ \
+        &ErrorObjectClassOps, \
         classSpecPtr \
     }
 
diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp
index 824c191c7e..6e1aae13b7 100644
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -603,7 +603,7 @@ js::VisitGrayWrapperTargets(Zone* zone, GCThingCallback callback, void* closure)
 JS_FRIEND_API(JSObject*)
 js::GetWeakmapKeyDelegate(JSObject* key)
 {
-    if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp)
+    if (JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp())
         return op(key);
     return nullptr;
 }
@@ -1266,7 +1266,7 @@ js::ReportErrorWithId(JSContext* cx, const char* msg, HandleId id)
 #ifdef DEBUG
 bool
 js::HasObjectMovedOp(JSObject* obj) {
-    return !!GetObjectClass(obj)->ext.objectMovedOp;
+    return !!GetObjectClass(obj)->extObjectMovedOp();
 }
 #endif
 
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index dd3aaf5dc8..cf8efa7fb3 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -275,6 +275,8 @@ JS_DefineFunctionsWithHelp(JSContext* cx, JS::HandleObject obj, const JSFunction
 
 namespace js {
 
+extern JS_FRIEND_DATA(const js::ClassOps) ProxyClassOps;
+extern JS_FRIEND_DATA(const js::ClassExtension) ProxyClassExtension;
 extern JS_FRIEND_DATA(const js::ObjectOps) ProxyObjectOps;
 
 /*
@@ -283,43 +285,27 @@ extern JS_FRIEND_DATA(const js::ObjectOps) ProxyObjectOps;
  * NB: The macro invocation must be surrounded by braces, so as to
  *     allow for potential JSClass extensions.
  */
-#define PROXY_MAKE_EXT(isWrappedNative, objectMoved)                    \
+#define PROXY_MAKE_EXT(objectMoved)                                     \
     {                                                                   \
-        isWrappedNative,                                                \
         js::proxy_WeakmapKeyDelegate,                                   \
         objectMoved                                                     \
     }
 
-#define PROXY_CLASS_WITH_EXT(name, flags, ext)                                          \
+#define PROXY_CLASS_WITH_EXT(name, flags, extPtr)                                       \
     {                                                                                   \
         name,                                                                           \
         js::Class::NON_NATIVE |                                                         \
             JSCLASS_IS_PROXY |                                                          \
             JSCLASS_DELAY_METADATA_BUILDER |                                            \
             flags,                                                                      \
-        nullptr,                 /* addProperty */                                      \
-        nullptr,                 /* delProperty */                                      \
-        nullptr,                 /* getProperty */                                      \
-        nullptr,                 /* setProperty */                                      \
-        nullptr,                 /* enumerate */                                        \
-        nullptr,                 /* resolve */                                          \
-        nullptr,                 /* mayResolve */                                       \
-        js::proxy_Finalize,      /* finalize    */                                      \
-        nullptr,                 /* call        */                                      \
-        js::proxy_HasInstance,   /* hasInstance */                                      \
-        nullptr,                 /* construct   */                                      \
-        js::proxy_Trace,         /* trace       */                                      \
+        &js::ProxyClassOps,                                                             \
         JS_NULL_CLASS_SPEC,                                                             \
-        ext,                                                                            \
+        extPtr,                                                                         \
         &js::ProxyObjectOps                                                             \
     }
 
-#define PROXY_CLASS_DEF(name, flags)                                    \
-  PROXY_CLASS_WITH_EXT(name, flags,                                     \
-                       PROXY_MAKE_EXT(                                  \
-                         false,   /* isWrappedNative */                 \
-                         js::proxy_ObjectMoved                          \
-                       ))
+#define PROXY_CLASS_DEF(name, flags) \
+  PROXY_CLASS_WITH_EXT(name, flags, &js::ProxyClassExtension)
 
 /*
  * Proxy stubs, similar to JS_*Stub, for embedder proxy class definitions.
@@ -1327,6 +1313,11 @@ class MOZ_STACK_CLASS JS_FRIEND_API(AutoStableStringChars)
   private:
     AutoStableStringChars(const AutoStableStringChars& other) = delete;
     void operator=(const AutoStableStringChars& other) = delete;
+
+    bool baseIsInline(JS::Handle linearString);
+    bool copyLatin1Chars(JSContext*, JS::Handle linearString);
+    bool copyTwoByteChars(JSContext*, JS::Handle linearString);
+    bool copyAndInflateLatin1Chars(JSContext*, JS::Handle linearString);
 };
 
 struct MOZ_STACK_CLASS JS_FRIEND_API(ErrorReport)
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index 2aeda5b944..822e18c11d 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -920,18 +920,7 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
     return functionProto;
 }
 
-static const ClassSpec JSFunctionClassSpec = {
-    CreateFunctionConstructor,
-    CreateFunctionPrototype,
-    nullptr,
-    nullptr,
-    function_methods,
-    function_properties
-};
-
-const Class JSFunction::class_ = {
-    js_Function_str,
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
+static const ClassOps JSFunctionClassOps = {
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
@@ -944,6 +933,21 @@ const Class JSFunction::class_ = {
     fun_hasInstance,
     nullptr,                 /* construct   */
     fun_trace,
+};
+
+static const ClassSpec JSFunctionClassSpec = {
+    CreateFunctionConstructor,
+    CreateFunctionPrototype,
+    nullptr,
+    nullptr,
+    function_methods,
+    function_properties
+};
+
+const Class JSFunction::class_ = {
+    js_Function_str,
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
+    &JSFunctionClassOps,
     &JSFunctionClassSpec
 };
 
@@ -2223,8 +2227,8 @@ js::DefineFunction(JSContext* cx, HandleObject obj, HandleId id, Native native,
         gop = nullptr;
         sop = nullptr;
     } else {
-        gop = obj->getClass()->getProperty;
-        sop = obj->getClass()->setProperty;
+        gop = obj->getClass()->getGetProperty();
+        sop = obj->getClass()->getSetProperty();
         MOZ_ASSERT(gop != JS_PropertyStub);
         MOZ_ASSERT(sop != JS_StrictPropertyStub);
     }
diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
index a5f764af64..b165a89a88 100644
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1449,6 +1449,9 @@ GCRuntime::setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock)
 bool
 GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock)
 {
+    // Limit heap growth factor to one hundred times size of current heap.
+    const double MaxHeapGrowthFactor = 100;
+
     switch(key) {
       case JSGC_MAX_BYTES:
         gcMaxBytes_ = value;
@@ -1478,7 +1481,7 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL
       }
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX: {
         double newGrowth = value / 100.0;
-        if (newGrowth <= 0.85)
+        if (newGrowth <= 0.85 || newGrowth > MaxHeapGrowthFactor)
             return false;
         highFrequencyHeapGrowthMax_ = newGrowth;
         MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0);
@@ -1486,7 +1489,7 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL
       }
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN: {
         double newGrowth = value / 100.0;
-        if (newGrowth <= 0.85)
+        if (newGrowth <= 0.85 || newGrowth > MaxHeapGrowthFactor)
             return false;
         highFrequencyHeapGrowthMin_ = newGrowth;
         MOZ_ASSERT(highFrequencyHeapGrowthMin_ / 0.85 > 1.0);
@@ -1494,7 +1497,7 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL
       }
       case JSGC_LOW_FREQUENCY_HEAP_GROWTH: {
         double newGrowth = value / 100.0;
-        if (newGrowth <= 0.9)
+        if (newGrowth <= 0.9 || newGrowth > MaxHeapGrowthFactor)
             return false;
         lowFrequencyHeapGrowth_ = newGrowth;
         MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0);
@@ -1521,6 +1524,9 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL
             minEmptyChunkCount_ = maxEmptyChunkCount_;
         MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
         break;
+      case JSGC_REFRESH_FRAME_SLICES_ENABLED:
+        refreshFrameSlicesEnabled_ = value != 0;
+        break;
       default:
         MOZ_CRASH("Unknown GC parameter.");
     }
@@ -1582,6 +1588,8 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock)
         return tunables.maxEmptyChunkCount();
       case JSGC_COMPACTING_ENABLED:
         return compactingEnabled;
+      case JSGC_REFRESH_FRAME_SLICES_ENABLED:
+        return tunables.areRefreshFrameSlicesEnabled();
       default:
         MOZ_ASSERT(key == JSGC_NUMBER);
         return uint32_t(number);
@@ -2036,8 +2044,13 @@ static const AllocKind AllocKindsToRelocate[] = {
     AllocKind::OBJECT12_BACKGROUND,
     AllocKind::OBJECT16,
     AllocKind::OBJECT16_BACKGROUND,
+    AllocKind::SCRIPT,
+    AllocKind::LAZY_SCRIPT,
     AllocKind::SHAPE,
-    AllocKind::ACCESSOR_SHAPE
+    AllocKind::ACCESSOR_SHAPE,
+    AllocKind::FAT_INLINE_STRING,
+    AllocKind::STRING,
+    AllocKind::EXTERNAL_STRING
 };
 
 Arena*
@@ -2188,7 +2201,7 @@ RelocateCell(Zone* zone, TenuredCell* src, AllocKind thingKind, size_t thingSize
         }
 
         // Call object moved hook if present.
-        if (JSObjectMovedOp op = srcObj->getClass()->ext.objectMovedOp)
+        if (JSObjectMovedOp op = srcObj->getClass()->extObjectMovedOp())
             op(dstObj, srcObj);
 
         MOZ_ASSERT_IF(dstObj->isNative(),
@@ -2384,6 +2397,30 @@ MovingTracer::onShapeEdge(Shape** shapep)
         *shapep = Forwarded(shape);
 }
 
+void
+MovingTracer::onStringEdge(JSString** stringp)
+{
+    JSString* string = *stringp;
+    if (IsForwarded(string))
+        *stringp = Forwarded(string);
+}
+
+void
+MovingTracer::onScriptEdge(JSScript** scriptp)
+{
+    JSScript* script = *scriptp;
+    if (IsForwarded(script))
+        *scriptp = Forwarded(script);
+}
+
+void
+MovingTracer::onLazyScriptEdge(LazyScript** lazyp)
+{
+    LazyScript* lazy = *lazyp;
+    if (IsForwarded(lazy))
+        *lazyp = Forwarded(lazy);
+}
+
 void
 Zone::prepareForCompacting()
 {
@@ -2399,12 +2436,12 @@ GCRuntime::sweepTypesAfterCompacting(Zone* zone)
 
     AutoClearTypeInferenceStateOnOOM oom(zone);
 
-    for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript* script = i.get();
         script->maybeSweepTypes(&oom);
     }
 
-    for (ZoneCellIter i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
         ObjectGroup* group = i.get();
         group->maybeSweep(&oom);
     }
@@ -2425,8 +2462,6 @@ GCRuntime::sweepZoneAfterCompacting(Zone* zone)
 
     for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
         c->sweepInnerViews();
-        c->sweepBaseShapeTable();
-        c->sweepInitialShapeTable();
         c->objectGroups.sweep(fop);
         c->sweepRegExps();
         c->sweepSavedStacks();
@@ -2441,21 +2476,26 @@ GCRuntime::sweepZoneAfterCompacting(Zone* zone)
 }
 
 template 
-static void
-UpdateCellPointersTyped(MovingTracer* trc, Arena* arena, JS::TraceKind traceKind)
+static inline void
+UpdateCellPointers(MovingTracer* trc, T* cell)
 {
-    for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
-        T* cell = reinterpret_cast(i.getCell());
-        cell->fixupAfterMovingGC();
-        TraceChildren(trc, cell, traceKind);
-    }
+    cell->fixupAfterMovingGC();
+    cell->traceChildren(trc);
+}
+
+template 
+static void
+UpdateArenaPointersTyped(MovingTracer* trc, Arena* arena, JS::TraceKind traceKind)
+{
+    for (ArenaCellIterUnderGC i(arena); !i.done(); i.next())
+        UpdateCellPointers(trc, reinterpret_cast(i.getCell()));
 }
 
 /*
- * Update the interal pointers for all cells in an arena.
+ * Update the internal pointers for all cells in an arena.
  */
 static void
-UpdateCellPointers(MovingTracer* trc, Arena* arena)
+UpdateArenaPointers(MovingTracer* trc, Arena* arena)
 {
     AllocKind kind = arena->getAllocKind();
     JS::TraceKind traceKind = MapAllocToTraceKind(kind);
@@ -2475,31 +2515,34 @@ UpdateCellPointers(MovingTracer* trc, Arena* arena)
       case AllocKind::OBJECT12_BACKGROUND:
       case AllocKind::OBJECT16:
       case AllocKind::OBJECT16_BACKGROUND:
-        UpdateCellPointersTyped(trc, arena, traceKind);
+        UpdateArenaPointersTyped(trc, arena, traceKind);
         return;
       case AllocKind::SCRIPT:
-        UpdateCellPointersTyped(trc, arena, traceKind);
+        UpdateArenaPointersTyped(trc, arena, traceKind);
         return;
       case AllocKind::LAZY_SCRIPT:
-        UpdateCellPointersTyped(trc, arena, traceKind);
+        UpdateArenaPointersTyped(trc, arena, traceKind);
         return;
       case AllocKind::SHAPE:
-        UpdateCellPointersTyped(trc, arena, traceKind);
+        UpdateArenaPointersTyped(trc, arena, traceKind);
         return;
       case AllocKind::ACCESSOR_SHAPE:
-        UpdateCellPointersTyped(trc, arena, traceKind);
+        UpdateArenaPointersTyped(trc, arena, traceKind);
         return;
       case AllocKind::BASE_SHAPE:
-        UpdateCellPointersTyped(trc, arena, traceKind);
+        UpdateArenaPointersTyped(trc, arena, traceKind);
         return;
       case AllocKind::OBJECT_GROUP:
-        UpdateCellPointersTyped(trc, arena, traceKind);
+        UpdateArenaPointersTyped(trc, arena, traceKind);
+        return;
+      case AllocKind::STRING:
+        UpdateArenaPointersTyped(trc, arena, traceKind);
         return;
       case AllocKind::JITCODE:
-        UpdateCellPointersTyped(trc, arena, traceKind);
+        UpdateArenaPointersTyped(trc, arena, traceKind);
         return;
       default:
-        MOZ_CRASH("Invalid alloc kind for UpdateCellPointers");
+        MOZ_CRASH("Invalid alloc kind for UpdateArenaPointers");
     }
 }
 
@@ -2541,9 +2584,10 @@ ArenasToUpdate::updateKind(AllocKind kind)
 {
     MOZ_ASSERT(IsValidAllocKind(kind));
 
-    // GC things that do not contain JSObject pointers don't need updating.
+    // GC things that do not contain pointers to cells we relocate don't need
+    // updating. Note that AllocKind::STRING is the only string kind which can
+    // be a rope and hence contain relocatable pointers.
     if (kind == AllocKind::FAT_INLINE_STRING ||
-        kind == AllocKind::STRING ||
         kind == AllocKind::EXTERNAL_STRING ||
         kind == AllocKind::SYMBOL)
     {
@@ -2617,7 +2661,7 @@ ArenasToUpdate::getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned maxL
     return { begin, last->next };
 }
 
-struct UpdateCellPointersTask : public GCParallelTask
+struct UpdatePointersTask : public GCParallelTask
 {
     // Maximum number of arenas to update in one block.
 #ifdef DEBUG
@@ -2626,11 +2670,11 @@ struct UpdateCellPointersTask : public GCParallelTask
     static const unsigned MaxArenasToProcess = 256;
 #endif
 
-    UpdateCellPointersTask(JSRuntime* rt, ArenasToUpdate* source, AutoLockHelperThreadState& lock)
+    UpdatePointersTask(JSRuntime* rt, ArenasToUpdate* source, AutoLockHelperThreadState& lock)
       : rt_(rt), source_(source)
     {}
 
-    ~UpdateCellPointersTask() override { join(); }
+    ~UpdatePointersTask() override { join(); }
 
   private:
     JSRuntime* rt_;
@@ -2643,7 +2687,7 @@ struct UpdateCellPointersTask : public GCParallelTask
 };
 
 bool
-UpdateCellPointersTask::getArenasToUpdate()
+UpdatePointersTask::getArenasToUpdate()
 {
     AutoLockHelperThreadState lock;
     arenas_ = source_->getArenasToUpdate(lock, MaxArenasToProcess);
@@ -2651,15 +2695,15 @@ UpdateCellPointersTask::getArenasToUpdate()
 }
 
 void
-UpdateCellPointersTask::updateArenas()
+UpdatePointersTask::updateArenas()
 {
     MovingTracer trc(rt_);
     for (Arena* arena = arenas_.begin; arena != arenas_.end; arena = arena->next)
-        UpdateCellPointers(&trc, arena);
+        UpdateArenaPointers(&trc, arena);
 }
 
 /* virtual */ void
-UpdateCellPointersTask::run()
+UpdatePointersTask::run()
 {
     while (getArenasToUpdate())
         updateArenas();
@@ -2693,8 +2737,8 @@ GCRuntime::updateAllCellPointers(MovingTracer* trc, Zone* zone)
     ArenasToUpdate
         bgArenas(zone, bgTaskCount == 0 ? ArenasToUpdate::NONE : ArenasToUpdate::BACKGROUND);
 
-    Maybe fgTask;
-    Maybe bgTasks[MaxCellUpdateBackgroundTasks];
+    Maybe fgTask;
+    Maybe bgTasks[MaxCellUpdateBackgroundTasks];
 
     size_t tasksStarted = 0;
 
@@ -2720,6 +2764,14 @@ GCRuntime::updateAllCellPointers(MovingTracer* trc, Zone* zone)
     }
 }
 
+void
+GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
+{
+    zone->typeDescrObjects.sweep();
+    for (auto r = zone->typeDescrObjects.all(); !r.empty(); r.popFront())
+        UpdateCellPointers(trc, r.front().get());
+}
+
 /*
  * Update pointers to relocated cells by doing a full heap traversal and sweep.
  *
@@ -2739,6 +2791,11 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone)
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         comp->fixupAfterMovingGC();
     JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(&trc);
+    rt->spsProfiler.fixupStringsMapAfterMovingGC();
+
+    // Update TypeDescrs before all other objects as typed objects access these
+    // objects when we trace them.
+    updateTypeDescrObjects(&trc, zone);
 
     // Iterate through all cells that can contain relocatable pointers to update
     // them. Since updating each cell is independent we try to parallelize this
@@ -2775,15 +2832,12 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone)
     // Type inference may put more blocks here to free.
     freeLifoAlloc.freeAll();
 
-    // Clear runtime caches that can contain cell pointers.
-    // TODO: Should possibly just call purgeRuntime() here.
-    rt->newObjectCache.purge();
-    rt->nativeIterCache.purge();
-
     // Call callbacks to get the rest of the system to fixup other untraced pointers.
     callWeakPointerZoneGroupCallbacks();
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         callWeakPointerCompartmentCallbacks(comp);
+    if (rt->sweepZoneCallback)
+        rt->sweepZoneCallback(zone);
 }
 
 void
@@ -3918,15 +3972,11 @@ GCRuntime::checkForCompartmentMismatches()
     if (disableStrictProxyCheckingCount)
         return;
 
-    // ZoneCellIter could GC were this not called under GC.
-    MOZ_ASSERT(rt->isHeapBusy());
-    JS::AutoSuppressGCAnalysis noAnalysis;
-
     CompartmentCheckTracer trc(rt);
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         trc.zone = zone;
         for (auto thingKind : AllAllocKinds()) {
-            for (ZoneCellIter i(zone, thingKind); !i.done(); i.next()) {
+            for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
                 trc.src = i.getCell();
                 trc.srcKind = MapAllocToTraceKind(thingKind);
                 trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(),
@@ -3946,7 +3996,7 @@ RelazifyFunctions(Zone* zone, AllocKind kind)
 
     JSRuntime* rt = zone->runtimeFromMainThread();
 
-    for (ZoneCellIter i(zone, kind); !i.done(); i.next()) {
+    for (ZoneCellIterUnderGC i(zone, kind); !i.done(); i.next()) {
         JSFunction* fun = &i.get()->as();
         if (fun->hasScript())
             fun->maybeRelazify(rt);
@@ -4996,15 +5046,40 @@ class GCSweepTask : public GCParallelTask
         AutoSetThreadIsSweeping threadIsSweeping;
         GCParallelTask::runFromHelperThread();
     }
+    GCSweepTask(const GCSweepTask&) = delete;
+
   protected:
     JSRuntime* runtime;
+
   public:
     explicit GCSweepTask(JSRuntime* rt) : runtime(rt) {}
+    GCSweepTask(GCSweepTask&& other)
+      : GCParallelTask(mozilla::Forward(other)),
+        runtime(other.runtime)
+    {}
+};
+
+// Causes the given WeakCache to be swept when run.
+class SweepWeakCacheTask : public GCSweepTask
+{
+    JS::WeakCache& cache;
+
+    SweepWeakCacheTask(const SweepWeakCacheTask&) = delete;
+
+  public:
+    SweepWeakCacheTask(JSRuntime* rt, JS::WeakCache& wc) : GCSweepTask(rt), cache(wc) {}
+    SweepWeakCacheTask(SweepWeakCacheTask&& other)
+      : GCSweepTask(mozilla::Forward(other)), cache(other.cache)
+    {}
+
+    void run() override {
+        cache.sweep();
+    }
 };
 
 #define MAKE_GC_SWEEP_TASK(name)                                              \
     class name : public GCSweepTask {                                         \
-        virtual void run() override;                                          \
+        void run() override;                                                  \
       public:                                                                 \
         explicit name (JSRuntime* rt) : GCSweepTask(rt) {}                    \
     }
@@ -5038,20 +5113,6 @@ SweepCCWrappersTask::run()
         c->sweepCrossCompartmentWrappers();
 }
 
-/* virtual */ void
-SweepBaseShapesTask::run()
-{
-    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
-        c->sweepBaseShapeTable();
-}
-
-/* virtual */ void
-SweepInitialShapesTask::run()
-{
-    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
-        c->sweepInitialShapeTable();
-}
-
 /* virtual */ void
 SweepObjectGroupsTask::run()
 {
@@ -5124,6 +5185,15 @@ GCRuntime::beginSweepingZoneGroup()
 
     validateIncrementalMarking();
 
+    FreeOp fop(rt);
+    SweepAtomsTask sweepAtomsTask(rt);
+    SweepInnerViewsTask sweepInnerViewsTask(rt);
+    SweepCCWrappersTask sweepCCWrappersTask(rt);
+    SweepObjectGroupsTask sweepObjectGroupsTask(rt);
+    SweepRegExpsTask sweepRegExpsTask(rt);
+    SweepMiscTask sweepMiscTask(rt);
+    mozilla::Vector sweepCacheTasks;
+
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         /* Clear all weakrefs that point to unmarked things. */
         for (auto edge : zone->gcWeakRefs) {
@@ -5133,25 +5203,17 @@ GCRuntime::beginSweepingZoneGroup()
         }
         zone->gcWeakRefs.clear();
 
-        for (JS::WeakCache* cache : zone->weakCaches_)
-            cache->sweep();
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        for (JS::WeakCache* cache : zone->weakCaches_) {
+            if (!sweepCacheTasks.append(SweepWeakCacheTask(rt, *cache)))
+                oomUnsafe.crash("preparing weak cache sweeping task list");
+        }
 
         /* No need to look up any more weakmap keys from this zone group. */
-        AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!zone->gcWeakKeys.clear())
             oomUnsafe.crash("clearing weak keys in beginSweepingZoneGroup()");
     }
 
-    FreeOp fop(rt);
-    SweepAtomsTask sweepAtomsTask(rt);
-    SweepInnerViewsTask sweepInnerViewsTask(rt);
-    SweepCCWrappersTask sweepCCWrappersTask(rt);
-    SweepBaseShapesTask sweepBaseShapesTask(rt);
-    SweepInitialShapesTask sweepInitialShapesTask(rt);
-    SweepObjectGroupsTask sweepObjectGroupsTask(rt);
-    SweepRegExpsTask sweepRegExpsTask(rt);
-    SweepMiscTask sweepMiscTask(rt);
-
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_START);
         callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START);
@@ -5181,11 +5243,11 @@ GCRuntime::beginSweepingZoneGroup()
             AutoLockHelperThreadState helperLock;
             startTask(sweepInnerViewsTask, gcstats::PHASE_SWEEP_INNER_VIEWS);
             startTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER);
-            startTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE);
-            startTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
             startTask(sweepObjectGroupsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT);
             startTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP);
             startTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC);
+            for (auto& task : sweepCacheTasks)
+                startTask(task, gcstats::PHASE_SWEEP_MISC);
         }
 
         // The remainder of the of the tasks run in parallel on the main
@@ -5220,24 +5282,21 @@ GCRuntime::beginSweepingZoneGroup()
 
         {
             gcstats::AutoPhase apdc(stats, gcstats::PHASE_SWEEP_DISCARD_CODE);
-            for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
+            for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
                 zone->discardJitCode(&fop);
-            }
         }
 
         {
             gcstats::AutoPhase ap1(stats, gcstats::PHASE_SWEEP_TYPES);
             gcstats::AutoPhase ap2(stats, gcstats::PHASE_SWEEP_TYPES_BEGIN);
-            for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
+            for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
                 zone->beginSweepTypes(&fop, releaseObservedTypes && !zone->isPreservingCode());
-            }
         }
 
         {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_BREAKPOINT);
-            for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
+            for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
                 zone->sweepBreakpoints(&fop);
-            }
         }
 
         {
@@ -5265,11 +5324,11 @@ GCRuntime::beginSweepingZoneGroup()
         AutoLockHelperThreadState helperLock;
         joinTask(sweepInnerViewsTask, gcstats::PHASE_SWEEP_INNER_VIEWS);
         joinTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER);
-        joinTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE);
-        joinTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
         joinTask(sweepObjectGroupsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT);
         joinTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP);
         joinTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC);
+        for (auto& task : sweepCacheTasks)
+            joinTask(task, gcstats::PHASE_SWEEP_MISC);
     }
 
     /*
@@ -5709,6 +5768,10 @@ GCRuntime::compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget)
             break;
     }
 
+    // Clear runtime caches that can contain cell pointers.
+    rt->newObjectCache.purge();
+    rt->nativeIterCache.purge();
+
 #ifdef DEBUG
     CheckHashTablesAfterMovingGC(rt);
 #endif
@@ -6582,7 +6645,7 @@ GCRuntime::notifyDidPaint()
     }
 #endif
 
-    if (isIncrementalGCInProgress() && !interFrameGC) {
+    if (isIncrementalGCInProgress() && !interFrameGC && tunables.areRefreshFrameSlicesEnabled()) {
         JS::PrepareForIncrementalGC(rt);
         gcSlice(JS::gcreason::REFRESH_FRAME);
     }
@@ -7188,7 +7251,8 @@ JS_FRIEND_API(void)
 JS::AssertGCThingMustBeTenured(JSObject* obj)
 {
     MOZ_ASSERT(obj->isTenured() &&
-               (!IsNurseryAllocable(obj->asTenured().getAllocKind()) || obj->getClass()->finalize));
+               (!IsNurseryAllocable(obj->asTenured().getAllocKind()) ||
+                obj->getClass()->hasFinalize()));
 }
 
 JS_FRIEND_API(void)
@@ -7350,9 +7414,11 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
      * Check that internal hash tables no longer have any pointers to things
      * that have been moved.
      */
+    rt->spsProfiler.checkStringsMapAfterMovingGC();
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         zone->checkUniqueIdTableAfterMovingGC();
-        for (ZoneCellIter i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) {
+
+        for (ZoneCellIterUnderGC i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) {
             BaseShape* baseShape = i.get();
             if (baseShape->hasTable())
                 baseShape->table().checkAfterMovingGC();
@@ -7360,9 +7426,11 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
     }
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         c->objectGroups.checkTablesAfterMovingGC();
+        c->dtoaCache.checkCacheAfterMovingGC();
         c->checkInitialShapesTableAfterMovingGC();
         c->checkWrapperMapAfterMovingGC();
         c->checkBaseShapeTableAfterMovingGC();
+        c->checkScriptMapsAfterMovingGC();
         if (c->debugScopes)
             c->debugScopes->checkHashTablesAfterMovingGC(rt);
     }
diff --git a/js/src/jsgc.h b/js/src/jsgc.h
index 4809208cb2..8736b8abf2 100644
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -155,7 +155,7 @@ CanBeFinalizedInBackground(AllocKind kind, const Class* clasp)
      * the alloc kind; kind may already be a background finalize kind.
      */
     return (!IsBackgroundFinalized(kind) &&
-            (!clasp->finalize || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
+            (!clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
 }
 
 /* Capacity for slotsToThingKind */
@@ -918,6 +918,8 @@ class GCParallelTask
     // Amount of time this task took to execute.
     uint64_t duration_;
 
+    explicit GCParallelTask(const GCParallelTask&) = delete;
+
   protected:
     // A flag to signal a request for early completion of the off-thread task.
     mozilla::Atomic cancel_;
@@ -926,6 +928,11 @@ class GCParallelTask
 
   public:
     GCParallelTask() : state(NotStarted), duration_(0) {}
+    GCParallelTask(GCParallelTask&& other)
+      : state(other.state),
+        duration_(0),
+        cancel_(false)
+    {}
 
     // Derived classes must override this to ensure that join() gets called
     // before members get destructed.
@@ -1034,18 +1041,15 @@ class RelocationOverlay
     /* The low bit is set so this should never equal a normal pointer. */
     static const uintptr_t Relocated = uintptr_t(0xbad0bad1);
 
-    // Arrange the fields of the RelocationOverlay so that JSObject's group
-    // pointer is not overwritten during compacting.
-
-    /* A list entry to track all relocated things. */
-    RelocationOverlay* next_;
-
     /* Set to Relocated when moved. */
     uintptr_t magic_;
 
     /* The location |this| was moved to. */
     Cell* newLocation_;
 
+    /* A list entry to track all relocated things. */
+    RelocationOverlay* next_;
+
   public:
     static RelocationOverlay* fromCell(Cell* cell) {
         return reinterpret_cast(cell);
@@ -1060,14 +1064,7 @@ class RelocationOverlay
         return newLocation_;
     }
 
-    void forwardTo(Cell* cell) {
-        MOZ_ASSERT(!isForwarded());
-        static_assert(offsetof(JSObject, group_) == offsetof(RelocationOverlay, next_),
-                      "next pointer and group should be at same location, "
-                      "so that group is not overwritten during compacting");
-        newLocation_ = cell;
-        magic_ = Relocated;
-    }
+    void forwardTo(Cell* cell);
 
     RelocationOverlay*& nextRef() {
         MOZ_ASSERT(isForwarded());
@@ -1107,7 +1104,10 @@ struct MightBeForwarded
                   "T must not be Cell or TenuredCell");
 
     static const bool value = mozilla::IsBaseOf::value ||
-                              mozilla::IsBaseOf::value;
+                              mozilla::IsBaseOf::value ||
+                              mozilla::IsBaseOf::value ||
+                              mozilla::IsBaseOf::value ||
+                              mozilla::IsBaseOf::value;
 };
 
 template 
diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h
index bf74766bd7..4ed0fd7b25 100644
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -10,6 +10,7 @@
 #include "jsgc.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
 
 #include "gc/GCTrace.h"
 #include "gc/Zone.h"
@@ -185,37 +186,15 @@ class ArenaCellIterUnderFinalize : public ArenaCellIterImpl
     explicit ArenaCellIterUnderFinalize(Arena* arena) : ArenaCellIterImpl(arena) {}
 };
 
-class ZoneCellIter
+class ZoneCellIterImpl
 {
     ArenaIter arenaIter;
     ArenaCellIterImpl cellIter;
-    JS::AutoAssertNoAlloc noAlloc;
 
   public:
-    ZoneCellIter(JS::Zone* zone, AllocKind kind) {
+    ZoneCellIterImpl(JS::Zone* zone, AllocKind kind) {
         MOZ_ASSERT(zone);
-
-        // If called from outside a GC, ensure that the heap is in a state
-        // allows us to iterate.
-        JSRuntime* rt = zone->runtimeFromMainThread();
-        MOZ_ASSERT_IF(rt->isHeapBusy(), rt->gc.nursery.isEmpty());
-        if (!rt->isHeapBusy()) {
-            // We have a single-threaded runtime, so there's no need to protect
-            // against other threads iterating or allocating. However, we do
-            // have background finalization; we have to wait for this to finish
-            // if it's currently active.
-            if (IsBackgroundFinalized(kind) &&
-                zone->arenas.needBackgroundFinalizeWait(kind))
-            {
-                rt->gc.waitBackgroundSweepEnd();
-            }
-
-            // Evict the nursery before iterating so we can see all things.
-            rt->gc.evictNursery();
-
-            // Assert that no GCs can occur while a ZoneCellIter is live.
-            noAlloc.disallowAlloc(rt);
-        }
+        MOZ_ASSERT(zone->runtimeFromAnyThread()->gc.nursery.isEmpty());
 
         arenaIter.init(zone, kind);
         if (!arenaIter.done())
@@ -248,6 +227,51 @@ class ZoneCellIter
     }
 };
 
+class ZoneCellIterUnderGC : public ZoneCellIterImpl
+{
+  public:
+    ZoneCellIterUnderGC(JS::Zone* zone, AllocKind kind)
+      : ZoneCellIterImpl(zone, kind)
+    {
+        MOZ_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy());
+    }
+};
+
+class ZoneCellIter
+{
+    mozilla::Maybe impl;
+    JS::AutoAssertNoAlloc noAlloc;
+
+  public:
+    ZoneCellIter(JS::Zone* zone, AllocKind kind) {
+        // If called from outside a GC, ensure that the heap is in a state
+        // that allows us to iterate.
+        JSRuntime* rt = zone->runtimeFromMainThread();
+        if (!rt->isHeapBusy()) {
+            // We have a single-threaded runtime, so there's no need to protect
+            // against other threads iterating or allocating. However, we do
+            // have background finalization; we have to wait for this to finish
+            // if it's currently active.
+            if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind))
+                rt->gc.waitBackgroundSweepEnd();
+
+            // Evict the nursery before iterating so we can see all things.
+            rt->gc.evictNursery();
+
+            // Assert that no GCs can occur while a ZoneCellIter is live.
+            noAlloc.disallowAlloc(rt);
+        }
+
+        impl.emplace(zone, kind);
+    }
+
+    bool done() const { return impl->done(); }
+    template
+    T* get() const { return impl->get(); }
+    Cell* getCell() const { return impl->getCell(); }
+    void next() { impl->next(); }
+};
+
 class GCZonesIter
 {
   private:
@@ -308,6 +332,20 @@ class GCZoneGroupIter {
 
 typedef CompartmentsIterT GCCompartmentGroupIter;
 
+inline void
+RelocationOverlay::forwardTo(Cell* cell)
+{
+    MOZ_ASSERT(!isForwarded());
+    // The location of magic_ is important because it must never be valid to see
+    // the value Relocated there in a GC thing that has not been moved.
+    static_assert(offsetof(RelocationOverlay, magic_) == offsetof(JSObject, group_) &&
+                  offsetof(RelocationOverlay, magic_) == offsetof(js::Shape, base_) &&
+                  offsetof(RelocationOverlay, magic_) == offsetof(JSString, d.u1.flags),
+                  "RelocationOverlay::magic_ is in the wrong location");
+    magic_ = Relocated;
+    newLocation_ = cell;
+}
+
 } /* namespace gc */
 } /* namespace js */
 
diff --git a/js/src/jshashutil.h b/js/src/jshashutil.h
index a7e9a15145..c653bd0c70 100644
--- a/js/src/jshashutil.h
+++ b/js/src/jshashutil.h
@@ -30,6 +30,11 @@ struct DependentAddPtr
       , originalGcNumber(cx->zone()->gcNumber())
     {}
 
+    DependentAddPtr(DependentAddPtr&& other)
+      : addPtr(other.addPtr)
+      , originalGcNumber(other.originalGcNumber)
+    {}
+
     template 
     bool add(ExclusiveContext* cx, T& table, const KeyInput& key, const ValueInput& value) {
         bool gcHappened = originalGcNumber != cx->zone()->gcNumber();
@@ -42,7 +47,6 @@ struct DependentAddPtr
         return true;
     }
 
-
     bool found() const                 { return addPtr.found(); }
     explicit operator bool() const     { return found(); }
     const Entry& operator*() const     { return *addPtr; }
@@ -57,6 +61,15 @@ struct DependentAddPtr
     DependentAddPtr& operator=(const DependentAddPtr&) = delete;
 };
 
+template 
+inline auto
+MakeDependentAddPtr(const ExclusiveContext* cx, T& table, const Lookup& lookup)
+  -> DependentAddPtr::Type>
+{
+    using Ptr = DependentAddPtr::Type>;
+    return Ptr(cx, table, lookup);
+}
+
 } // namespace js
 
 #endif
diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp
index 9396e90495..3bb8aada2f 100644
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -53,10 +53,10 @@ typedef Rooted RootedPropertyIteratorObject;
 static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
 
 void
-NativeIterator::mark(JSTracer* trc)
+NativeIterator::trace(JSTracer* trc)
 {
     for (HeapPtrFlatString* str = begin(); str < end(); str++)
-        TraceEdge(trc, str, "prop");
+        TraceNullableEdge(trc, str, "prop");
     TraceNullableEdge(trc, &obj, "obj");
 
     for (size_t i = 0; i < guard_length; i++)
@@ -381,7 +381,7 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props)
             }
         } else if (pobj->isNative()) {
             // Give the object a chance to resolve all lazy properties
-            if (JSEnumerateOp enumerate = pobj->getClass()->enumerate) {
+            if (JSEnumerateOp enumerate = pobj->getClass()->getEnumerate()) {
                 if (!enumerate(cx, pobj.as()))
                     return false;
             }
@@ -592,30 +592,22 @@ NewPropertyIteratorObject(JSContext* cx, unsigned flags)
 }
 
 NativeIterator*
-NativeIterator::allocateIterator(JSContext* cx, uint32_t numGuards, const AutoIdVector& props)
+NativeIterator::allocateIterator(JSContext* cx, uint32_t numGuards, uint32_t plength)
 {
     JS_STATIC_ASSERT(sizeof(ReceiverGuard) == 2 * sizeof(void*));
 
-    size_t plength = props.length();
-    NativeIterator* ni = cx->zone()->pod_malloc_with_extra(plength + numGuards * 2);
+    size_t extraLength = plength + numGuards * 2;
+    NativeIterator* ni = cx->zone()->pod_malloc_with_extra(extraLength);
     if (!ni) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    AutoValueVector strings(cx);
-    ni->props_array = ni->props_cursor = reinterpret_cast(ni + 1);
+    void** extra = reinterpret_cast(ni + 1);
+    PodZero(ni);
+    PodZero(extra, extraLength);
+    ni->props_array = ni->props_cursor = reinterpret_cast(extra);
     ni->props_end = ni->props_array + plength;
-    if (plength) {
-        for (size_t i = 0; i < plength; i++) {
-            JSFlatString* str = IdToString(cx, props[i]);
-            if (!str || !strings.append(StringValue(str)))
-                return nullptr;
-            ni->props_array[i].init(str);
-        }
-    }
-    ni->next_ = nullptr;
-    ni->prev_ = nullptr;
     return ni;
 }
 
@@ -647,6 +639,27 @@ NativeIterator::init(JSObject* obj, JSObject* iterObj, unsigned flags, uint32_t
     this->guard_key = key;
 }
 
+bool
+NativeIterator::initProperties(JSContext* cx, Handle obj,
+                               const AutoIdVector& props)
+{
+    // The obj parameter is just so that we can ensure that this object will get
+    // traced if we GC.
+    MOZ_ASSERT(this == obj->getNativeIterator());
+
+    size_t plength = props.length();
+    MOZ_ASSERT(plength == size_t(end() - begin()));
+
+    for (size_t i = 0; i < plength; i++) {
+        JSFlatString* str = IdToString(cx, props[i]);
+        if (!str)
+            return false;
+        props_array[i].init(str);
+    }
+
+    return true;
+}
+
 static inline void
 RegisterEnumerator(JSContext* cx, PropertyIteratorObject* iterobj, NativeIterator* ni)
 {
@@ -673,10 +686,14 @@ VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVecto
     if (!iterobj)
         return false;
 
-    NativeIterator* ni = NativeIterator::allocateIterator(cx, numGuards, keys);
+    NativeIterator* ni = NativeIterator::allocateIterator(cx, numGuards, keys.length());
     if (!ni)
         return false;
+
+    iterobj->setNativeIterator(ni);
     ni->init(obj, iterobj, flags, numGuards, key);
+    if (!ni->initProperties(cx, iterobj, keys))
+        return false;
 
     if (numGuards) {
         // Fill in the guard array from scratch.
@@ -689,7 +706,6 @@ VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVecto
         MOZ_ASSERT(ind == numGuards);
     }
 
-    iterobj->setNativeIterator(ni);
     objp.set(iterobj);
 
     RegisterEnumerator(cx, iterobj, ni);
@@ -710,12 +726,15 @@ VectorToValueIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVec
     if (!iterobj)
         return false;
 
-    NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys);
+    NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys.length());
     if (!ni)
         return false;
-    ni->init(obj, iterobj, flags, 0, 0);
 
     iterobj->setNativeIterator(ni);
+    ni->init(obj, iterobj, flags, 0, 0);
+    if (!ni->initProperties(cx, iterobj, keys))
+        return false;
+
     objp.set(iterobj);
 
     RegisterEnumerator(cx, iterobj, ni);
@@ -741,12 +760,15 @@ js::NewEmptyPropertyIterator(JSContext* cx, unsigned flags, MutableHandleObject
         return false;
 
     AutoIdVector keys(cx); // Empty
-    NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys);
+    NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys.length());
     if (!ni)
         return false;
-    ni->init(nullptr, iterobj, flags, 0, 0);
 
     iterobj->setNativeIterator(ni);
+    ni->init(nullptr, iterobj, flags, 0, 0);
+    if (!ni->initProperties(cx, iterobj, keys))
+        return false;
+
     objp.set(iterobj);
 
     RegisterEnumerator(cx, iterobj, ni);
@@ -783,7 +805,7 @@ CanCacheIterableObject(JSContext* cx, JSObject* obj)
         if (obj->is() ||
             obj->hasUncacheableProto() ||
             obj->getOpsEnumerate() ||
-            obj->getClass()->enumerate ||
+            obj->getClass()->getEnumerate() ||
             obj->as().containsPure(cx->names().iteratorIntrinsic))
         {
             return false;
@@ -1059,7 +1081,7 @@ void
 PropertyIteratorObject::trace(JSTracer* trc, JSObject* obj)
 {
     if (NativeIterator* ni = obj->as().getNativeIterator())
-        ni->mark(trc);
+        ni->trace(trc);
 }
 
 void
@@ -1069,11 +1091,7 @@ PropertyIteratorObject::finalize(FreeOp* fop, JSObject* obj)
         fop->free_(ni);
 }
 
-const Class PropertyIteratorObject::class_ = {
-    "Iterator",
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_BACKGROUND_FINALIZE,
+const ClassOps PropertyIteratorObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -1088,6 +1106,14 @@ const Class PropertyIteratorObject::class_ = {
     trace
 };
 
+const Class PropertyIteratorObject::class_ = {
+    "Iterator",
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_BACKGROUND_FINALIZE,
+    &PropertyIteratorObject::classOps_
+};
+
 static const Class ArrayIteratorPrototypeClass = {
     "Array Iterator",
     0
@@ -1414,9 +1440,7 @@ stopiter_hasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool
     return true;
 }
 
-const Class StopIterationObject::class_ = {
-    "StopIteration",
-    JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
+static const ClassOps StopIterationObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -1429,6 +1453,12 @@ const Class StopIterationObject::class_ = {
     stopiter_hasInstance
 };
 
+const Class StopIterationObject::class_ = {
+    "StopIteration",
+    JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
+    &StopIterationObjectClassOps
+};
+
 static const JSFunctionSpec iterator_proto_methods[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FS_END
@@ -1499,13 +1529,12 @@ js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj)
     if (!iteratorProto)
         return nullptr;
 
-    AutoIdVector blank(cx);
-    NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, blank);
+    NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, 0);
     if (!ni)
         return nullptr;
-    ni->init(nullptr, nullptr, 0 /* flags */, 0, 0);
 
     iteratorProto->as().setNativeIterator(ni);
+    ni->init(nullptr, nullptr, 0 /* flags */, 0, 0);
 
     Rooted ctor(cx);
     ctor = global->createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
diff --git a/js/src/jsiter.h b/js/src/jsiter.h
index 31bb95d9c8..2c2d7fb480 100644
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -28,6 +28,8 @@
 
 namespace js {
 
+class PropertyIteratorObject;
+
 struct NativeIterator
 {
     HeapPtrObject obj;                  // Object being iterated.
@@ -104,11 +106,12 @@ struct NativeIterator
     }
 
     static NativeIterator* allocateSentinel(JSContext* maybecx);
-    static NativeIterator* allocateIterator(JSContext* cx, uint32_t slength,
-                                            const js::AutoIdVector& props);
+    static NativeIterator* allocateIterator(JSContext* cx, uint32_t slength, uint32_t plength);
     void init(JSObject* obj, JSObject* iterObj, unsigned flags, uint32_t slength, uint32_t key);
+    bool initProperties(JSContext* cx, Handle obj,
+                        const js::AutoIdVector& props);
 
-    void mark(JSTracer* trc);
+    void trace(JSTracer* trc);
 
     static void destroy(NativeIterator* iter) {
         js_free(iter);
@@ -117,6 +120,8 @@ struct NativeIterator
 
 class PropertyIteratorObject : public NativeObject
 {
+    static const ClassOps classOps_;
+
   public:
     static const Class class_;
 
diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp
index e96d00680e..7097bd2813 100644
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1859,8 +1859,8 @@ js::InitClass(JSContext* cx, HandleObject obj, HandleObject protoProto_,
     RootedObject protoProto(cx, protoProto_);
 
     /* Check function pointer members. */
-    MOZ_ASSERT(clasp->getProperty != JS_PropertyStub);
-    MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub);
+    MOZ_ASSERT(clasp->getGetProperty() != JS_PropertyStub);
+    MOZ_ASSERT(clasp->getSetProperty() != JS_StrictPropertyStub);
 
     RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
     if (!atom)
@@ -2116,8 +2116,8 @@ JSObject::callHook() const
 {
     const js::Class* clasp = getClass();
 
-    if (clasp->call)
-        return clasp->call;
+    if (JSNative call = clasp->getCall())
+        return call;
 
     if (is()) {
         const js::ProxyObject& p = as();
@@ -2132,8 +2132,8 @@ JSObject::constructHook() const
 {
     const js::Class* clasp = getClass();
 
-    if (clasp->construct)
-        return clasp->construct;
+    if (JSNative construct = clasp->getConstruct())
+        return construct;
 
     if (is()) {
         const js::ProxyObject& p = as();
@@ -2920,8 +2920,8 @@ DefineFunctionFromSpec(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs
         gop = nullptr;
         sop = nullptr;
     } else {
-        gop = obj->getClass()->getProperty;
-        sop = obj->getClass()->setProperty;
+        gop = obj->getClass()->getGetProperty();
+        sop = obj->getClass()->getSetProperty();
         MOZ_ASSERT(gop != JS_PropertyStub);
         MOZ_ASSERT(sop != JS_StrictPropertyStub);
     }
@@ -3866,8 +3866,8 @@ JSObject::traceChildren(JSTracer* trc)
 
     // Call the trace hook at the end so that during a moving GC the trace hook
     // will see updated fields and slots.
-    if (clasp->trace)
-        clasp->trace(trc, this);
+    if (clasp->hasTrace())
+        clasp->doTrace(trc, this);
 }
 
 static JSAtom*
diff --git a/js/src/jsobj.h b/js/src/jsobj.h
index f6b7d8ae33..cc56342257 100644
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1098,7 +1098,7 @@ GetInitialHeap(NewObjectKind newKind, const Class* clasp)
 {
     if (newKind != GenericObject)
         return gc::TenuredHeap;
-    if (clasp->finalize && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
+    if (clasp->hasFinalize() && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
         return gc::TenuredHeap;
     return gc::DefaultHeap;
 }
diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h
index f2bb74debe..05379af10f 100644
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -78,8 +78,8 @@ JSObject::finalize(js::FreeOp* fop)
 #endif
 
     const js::Class* clasp = getClass();
-    if (clasp->finalize)
-        clasp->finalize(fop, this);
+    if (clasp->hasFinalize())
+        clasp->doFinalize(fop, this);
 
     if (!clasp->isNative())
         return;
@@ -321,7 +321,7 @@ JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::Initi
                   js::gc::GetGCKindSlots(kind, group->clasp()) == shape->numFixedSlots());
     MOZ_ASSERT_IF(group->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE,
                   IsBackgroundFinalized(kind));
-    MOZ_ASSERT_IF(group->clasp()->finalize,
+    MOZ_ASSERT_IF(group->clasp()->hasFinalize(),
                   heap == js::gc::TenuredHeap ||
                   (group->clasp()->flags & JSCLASS_SKIP_NURSERY_FINALIZE));
     MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(),
diff --git a/js/src/jspropertytree.cpp b/js/src/jspropertytree.cpp
index 5574c36326..9892463a3e 100644
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -318,14 +318,6 @@ Shape::fixupAfterMovingGC()
         fixupShapeTreeAfterMovingGC();
 }
 
-void
-BaseShape::fixupAfterMovingGC()
-{
-    if (hasTable())
-        table().fixupAfterMovingGC();
-}
-
-
 void
 Shape::fixupGetterSetterForBarrier(JSTracer* trc)
 {
diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp
index 62e289f836..19d4f0f32d 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -213,15 +213,15 @@ Bindings::initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle
     return true;
 }
 
-bool
-Bindings::initTrivial(ExclusiveContext* cx)
+/* static */ bool
+Bindings::initTrivialForScript(ExclusiveContext* cx, HandleScript script)
 {
     Shape* shape = EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr),
                                                CallObject::RESERVED_SLOTS,
                                                BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE);
     if (!shape)
         return false;
-    callObjShape_.init(shape);
+    script->bindings.callObjShape_.init(shape);
     return true;
 }
 
@@ -242,7 +242,7 @@ Bindings::clone(JSContext* cx, MutableHandle self,
                 uint8_t* dstScriptData, HandleScript srcScript)
 {
     /* The clone has the same bindingArray_ offset as 'src'. */
-    Handle src = Handle::fromMarkedLocation(&srcScript->bindings);
+    const Bindings& src = srcScript->bindings;
     ptrdiff_t off = (uint8_t*)src.bindingArray() - srcScript->data;
     MOZ_ASSERT(off >= 0);
     MOZ_ASSERT(size_t(off) <= srcScript->dataSize());
@@ -1702,10 +1702,7 @@ ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
     sso->setReservedSlot(SOURCE_SLOT, PrivateValue(nullptr));
 }
 
-const Class ScriptSourceObject::class_ = {
-    "ScriptSource",
-    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
-    JSCLASS_IS_ANONYMOUS,
+static const ClassOps ScriptSourceObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -1713,11 +1710,18 @@ const Class ScriptSourceObject::class_ = {
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
-    finalize,
+    ScriptSourceObject::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
-    trace
+    ScriptSourceObject::trace
+};
+
+const Class ScriptSourceObject::class_ = {
+    "ScriptSource",
+    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
+    JSCLASS_IS_ANONYMOUS,
+    &ScriptSourceObjectClassOps
 };
 
 ScriptSourceObject*
@@ -2937,7 +2941,7 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t ncon
 /* static */ bool
 JSScript::fullyInitTrivial(ExclusiveContext* cx, Handle script)
 {
-    if (!script->bindings.initTrivial(cx))
+    if (!Bindings::initTrivialForScript(cx, script))
         return false;
 
     if (!partiallyInit(cx, script, 0, 0, 0, 0, 0, 0))
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index abd45a7c82..e86dbbad7b 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -286,7 +286,7 @@ class Bindings
                                          bool isModule = false);
 
     // Initialize a trivial Bindings with no slots and an empty callObjShape.
-    bool initTrivial(ExclusiveContext* cx);
+    static bool initTrivialForScript(ExclusiveContext* cx, HandleScript script);
 
     // CompileScript parses and compiles one statement at a time, but the result
     // is one Script object.  There will be no vars or bindings, because those
@@ -528,6 +528,7 @@ typedef HashMap Compre
 
 class ScriptSourceObject : public NativeObject
 {
+    static const ClassOps classOps_;
+
   public:
     static const Class class_;
 
@@ -2012,24 +2015,37 @@ namespace js {
  */
 class BindingIter
 {
-    Handle bindings_;
+    Binding* bindingArray_;
+    uint32_t numArgs_;
+    uint32_t count_;
     uint32_t i_;
     uint32_t unaliasedLocal_;
 
     friend class ::JSScript;
     friend class Bindings;
 
+    void init(const Bindings& bindings) {
+        // Bindings contained in a JSScript may be moved by the GC.  Copy the
+        // necessary fields into this object.
+        bindingArray_ = bindings.bindingArray();
+        numArgs_ = bindings.numArgs();
+        count_ = bindings.count();
+    }
+
   public:
     explicit BindingIter(Handle bindings)
-      : bindings_(bindings), i_(0), unaliasedLocal_(0)
-    {}
+      : i_(0), unaliasedLocal_(0)
+    {
+        init(bindings);
+    }
 
-    explicit BindingIter(const HandleScript& script)
-      : bindings_(Handle::fromMarkedLocation(&script->bindings)),
-        i_(0), unaliasedLocal_(0)
-    {}
+    explicit BindingIter(HandleScript script)
+      : i_(0), unaliasedLocal_(0)
+    {
+        init(script->bindings);
+    }
 
-    bool done() const { return i_ == bindings_.count(); }
+    bool done() const { return i_ == count_; }
     explicit operator bool() const { return !done(); }
     BindingIter& operator++() { (*this)++; return *this; }
 
@@ -2047,7 +2063,7 @@ class BindingIter
     // has no stack slot.
     uint32_t frameIndex() const {
         MOZ_ASSERT(!done());
-        if (i_ < bindings_.numArgs())
+        if (i_ < numArgs_)
             return i_;
         MOZ_ASSERT(!(*this)->aliased());
         return unaliasedLocal_;
@@ -2058,17 +2074,17 @@ class BindingIter
     // both unaliased and aliased arguments.
     uint32_t argIndex() const {
         MOZ_ASSERT(!done());
-        MOZ_ASSERT(i_ < bindings_.numArgs());
+        MOZ_ASSERT(i_ < numArgs_);
         return i_;
     }
     uint32_t argOrLocalIndex() const {
         MOZ_ASSERT(!done());
-        return i_ < bindings_.numArgs() ? i_ : i_ - bindings_.numArgs();
+        return i_ < numArgs_ ? i_ : i_ - numArgs_;
     }
     uint32_t localIndex() const {
         MOZ_ASSERT(!done());
-        MOZ_ASSERT(i_ >= bindings_.numArgs());
-        return i_ - bindings_.numArgs();
+        MOZ_ASSERT(i_ >= numArgs_);
+        return i_ - numArgs_;
     }
     bool isBodyLevelLexical() const {
         MOZ_ASSERT(!done());
@@ -2076,8 +2092,8 @@ class BindingIter
         return binding.kind() != Binding::ARGUMENT;
     }
 
-    const Binding& operator*() const { MOZ_ASSERT(!done()); return bindings_.bindingArray()[i_]; }
-    const Binding* operator->() const { MOZ_ASSERT(!done()); return &bindings_.bindingArray()[i_]; }
+    const Binding& operator*() const { MOZ_ASSERT(!done()); return bindingArray_[i_]; }
+    const Binding* operator->() const { MOZ_ASSERT(!done()); return &bindingArray_[i_]; }
 };
 
 /*
diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp
index 19af7bf8f9..9c29c53dd3 100644
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -430,10 +430,7 @@ str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
     return true;
 }
 
-const Class StringObject::class_ = {
-    js_String_str,
-    JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_String),
+static const ClassOps StringObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -443,6 +440,13 @@ const Class StringObject::class_ = {
     str_mayResolve
 };
 
+const Class StringObject::class_ = {
+    js_String_str,
+    JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_String),
+    &StringObjectClassOps
+};
+
 /*
  * Returns a JSString * for the |this| value associated with 'call', or throws
  * a TypeError if |this| is null or undefined.  This algorithm is the same as
@@ -2305,6 +2309,9 @@ js::str_replace_string_raw(JSContext* cx, HandleString string, HandleString patt
         return nullptr;
 
     RootedAtom pat(cx, AtomizeString(cx, pattern));
+    if (!pat)
+        return nullptr;
+
     size_t patternLength = pat->length();
     int32_t match;
     uint32_t dollarIndex;
diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp
index bd67162d51..25658a2043 100644
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -142,7 +142,7 @@ ObjectValueMap::findZoneEdges()
         JSObject* key = r.front().key();
         if (key->asTenured().isMarked(BLACK) && !key->asTenured().isMarked(GRAY))
             continue;
-        JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp;
+        JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp();
         if (!op)
             continue;
         JSObject* delegate = op(key);
diff --git a/js/src/jsweakmap.h b/js/src/jsweakmap.h
index 89809eae17..0c746a7065 100644
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -282,7 +282,7 @@ class WeakMap : public HashMap,
     void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
 
     JSObject* getDelegate(JSObject* key) const {
-        JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp;
+        JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp();
         return op ? op(key) : nullptr;
     }
 
diff --git a/js/src/perf/jsperf.cpp b/js/src/perf/jsperf.cpp
index ddb9d62564..3f51b2a996 100644
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -159,10 +159,21 @@ static const struct pm_const {
 static bool pm_construct(JSContext* cx, unsigned argc, Value* vp);
 static void pm_finalize(JSFreeOp* fop, JSObject* obj);
 
+static const JSClassOps pm_classOps = {
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    pm_finalize
+};
+
 static const JSClass pm_class = {
-    "PerfMeasurement", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, pm_finalize
+    "PerfMeasurement",
+    JSCLASS_HAS_PRIVATE,
+    &pm_classOps
 };
 
 // Constructor and destructor
diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp
index 4099255f52..1fa146817b 100644
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -228,10 +228,10 @@ js::SetPropertyIgnoringNamedGetter(JSContext* cx, HandleObject obj, HandleId id,
         // A very old nonstandard SpiderMonkey extension: default to the Class
         // getter and setter ops.
         const Class* clasp = receiverObj->getClass();
-        MOZ_ASSERT(clasp->getProperty != JS_PropertyStub);
-        MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub);
-        return DefineProperty(cx, receiverObj, id, v, clasp->getProperty, clasp->setProperty,
-                              attrs, result);
+        MOZ_ASSERT(clasp->getGetProperty() != JS_PropertyStub);
+        MOZ_ASSERT(clasp->getSetProperty() != JS_StrictPropertyStub);
+        return DefineProperty(cx, receiverObj, id, v,
+                              clasp->getGetProperty(), clasp->getSetProperty(), attrs, result);
     }
 
     // Step 6.
diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp
index 876645cef3..b8ae33a981 100644
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -716,6 +716,25 @@ js::proxy_FunToString(JSContext* cx, HandleObject proxy, unsigned indent)
     return Proxy::fun_toString(cx, proxy, indent);
 }
 
+const ClassOps js::ProxyClassOps = {
+    nullptr,                 /* addProperty */
+    nullptr,                 /* delProperty */
+    nullptr,                 /* getProperty */
+    nullptr,                 /* setProperty */
+    nullptr,                 /* enumerate   */
+    nullptr,                 /* resolve     */
+    nullptr,                 /* mayResolve  */
+    js::proxy_Finalize,      /* finalize    */
+    nullptr,                 /* call        */
+    js::proxy_HasInstance,   /* hasInstance */
+    nullptr,                 /* construct   */
+    js::proxy_Trace,         /* trace       */
+};
+
+const ClassExtension js::ProxyClassExtension = PROXY_MAKE_EXT(
+    js::proxy_ObjectMoved
+);
+
 const ObjectOps js::ProxyObjectOps = {
     js::proxy_LookupProperty,
     js::proxy_DefineProperty,
diff --git a/js/src/shell/OSObject.cpp b/js/src/shell/OSObject.cpp
index 1777d6f59d..dd3fdd0686 100644
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -395,9 +395,7 @@ class FileObject : public JSObject {
     }
 };
 
-const js::Class FileObject::class_ = {
-    "File",
-    JSCLASS_HAS_RESERVED_SLOTS(FileObject::NUM_SLOTS),
+static const js::ClassOps FileObjectClassOps = {
     nullptr,               /* addProperty */
     nullptr,               /* delProperty */
     nullptr,               /* getProperty */
@@ -412,6 +410,12 @@ const js::Class FileObject::class_ = {
     nullptr                /* trace */
 };
 
+const js::Class FileObject::class_ = {
+    "File",
+    JSCLASS_HAS_RESERVED_SLOTS(FileObject::NUM_SLOTS),
+    &FileObjectClassOps
+};
+
 static FileObject*
 redirect(JSContext* cx, HandleString relFilename, RCFile** globalFile)
 {
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 43de4b012e..dae5f29055 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2648,9 +2648,7 @@ sandbox_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
     return true;
 }
 
-static const JSClass sandbox_class = {
-    "sandbox",
-    JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps sandbox_classOps = {
     nullptr, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
     nullptr, nullptr,
@@ -2658,6 +2656,12 @@ static const JSClass sandbox_class = {
     JS_GlobalObjectTraceHook
 };
 
+static const JSClass sandbox_class = {
+    "sandbox",
+    JSCLASS_GLOBAL_FLAGS,
+    &sandbox_classOps
+};
+
 static void
 SetStandardCompartmentOptions(JS::CompartmentOptions& options)
 {
@@ -5909,8 +5913,7 @@ global_mayResolve(const JSAtomState& names, jsid id, JSObject* maybeObj)
     return JS_MayResolveStandardClass(names, id, maybeObj);
 }
 
-static const JSClass global_class = {
-    "global", JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps global_classOps = {
     nullptr, nullptr, nullptr, nullptr,
     global_enumerate, global_resolve, global_mayResolve,
     nullptr,
@@ -5918,6 +5921,11 @@ static const JSClass global_class = {
     JS_GlobalObjectTraceHook
 };
 
+static const JSClass global_class = {
+    "global", JSCLASS_GLOBAL_FLAGS,
+    &global_classOps
+};
+
 /*
  * Define a FakeDOMObject constructor. It returns an object with a getter,
  * setter and method with attached JitInfo. This object can be used to test
diff --git a/js/src/tests/ecma_6/ArrayBuffer/CloneArrayBuffer.js b/js/src/tests/ecma_6/ArrayBuffer/CloneArrayBuffer.js
new file mode 100644
index 0000000000..480314ad8e
--- /dev/null
+++ b/js/src/tests/ecma_6/ArrayBuffer/CloneArrayBuffer.js
@@ -0,0 +1,35 @@
+var BUGNUMBER = 1264941;
+var summary = 'CloneArrayBuffer should be called with byteLength of source typedArray';
+
+print(BUGNUMBER + ": " + summary);
+
+function test(ctor, byteLength) {
+  var abuf = new ctor(byteLength);
+  assertEq(abuf.byteLength, byteLength);
+
+  for (var byteOffset of [0, 16]) {
+    for (var elementLength = 0;
+         elementLength < (byteLength - byteOffset) / Float64Array.BYTES_PER_ELEMENT;
+         elementLength++) {
+      var a1 = new Float64Array(abuf, byteOffset, elementLength);
+      assertEq(a1.buffer.byteLength, byteLength);
+      assertEq(a1.byteLength, elementLength * Float64Array.BYTES_PER_ELEMENT);
+      assertEq(a1.byteOffset, byteOffset);
+
+      var a2 = new Float64Array(a1);
+      assertEq(a2.buffer.byteLength, a1.byteLength);
+      assertEq(a2.byteLength, a1.byteLength);
+      assertEq(a2.byteOffset, 0);
+    }
+  }
+}
+
+test(ArrayBuffer, 16);
+test(ArrayBuffer, 128);
+
+class MyArrayBuffer extends ArrayBuffer {}
+test(MyArrayBuffer, 16);
+test(MyArrayBuffer, 128);
+
+if (typeof reportCompare === 'function')
+  reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/extensions/String-match-flags.js b/js/src/tests/ecma_6/extensions/String-match-flags.js
new file mode 100644
index 0000000000..ca5404d283
--- /dev/null
+++ b/js/src/tests/ecma_6/extensions/String-match-flags.js
@@ -0,0 +1,29 @@
+var BUGNUMBER = 1263139;
+var summary = "String.prototype.match with non-string non-standard flags argument.";
+
+print(BUGNUMBER + ": " + summary);
+
+enableMatchFlagArgument();
+
+var called;
+var flags = {
+  toString() {
+    called = true;
+    return "";
+  }
+};
+
+called = false;
+"a".match("a", flags);
+assertEq(called, true);
+
+called = false;
+"a".search("a", flags);
+assertEq(called, true);
+
+called = false;
+"a".replace("a", "b", flags);
+assertEq(called, true);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp
index b1c231c694..f8e2f6c829 100644
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -675,13 +675,7 @@ ArgumentsObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject
  * stack frame with their corresponding property values in the frame's
  * arguments object.
  */
-const Class MappedArgumentsObject::class_ = {
-    "Arguments",
-    JSCLASS_DELAY_METADATA_BUILDER |
-    JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
-    JSCLASS_SKIP_NURSERY_FINALIZE |
-    JSCLASS_BACKGROUND_FINALIZE,
+const ClassOps MappedArgumentsObject::classOps_ = {
     nullptr,                 /* addProperty */
     ArgumentsObject::obj_delProperty,
     nullptr,                 /* getProperty */
@@ -696,17 +690,21 @@ const Class MappedArgumentsObject::class_ = {
     ArgumentsObject::trace
 };
 
+const Class MappedArgumentsObject::class_ = {
+    "Arguments",
+    JSCLASS_DELAY_METADATA_BUILDER |
+    JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
+    JSCLASS_SKIP_NURSERY_FINALIZE |
+    JSCLASS_BACKGROUND_FINALIZE,
+    &MappedArgumentsObject::classOps_
+};
+
 /*
  * Unmapped arguments is significantly less magical than mapped arguments, so
  * it is represented by a different class while sharing some functionality.
  */
-const Class UnmappedArgumentsObject::class_ = {
-    "Arguments",
-    JSCLASS_DELAY_METADATA_BUILDER |
-    JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
-    JSCLASS_SKIP_NURSERY_FINALIZE |
-    JSCLASS_BACKGROUND_FINALIZE,
+const ClassOps UnmappedArgumentsObject::classOps_ = {
     nullptr,                 /* addProperty */
     ArgumentsObject::obj_delProperty,
     nullptr,                 /* getProperty */
@@ -720,3 +718,13 @@ const Class UnmappedArgumentsObject::class_ = {
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
+
+const Class UnmappedArgumentsObject::class_ = {
+    "Arguments",
+    JSCLASS_DELAY_METADATA_BUILDER |
+    JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
+    JSCLASS_SKIP_NURSERY_FINALIZE |
+    JSCLASS_BACKGROUND_FINALIZE,
+    &UnmappedArgumentsObject::classOps_
+};
diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h
index 636b7a79b7..e029384863 100644
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -329,6 +329,8 @@ class ArgumentsObject : public NativeObject
 
 class MappedArgumentsObject : public ArgumentsObject
 {
+    static const ClassOps classOps_;
+
   public:
     static const Class class_;
 
@@ -352,6 +354,8 @@ class MappedArgumentsObject : public ArgumentsObject
 
 class UnmappedArgumentsObject : public ArgumentsObject
 {
+    static const ClassOps classOps_;
+
   public:
     static const Class class_;
 
diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp
index 0f0d72d4c7..b53c2f8c0f 100644
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -96,30 +96,35 @@ const Class ArrayBufferObject::protoClass = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer)
 };
 
+const ClassOps ArrayBufferObject::classOps_ = {
+    nullptr,        /* addProperty */
+    nullptr,        /* delProperty */
+    nullptr,        /* getProperty */
+    nullptr,        /* setProperty */
+    nullptr,        /* enumerate */
+    nullptr,        /* resolve */
+    nullptr,        /* mayResolve */
+    ArrayBufferObject::finalize,
+    nullptr,        /* call        */
+    nullptr,        /* hasInstance */
+    nullptr,        /* construct   */
+    ArrayBufferObject::trace,
+};
+
+static const ClassExtension ArrayBufferObjectClassExtension = {
+    nullptr,    /* weakmapKeyDelegateOp */
+    ArrayBufferObject::objectMoved
+};
+
 const Class ArrayBufferObject::class_ = {
     "ArrayBuffer",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
     JSCLASS_BACKGROUND_FINALIZE,
-    nullptr,                 /* addProperty */
-    nullptr,                 /* delProperty */
-    nullptr,                 /* getProperty */
-    nullptr,                 /* setProperty */
-    nullptr,                 /* enumerate */
-    nullptr,                 /* resolve */
-    nullptr,                 /* mayResolve */
-    ArrayBufferObject::finalize,
-    nullptr,        /* call        */
-    nullptr,        /* hasInstance */
-    nullptr,        /* construct   */
-    ArrayBufferObject::trace,
+    &ArrayBufferObject::classOps_,
     JS_NULL_CLASS_SPEC,
-    {
-        false,      /* isWrappedNative */
-        nullptr,    /* weakmapKeyDelegateOp */
-        ArrayBufferObject::objectMoved
-    }
+    &ArrayBufferObjectClassExtension
 };
 
 const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h
index 3fb9b7d32c..f8f97f32f0 100644
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -102,6 +102,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
     static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
     static bool fun_slice_impl(JSContext* cx, const CallArgs& args);
 
+    static const ClassOps classOps_;
+
   public:
     static const uint8_t DATA_SLOT = 0;
     static const uint8_t BYTE_LENGTH_SLOT = 1;
diff --git a/js/src/vm/ArrayObject-inl.h b/js/src/vm/ArrayObject-inl.h
index ff50aa49b3..c83e7b6760 100644
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -41,7 +41,7 @@ ArrayObject::createArrayInternal(ExclusiveContext* cx, gc::AllocKind kind, gc::I
     MOZ_ASSERT(shape && group);
     MOZ_ASSERT(group->clasp() == shape->getObjectClass());
     MOZ_ASSERT(group->clasp() == &ArrayObject::class_);
-    MOZ_ASSERT_IF(group->clasp()->finalize, heap == gc::TenuredHeap);
+    MOZ_ASSERT_IF(group->clasp()->hasFinalize(), heap == gc::TenuredHeap);
     MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(),
                   heap == js::gc::TenuredHeap);
     MOZ_ASSERT(group->clasp()->shouldDelayMetadataBuilder());
diff --git a/js/src/vm/Debugger-inl.h b/js/src/vm/Debugger-inl.h
index 82aeb4beb7..2306698164 100644
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -29,7 +29,7 @@ js::Debugger::onLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc
 /* static */ inline js::Debugger*
 js::Debugger::fromJSObject(const JSObject* obj)
 {
-    MOZ_ASSERT(js::GetObjectClass(obj) == &jsclass);
+    MOZ_ASSERT(js::GetObjectClass(obj) == &class_);
     return (Debugger*) obj->as().getPrivate();
 }
 
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index 4b8ba2099b..66c5786c53 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -59,9 +59,13 @@ using mozilla::Variant;
 using mozilla::AsVariant;
 
 
-/*** Forward declarations ************************************************************************/
+/*** Forward declarations, ClassOps and Classes **************************************************/
 
-extern const Class DebuggerFrame_class;
+static void DebuggerFrame_finalize(FreeOp* fop, JSObject* obj);
+static void DebuggerEnv_trace(JSTracer* trc, JSObject* obj);
+static void DebuggerObject_trace(JSTracer* trc, JSObject* obj);
+static void DebuggerScript_trace(JSTracer* trc, JSObject* obj);
+static void DebuggerSource_trace(JSTracer* trc, JSObject* obj);
 
 enum {
     JSSLOT_DEBUGFRAME_OWNER,
@@ -71,35 +75,117 @@ enum {
     JSSLOT_DEBUGFRAME_COUNT
 };
 
-extern const Class DebuggerArguments_class;
+static const ClassOps DebuggerFrame_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    DebuggerFrame_finalize,
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    nullptr,    /* trace   */
+};
+
+static const Class DebuggerFrame_class = {
+    "Frame",
+    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
+    &DebuggerFrame_classOps
+};
 
 enum {
     JSSLOT_DEBUGARGUMENTS_FRAME,
     JSSLOT_DEBUGARGUMENTS_COUNT
 };
 
-extern const Class DebuggerEnv_class;
+static const Class DebuggerArguments_class = {
+    "Arguments",
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)
+};
 
 enum {
     JSSLOT_DEBUGENV_OWNER,
     JSSLOT_DEBUGENV_COUNT
 };
 
-extern const Class DebuggerObject_class;
+static const ClassOps DebuggerEnv_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    nullptr,    /* finalize    */
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    DebuggerEnv_trace
+};
+
+static const Class DebuggerEnv_class = {
+    "Environment",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
+    &DebuggerEnv_classOps
+};
 
 enum {
     JSSLOT_DEBUGOBJECT_OWNER,
     JSSLOT_DEBUGOBJECT_COUNT
 };
 
-extern const Class DebuggerScript_class;
+static const ClassOps DebuggerObject_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    nullptr,    /* finalize    */
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    DebuggerObject_trace
+};
+
+static const Class DebuggerObject_class = {
+    "Object",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
+    &DebuggerObject_classOps
+};
 
 enum {
     JSSLOT_DEBUGSCRIPT_OWNER,
     JSSLOT_DEBUGSCRIPT_COUNT
 };
 
-extern const Class DebuggerSource_class;
+static const ClassOps DebuggerScript_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    nullptr,    /* finalize    */
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    DebuggerScript_trace
+};
+
+static const Class DebuggerScript_class = {
+    "Script",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
+    &DebuggerScript_classOps
+};
 
 enum {
     JSSLOT_DEBUGSOURCE_OWNER,
@@ -107,10 +193,27 @@ enum {
     JSSLOT_DEBUGSOURCE_COUNT
 };
 
-void DebuggerObject_trace(JSTracer* trc, JSObject* obj);
-void DebuggerEnv_trace(JSTracer* trc, JSObject* obj);
-void DebuggerScript_trace(JSTracer* trc, JSObject* obj);
-void DebuggerSource_trace(JSTracer* trc, JSObject* obj);
+static const ClassOps DebuggerSource_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    nullptr,    /* finalize    */
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    DebuggerSource_trace
+};
+
+static const Class DebuggerSource_class = {
+    "Source",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
+    &DebuggerSource_classOps
+};
 
 
 /*** Utils ***************************************************************************************/
@@ -2657,7 +2760,7 @@ Debugger::markIncomingCrossCompartmentEdges(JSTracer* trc)
     MOZ_ASSERT(state == gc::MARK_ROOTS || state == gc::COMPACT);
 
     for (Debugger* dbg : rt->debuggerList) {
-        Zone* zone = dbg->object->zone();
+        Zone* zone = MaybeForwarded(dbg->object.get())->zone();
         if ((state == gc::MARK_ROOTS && !zone->isCollecting()) ||
             (state == gc::COMPACT && !zone->isGCCompacting()))
         {
@@ -2882,16 +2985,26 @@ Debugger::finalize(FreeOp* fop, JSObject* obj)
     fop->delete_(dbg);
 }
 
-const Class Debugger::jsclass = {
+const ClassOps Debugger::classOps_ = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    Debugger::finalize,
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    Debugger::traceObject
+};
+
+const Class Debugger::class_ = {
     "Debugger",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, Debugger::finalize,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    Debugger::traceObject
+    &Debugger::classOps_
 };
 
 /* static */ Debugger*
@@ -2900,7 +3013,7 @@ Debugger::fromThisValue(JSContext* cx, const CallArgs& args, const char* fnname)
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
         return nullptr;
-    if (thisobj->getClass() != &Debugger::jsclass) {
+    if (thisobj->getClass() != &Debugger::class_) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              "Debugger", fnname, thisobj->getClass()->name);
         return nullptr;
@@ -3487,13 +3600,13 @@ Debugger::construct(JSContext* cx, unsigned argc, Value* vp)
     if (!GetProperty(cx, callee, callee, cx->names().prototype, &v))
         return false;
     RootedNativeObject proto(cx, &v.toObject().as());
-    MOZ_ASSERT(proto->getClass() == &Debugger::jsclass);
+    MOZ_ASSERT(proto->getClass() == &Debugger::class_);
     /*
      * Make the new Debugger object. Each one has a reference to
      * Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
      * rest of the reserved slots are for hooks; they default to undefined.
      */
-    RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::jsclass, proto));
+    RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto));
     if (!obj)
         return false;
     for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
@@ -4927,33 +5040,23 @@ void
 DebuggerScript_trace(JSTracer* trc, JSObject* obj)
 {
     /* This comes from a private pointer, so no barrier needed. */
-    DebuggerScriptReferent referent = GetScriptReferent(obj);
-    if (referent.is()) {
-        if (JSScript* script = referent.as()) {
+    gc::Cell* cell = GetScriptReferentCell(obj);
+    if (cell) {
+        if (cell->getTraceKind() == JS::TraceKind::Script) {
+            JSScript* script = static_cast(cell);
             TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &script,
                                                        "Debugger.Script script referent");
             obj->as().setPrivateUnbarriered(script);
+        } else {
+            JSObject* wasm = static_cast(cell);
+            TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &wasm,
+                                                       "Debugger.Script wasm referent");
+            MOZ_ASSERT(wasm->is());
+            obj->as().setPrivateUnbarriered(wasm);
         }
-    } else {
-        JSObject* wasm = referent.as();
-        TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &wasm,
-                                                   "Debugger.Script wasm referent");
-        obj->as().setPrivateUnbarriered(wasm);
     }
 }
 
-const Class DebuggerScript_class = {
-    "Script",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    DebuggerScript_trace
-};
-
 class DebuggerScriptSetPrivateMatcher
 {
     NativeObject* obj_;
@@ -6215,18 +6318,6 @@ DebuggerSource_trace(JSTracer* trc, JSObject* obj)
     }
 }
 
-const Class DebuggerSource_class = {
-    "Source",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    DebuggerSource_trace
-};
-
 class SetDebuggerSourcePrivateMatcher
 {
     NativeObject* obj_;
@@ -6778,12 +6869,6 @@ DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
     DebuggerFrame_freeScriptFrameIterData(fop, obj);
 }
 
-const Class DebuggerFrame_class = {
-    "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, DebuggerFrame_finalize
-};
-
 static NativeObject*
 CheckThisFrame(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
 {
@@ -7007,10 +7092,6 @@ DebuggerFrame_getOlder(JSContext* cx, unsigned argc, Value* vp)
     return true;
 }
 
-const Class DebuggerArguments_class = {
-    "Arguments", JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)
-};
-
 /* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */
 static bool
 DebuggerArguments_getArg(JSContext* cx, unsigned argc, Value* vp)
@@ -7547,18 +7628,6 @@ DebuggerObject_trace(JSTracer* trc, JSObject* obj)
     }
 }
 
-const Class DebuggerObject_class = {
-    "Object",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    DebuggerObject_trace
-};
-
 static NativeObject*
 DebuggerObject_checkThis(JSContext* cx, const CallArgs& args, const char* fnname)
 {
@@ -8789,18 +8858,6 @@ DebuggerEnv_trace(JSTracer* trc, JSObject* obj)
     }
 }
 
-const Class DebuggerEnv_class = {
-    "Environment",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    DebuggerEnv_trace
-};
-
 static NativeObject*
 DebuggerEnv_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
                       bool requireDebuggee = true)
@@ -9285,7 +9342,7 @@ JS_DefineDebuggerObject(JSContext* cx, HandleObject obj)
     if (!objProto)
         return false;
     debugProto = InitClass(cx, obj,
-                           objProto, &Debugger::jsclass, Debugger::construct,
+                           objProto, &Debugger::class_, Debugger::construct,
                            1, Debugger::properties, Debugger::methods, nullptr,
                            Debugger::static_methods, debugCtor.address());
     if (!debugProto)
@@ -9389,7 +9446,7 @@ JS::dbg::IsDebugger(JSObject& obj)
 {
     JSObject* unwrapped = CheckedUnwrap(&obj);
     return unwrapped &&
-           js::GetObjectClass(unwrapped) == &Debugger::jsclass &&
+           js::GetObjectClass(unwrapped) == &Debugger::class_ &&
            js::Debugger::fromJSObject(unwrapped) != nullptr;
 }
 
diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h
index d32db0d71e..326d158538 100644
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -560,7 +560,8 @@ class Debugger : private mozilla::LinkedListElement
     static void finalize(FreeOp* fop, JSObject* obj);
     void markCrossCompartmentEdges(JSTracer* tracer);
 
-    static const Class jsclass;
+    static const ClassOps classOps_;
+    static const Class class_;
 
     static bool getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
     static bool setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp
index a75254f9e1..6a461e3cf9 100644
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -293,7 +293,7 @@ GlobalObject*
 GlobalObject::createInternal(JSContext* cx, const Class* clasp)
 {
     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
-    MOZ_ASSERT(clasp->trace == JS_GlobalObjectTraceHook);
+    MOZ_ASSERT(clasp->isTrace(JS_GlobalObjectTraceHook));
 
     JSObject* obj = NewObjectWithGivenProto(cx, clasp, nullptr, SingletonObject);
     if (!obj)
@@ -628,11 +628,22 @@ GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj)
     fop->delete_((GlobalObject::DebuggerVector*) obj->as().getPrivate());
 }
 
+static const ClassOps
+GlobalDebuggees_classOps = {
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    GlobalDebuggees_finalize
+};
+
 static const Class
 GlobalDebuggees_class = {
     "GlobalDebuggee", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, GlobalDebuggees_finalize
+    &GlobalDebuggees_classOps
 };
 
 GlobalObject::DebuggerVector*
diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp
index 2694efb133..4928d6ff22 100644
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -187,14 +187,18 @@ js::CancelOffThreadIonCompile(JSCompartment* compartment, JSScript* script)
     }
 }
 
-static const JSClass parseTaskGlobalClass = {
-    "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps parseTaskGlobalClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
+static const JSClass parseTaskGlobalClass = {
+    "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
+    &parseTaskGlobalClassOps
+};
+
 ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
                      JSContext* initCx, const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h
index 9fb98b8472..d95bb95a0f 100644
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -576,8 +576,8 @@ static MOZ_ALWAYS_INLINE bool
 InitElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval, HandleValue val)
 {
     MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
-    MOZ_ASSERT(!obj->getClass()->getProperty);
-    MOZ_ASSERT(!obj->getClass()->setProperty);
+    MOZ_ASSERT(!obj->getClass()->getGetProperty());
+    MOZ_ASSERT(!obj->getClass()->getSetProperty());
 
     RootedId id(cx);
     if (!ToPropertyKey(cx, idval, &id))
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index d128aa5895..576ed3e5a3 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -553,7 +553,7 @@ InternalConstruct(JSContext* cx, const AnyConstructArgs& args)
 {
     MOZ_ASSERT(args.array() + args.length() + 1 == args.end(),
                "must pass constructing arguments to a construction attempt");
-    MOZ_ASSERT(!JSFunction::class_.construct);
+    MOZ_ASSERT(!JSFunction::class_.getConstruct());
 
     // Callers are responsible for enforcing these preconditions.
     MOZ_ASSERT(IsConstructor(args.calleev()),
@@ -780,10 +780,9 @@ js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
 {
     const Class* clasp = obj->getClass();
     RootedValue local(cx, v);
-    if (clasp->hasInstance)
-        return clasp->hasInstance(cx, obj, &local, bp);
+    if (JSHasInstanceOp hasInstance = clasp->getHasInstance())
+        return hasInstance(cx, obj, &local, bp);
 
-// XXX RM FIXME 2018-12-10 try to update this
     RootedValue val(cx, ObjectValue(*obj));
     ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                         JSDVG_SEARCH_STACK, val, nullptr);
diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp
index ea1190149b..a3946de93c 100644
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -329,7 +329,7 @@ StatsCompartmentCallback(JSRuntime* rt, void* data, JSCompartment* compartment)
         MOZ_CRASH("oom");
     rtStats->initExtraCompartmentStats(compartment, &cStats);
 
-    compartment->compartmentStats = &cStats;
+    compartment->setCompartmentStats(&cStats);
 
     // Measure the compartment object itself, and things hanging off it.
     compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
@@ -367,12 +367,6 @@ StatsArenaCallback(JSRuntime* rt, void* data, gc::Arena* arena,
     rtStats->currZoneStats->unusedGCThings.addToKind(traceKind, allocationSpace);
 }
 
-static CompartmentStats*
-GetCompartmentStats(JSCompartment* comp)
-{
-    return static_cast(comp->compartmentStats);
-}
-
 // FineGrained is used for normal memory reporting.  CoarseGrained is used by
 // AddSizeOfTab(), which aggregates all the measurements into a handful of
 // high-level numbers, which means that fine-grained reporting would be a waste
@@ -383,16 +377,15 @@ enum Granularity {
 };
 
 static void
-AddClassInfo(Granularity granularity, CompartmentStats* cStats, const char* className,
+AddClassInfo(Granularity granularity, CompartmentStats& cStats, const char* className,
              JS::ClassInfo& info)
 {
     if (granularity == FineGrained) {
         if (!className)
             className = "";
-        CompartmentStats::ClassesHashMap::AddPtr p =
-            cStats->allClasses->lookupForAdd(className);
+        CompartmentStats::ClassesHashMap::AddPtr p = cStats.allClasses->lookupForAdd(className);
         if (!p) {
-            bool ok = cStats->allClasses->add(p, className, info);
+            bool ok = cStats.allClasses->add(p, className, info);
             // Ignore failure -- we just won't record the
             // object/shape/base-shape as notable.
             (void)ok;
@@ -416,12 +409,12 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin
     switch (traceKind) {
       case JS::TraceKind::Object: {
         JSObject* obj = static_cast(thing);
-        CompartmentStats* cStats = GetCompartmentStats(obj->compartment());
+        CompartmentStats& cStats = obj->compartment()->compartmentStats();
         JS::ClassInfo info;        // This zeroes all the sizes.
         info.objectsGCHeap += thingSize;
         obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
 
-        cStats->classInfo.add(info);
+        cStats.classInfo.add(info);
 
         const Class* clasp = obj->getClass();
         const char* className = clasp->name;
@@ -430,20 +423,20 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin
         if (ObjectPrivateVisitor* opv = closure->opv) {
             nsISupports* iface;
             if (opv->getISupports_(obj, &iface) && iface)
-                cStats->objectsPrivate += opv->sizeOfIncludingThis(iface);
+                cStats.objectsPrivate += opv->sizeOfIncludingThis(iface);
         }
         break;
       }
 
       case JS::TraceKind::Script: {
         JSScript* script = static_cast(thing);
-        CompartmentStats* cStats = GetCompartmentStats(script->compartment());
-        cStats->scriptsGCHeap += thingSize;
-        cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
-        cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
-        jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData,
-                                   &cStats->baselineStubsFallback);
-        cStats->ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
+        CompartmentStats& cStats = script->compartment()->compartmentStats();
+        cStats.scriptsGCHeap += thingSize;
+        cStats.scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
+        cStats.typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
+        jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats.baselineData,
+                                   &cStats.baselineStubsFallback);
+        cStats.ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
 
         ScriptSource* ss = script->scriptSource();
         SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss);
@@ -514,17 +507,48 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin
 
       case JS::TraceKind::BaseShape: {
         BaseShape* base = static_cast(thing);
-        CompartmentStats* cStats = GetCompartmentStats(base->compartment());
+        CompartmentStats& cStats = base->compartment()->compartmentStats();
 
         JS::ClassInfo info;        // This zeroes all the sizes.
         info.shapesGCHeapBase += thingSize;
         // No malloc-heap measurements.
 
-        cStats->classInfo.add(info);
+        cStats.classInfo.add(info);
 
-        const Class* clasp = base->clasp();
-        const char* className = clasp->name;
-        AddClassInfo(granularity, cStats, className, info);
+        // XXX: This code is currently disabled because it occasionally causes
+        // crashes (bug 1132502 and bug 1243529). The best theory as to why is
+        // as follows.
+        //
+        // - XPCNativeScriptableShared have heap-allocated js::Class instances.
+        //
+        // - Once an XPCNativeScriptableShared is destroyed, its js::Class is
+        //   freed, but we can still have a BaseShape with a clasp_ pointer
+        //   that points to the freed js::Class.
+        //
+        // - This dangling pointer isn't used in normal execution, because the
+        //   BaseShape is unreachable.
+        //
+        // - However, memory reporting inspects all GC cells, reachable or not,
+        //   so we trace the dangling pointer and crash.
+        //
+        // One solution would be to mark BaseShapes whose js::Class is
+        // heap-allocated, and skip this code just for them. However, that's a
+        // non-trivial change, and heap-allocated js::Class instances are
+        // likely to go away soon.
+        //
+        // So for now we just skip this code for all BaseShapes. The
+        // consequence is that all BaseShapes will show up in about:memory
+        // under "class()" sub-trees, instead of the more
+        // appropriate, class-specific "class(Foo)" sub-tree. But BaseShapes
+        // typically don't take up that much memory so this isn't a big deal.
+        //
+        // XXX: once bug 1265271 is done this code should be re-enabled.
+        //
+        if (0) {
+            const Class* clasp = base->clasp();
+            const char* className = clasp->name;
+            AddClassInfo(granularity, cStats, className, info);
+        }
         break;
       }
 
@@ -543,19 +567,23 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin
 
       case JS::TraceKind::Shape: {
         Shape* shape = static_cast(thing);
-        CompartmentStats* cStats = GetCompartmentStats(shape->compartment());
+        CompartmentStats& cStats = shape->compartment()->compartmentStats();
         JS::ClassInfo info;        // This zeroes all the sizes.
         if (shape->inDictionary())
             info.shapesGCHeapDict += thingSize;
         else
             info.shapesGCHeapTree += thingSize;
         shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
-        cStats->classInfo.add(info);
+        cStats.classInfo.add(info);
 
-        const BaseShape* base = shape->base();
-        const Class* clasp = base->clasp();
-        const char* className = clasp->name;
-        AddClassInfo(granularity, cStats, className, info);
+        // XXX: once bug 1265271 is done, occur, this code should be
+        // re-enabled. (See the big comment on the BaseShape case above.)
+        if (0) {
+            const BaseShape* base = shape->base();
+            const Class* clasp = base->clasp();
+            const char* className = clasp->name;
+            AddClassInfo(granularity, cStats, className, info);
+        }
         break;
       }
 
@@ -779,7 +807,7 @@ CollectRuntimeStatsHelper(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVis
 #endif
 
     for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next())
-        comp->compartmentStats = nullptr;
+        comp->nullCompartmentStats();
 
     size_t numDirtyChunks =
         (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize;
@@ -884,7 +912,7 @@ AddSizeOfTab(JSRuntime* rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP
         rtStats.cTotals.addSizes(rtStats.compartmentStatsVector[i]);
 
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
-        comp->compartmentStats = nullptr;
+        comp->nullCompartmentStats();
 
     rtStats.zTotals.addToTabSizes(sizes);
     rtStats.cTotals.addToTabSizes(sizes);
diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h
index 23aeb3d7bb..177256834f 100644
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -394,7 +394,7 @@ CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleS
     *recursedp = false;
 
     bool resolved = false;
-    if (!obj->getClass()->resolve(cx, obj, id, &resolved))
+    if (!obj->getClass()->getResolve()(cx, obj, id, &resolved))
         return false;
 
     if (!resolved)
@@ -402,8 +402,8 @@ CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleS
 
     // Assert the mayResolve hook, if there is one, returns true for this
     // property.
-    MOZ_ASSERT_IF(obj->getClass()->mayResolve,
-                  obj->getClass()->mayResolve(cx->names(), id, obj));
+    MOZ_ASSERT_IF(obj->getClass()->getMayResolve(),
+                  obj->getClass()->getMayResolve()(cx->names(), id, obj));
 
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
         MarkDenseOrTypedArrayElementFound(propp);
@@ -421,17 +421,17 @@ ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObjec
 {
     MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
 
-    if (!clasp->resolve) {
+    if (!clasp->getResolve()) {
         // Sanity check: we should only have a mayResolve hook if we have a
         // resolve hook.
-        MOZ_ASSERT(!clasp->mayResolve, "Class with mayResolve hook but no resolve hook");
+        MOZ_ASSERT(!clasp->getMayResolve(), "Class with mayResolve hook but no resolve hook");
         return false;
     }
 
-    if (clasp->mayResolve) {
+    if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
         // Tell the analysis our mayResolve hooks won't trigger GC.
         JS::AutoSuppressGCAnalysis nogc;
-        if (!clasp->mayResolve(names, id, maybeObj))
+        if (!mayResolve(names, id, maybeObj))
             return false;
     }
 
@@ -477,8 +477,7 @@ LookupOwnPropertyInline(ExclusiveContext* cx,
     }
 
     // id was not found in obj. Try obj's resolve hook, if any.
-    if (obj->getClass()->resolve)
-    {
+    if (obj->getClass()->getResolve()) {
         if (!cx->shouldBeJSContext() || !allowGC)
             return false;
 
diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp
index 9e42e7ea97..a08dbe6ebe 100644
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1000,7 +1000,7 @@ static inline bool
 CallAddPropertyHook(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape,
                     HandleValue value)
 {
-    if (JSAddPropertyOp addProperty = obj->getClass()->addProperty) {
+    if (JSAddPropertyOp addProperty = obj->getClass()->getAddProperty()) {
         if (!cx->shouldBeJSContext())
             return false;
 
@@ -1026,7 +1026,7 @@ CallAddPropertyHookDense(ExclusiveContext* cx, HandleNativeObject obj, uint32_t
         return true;
     }
 
-    if (JSAddPropertyOp addProperty = obj->getClass()->addProperty) {
+    if (JSAddPropertyOp addProperty = obj->getClass()->getAddProperty()) {
         if (!cx->shouldBeJSContext())
             return false;
 
@@ -1873,7 +1873,7 @@ GetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
     // Non-standard extension: Call the getProperty hook. If it sets vp to a
     // value other than undefined, we're done. If not, fall through to the
     // warning/error checks below.
-    if (JSGetterOp getProperty = obj->getClass()->getProperty) {
+    if (JSGetterOp getProperty = obj->getClass()->getGetProperty()) {
         if (!CallJSGetterOp(cx, getProperty, obj, id, vp))
             return false;
 
@@ -2180,8 +2180,8 @@ js::SetPropertyByDefining(JSContext* cx, HandleId id, HandleValue v, HandleValue
         existing
         ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT
         : JSPROP_ENUMERATE;
-    JSGetterOp getter = clasp->getProperty;
-    JSSetterOp setter = clasp->setProperty;
+    JSGetterOp getter = clasp->getGetProperty();
+    JSSetterOp setter = clasp->getSetProperty();
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
     if (!DefineProperty(cx, receiver, id, v, getter, setter, attrs, result))
@@ -2451,7 +2451,7 @@ js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
     if (!shape) {
         // If no property call the class's delProperty hook, passing succeeded
         // as the result parameter. This always succeeds when there is no hook.
-        return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, result);
+        return CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result);
     }
 
     cx->runtime()->gc.poke();
@@ -2460,7 +2460,7 @@ js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
     if (GetShapeAttributes(obj, shape) & JSPROP_PERMANENT)
         return result.failCantDelete();
 
-    if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, result))
+    if (!CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result))
         return false;
     if (!result)
         return true;
diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h
index 0239a3d264..79c755a331 100644
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1280,10 +1280,8 @@ inline void
 NativeObject::privateWriteBarrierPre(void** oldval)
 {
     JS::shadow::Zone* shadowZone = this->shadowZoneFromAnyThread();
-    if (shadowZone->needsIncrementalBarrier()) {
-        if (*oldval && getClass()->trace)
-            getClass()->trace(shadowZone->barrierTracer(), this);
-    }
+    if (shadowZone->needsIncrementalBarrier() && *oldval && getClass()->hasTrace())
+        getClass()->doTrace(shadowZone->barrierTracer(), this);
 }
 
 #ifdef DEBUG
diff --git a/js/src/vm/PIC.cpp b/js/src/vm/PIC.cpp
index 92572aab6f..86da0b9e79 100644
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -294,8 +294,7 @@ ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
         chain->mark(trc);
 }
 
-const Class ForOfPIC::jsclass = {
-    "ForOfPIC", JSCLASS_HAS_PRIVATE,
+static const ClassOps ForOfPICClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, ForOfPIC_finalize,
     nullptr,              /* call        */
@@ -304,11 +303,16 @@ const Class ForOfPIC::jsclass = {
     ForOfPIC_traceObject
 };
 
+const Class ForOfPIC::class_ = {
+    "ForOfPIC", JSCLASS_HAS_PRIVATE,
+    &ForOfPICClassOps
+};
+
 /* static */ NativeObject*
 js::ForOfPIC::createForOfPICObject(JSContext* cx, Handle global)
 {
     assertSameCompartment(cx, global);
-    NativeObject* obj = NewNativeObjectWithGivenProto(cx, &ForOfPIC::jsclass, nullptr);
+    NativeObject* obj = NewNativeObjectWithGivenProto(cx, &ForOfPIC::class_, nullptr);
     if (!obj)
         return nullptr;
     ForOfPIC::Chain* chain = cx->new_();
diff --git a/js/src/vm/PIC.h b/js/src/vm/PIC.h
index 371bb62538..c50d68d8ec 100644
--- a/js/src/vm/PIC.h
+++ b/js/src/vm/PIC.h
@@ -253,12 +253,12 @@ struct ForOfPIC
     };
 
     // Class for object that holds ForOfPIC chain.
-    static const Class jsclass;
+    static const Class class_;
 
     static NativeObject* createForOfPICObject(JSContext* cx, Handle global);
 
     static inline Chain* fromJSObject(NativeObject* obj) {
-        MOZ_ASSERT(js::GetObjectClass(obj) == &ForOfPIC::jsclass);
+        MOZ_ASSERT(js::GetObjectClass(obj) == &ForOfPIC::class_);
         return (ForOfPIC::Chain*) obj->getPrivate();
     }
     static inline Chain* getOrCreate(JSContext* cx) {
diff --git a/js/src/vm/ProxyObject.h b/js/src/vm/ProxyObject.h
index 4064e31207..b9da0f14c5 100644
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -91,8 +91,8 @@ class ProxyObject : public JSObject
         // Proxy classes are not allowed to have call or construct hooks directly. Their
         // callability is instead decided by handler()->isCallable().
         return clasp->isProxy() &&
-               clasp->trace == proxy_Trace &&
-               !clasp->call && !clasp->construct;
+               clasp->isTrace(proxy_Trace) &&
+               !clasp->getCall() && !clasp->getConstruct();
     }
 
   public:
diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
index d10e4fb24b..9b7f42c5e4 100644
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -185,6 +185,21 @@ RegExpObject::trace(JSTracer* trc, JSObject* obj)
     }
 }
 
+static const ClassOps RegExpObjectClassOps = {
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    nullptr, /* finalize */
+    nullptr, /* call */
+    nullptr, /* hasInstance */
+    nullptr, /* construct */
+    RegExpObject::trace,
+};
+
 static const ClassSpec RegExpObjectClassSpec = {
     GenericCreateConstructor,
     CreateRegExpPrototype,
@@ -199,18 +214,7 @@ const Class RegExpObject::class_ = {
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    RegExpObject::trace,
+    &RegExpObjectClassOps,
     &RegExpObjectClassSpec
 };
 
diff --git a/js/src/vm/RegExpStatics.cpp b/js/src/vm/RegExpStatics.cpp
index dd378498ee..8404331a8c 100644
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -34,9 +34,7 @@ resc_trace(JSTracer* trc, JSObject* obj)
         static_cast(pdata)->mark(trc);
 }
 
-const Class RegExpStaticsObject::class_ = {
-    "RegExpStatics",
-    JSCLASS_HAS_PRIVATE,
+static const ClassOps RegExpStaticsObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -51,6 +49,12 @@ const Class RegExpStaticsObject::class_ = {
     resc_trace
 };
 
+const Class RegExpStaticsObject::class_ = {
+    "RegExpStatics",
+    JSCLASS_HAS_PRIVATE,
+    &RegExpStaticsObjectClassOps
+};
+
 RegExpStaticsObject*
 RegExpStatics::create(ExclusiveContext* cx, Handle parent)
 {
diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp
index 4e8ecd68db..4113c577fb 100644
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -305,8 +305,8 @@ void
 SPSProfiler::pop()
 {
     MOZ_ASSERT(installed());
+    MOZ_ASSERT(*size_ > 0);
     (*size_)--;
-    MOZ_ASSERT(*(int*)size_ >= 0);
 }
 
 /*
@@ -364,6 +364,57 @@ SPSProfiler::allocProfileString(JSScript* script, JSFunction* maybeFun)
     return cstr;
 }
 
+void
+SPSProfiler::trace(JSTracer* trc)
+{
+    if (stack_) {
+        size_t limit = Min(*size_, max_);
+        for (size_t i = 0; i < limit; i++)
+            stack_[i].trace(trc);
+    }
+}
+
+void
+SPSProfiler::fixupStringsMapAfterMovingGC()
+{
+    if (!strings.initialized())
+        return;
+
+    for (ProfileStringMap::Enum e(strings); !e.empty(); e.popFront()) {
+        JSScript* script = e.front().key();
+        if (IsForwarded(script)) {
+            script = Forwarded(script);
+            e.rekeyFront(script);
+        }
+    }
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+void
+SPSProfiler::checkStringsMapAfterMovingGC()
+{
+    if (!strings.initialized())
+        return;
+
+    for (auto r = strings.all(); !r.empty(); r.popFront()) {
+        JSScript* script = r.front().key();
+        CheckGCThingAfterMovingGC(script);
+        auto ptr = strings.lookup(script);
+        MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
+    }
+}
+#endif
+
+void
+ProfileEntry::trace(JSTracer* trc)
+{
+    if (isJs()) {
+        JSScript* s = script();
+        TraceNullableRoot(trc, &s, "ProfileEntry script");
+        spOrScript = s;
+    }
+}
+
 SPSEntryMarker::SPSEntryMarker(JSRuntime* rt,
                                JSScript* script
                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h
index a578f41b04..31dea3d84c 100644
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -203,6 +203,12 @@ class SPSProfiler
     uint32_t* addressOfEnabled() {
         return &enabled_;
     }
+
+    void trace(JSTracer* trc);
+    void fixupStringsMapAfterMovingGC();
+#ifdef JSGC_HASH_TABLE_CHECKS
+    void checkStringsMapAfterMovingGC();
+#endif
 };
 
 /*
diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp
index db8b5b2b1e..0510dccb3c 100644
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -287,6 +287,21 @@ SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject
     return FreezeObject(cx, proto);
 }
 
+static const ClassOps SavedFrameClassOps = {
+    nullptr,                    // addProperty
+    nullptr,                    // delProperty
+    nullptr,                    // getProperty
+    nullptr,                    // setProperty
+    nullptr,                    // enumerate
+    nullptr,                    // resolve
+    nullptr,                    // mayResolve
+    SavedFrame::finalize,       // finalize
+    nullptr,                    // call
+    nullptr,                    // hasInstance
+    nullptr,                    // construct
+    nullptr,                    // trace
+};
+
 const ClassSpec SavedFrame::classSpec_ = {
     GenericCreateConstructor,
     GenericCreatePrototype,
@@ -304,18 +319,7 @@ const ClassSpec SavedFrame::classSpec_ = {
     JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame) |
     JSCLASS_IS_ANONYMOUS,
-    nullptr,                    // addProperty
-    nullptr,                    // delProperty
-    nullptr,                    // getProperty
-    nullptr,                    // setProperty
-    nullptr,                    // enumerate
-    nullptr,                    // resolve
-    nullptr,                    // mayResolve
-    SavedFrame::finalize,       // finalize
-    nullptr,                    // call
-    nullptr,                    // hasInstance
-    nullptr,                    // construct
-    nullptr,                    // trace
+    &SavedFrameClassOps,
     &SavedFrame::classSpec_
 };
 
@@ -885,6 +889,19 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
     return true;
 }
 
+JS_PUBLIC_API(bool)
+IsSavedFrame(JSObject* obj)
+{
+    if (!obj)
+        return false;
+
+    JSObject* unwrapped = CheckedUnwrap(obj);
+    if (!unwrapped)
+        return false;
+
+    return js::SavedFrame::isSavedFrameAndNotProto(*unwrapped);
+}
+
 } /* namespace JS */
 
 namespace js {
diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp
index 352cd8b000..8cff9f711c 100644
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -458,18 +458,7 @@ const Class ModuleEnvironmentObject::class_ = {
     "ModuleEnvironmentObject",
     JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
-    nullptr,        /* addProperty */
-    nullptr,        /* delProperty */
-    nullptr,        /* getProperty */
-    nullptr,        /* setProperty */
-    nullptr,        /* enumerate   */
-    nullptr,        /* resolve     */
-    nullptr,        /* mayResolve  */
-    nullptr,        /* finalize    */
-    nullptr,        /* call        */
-    nullptr,        /* hasInstance */
-    nullptr,        /* construct   */
-    nullptr,        /* trace       */
+    JS_NULL_CLASS_OPS,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     &ModuleEnvironmentObject::objectOps_
@@ -698,8 +687,8 @@ DeclEnvObject::createTemplateObject(JSContext* cx, HandleFunction fun, NewObject
     const Class* clasp = obj->getClass();
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
 
-    JSGetterOp getter = clasp->getProperty;
-    JSSetterOp setter = clasp->setProperty;
+    JSGetterOp getter = clasp->getGetProperty();
+    JSSetterOp setter = clasp->getSetProperty();
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
 
@@ -886,18 +875,7 @@ const Class DynamicWithObject::class_ = {
     "With",
     JSCLASS_HAS_RESERVED_SLOTS(DynamicWithObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    JS_NULL_CLASS_OPS,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     &DynamicWithObjectObjectOps
@@ -1128,18 +1106,7 @@ const Class ClonedBlockObject::class_ = {
     "Block",
     JSCLASS_HAS_RESERVED_SLOTS(ClonedBlockObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    JS_NULL_CLASS_OPS,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     JS_NULL_OBJECT_OPS
@@ -1401,18 +1368,7 @@ const Class RuntimeLexicalErrorObject::class_ = {
     "RuntimeLexicalError",
     JSCLASS_HAS_RESERVED_SLOTS(RuntimeLexicalErrorObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    JS_NULL_CLASS_OPS,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     &RuntimeLexicalErrorObjectObjectOps
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index 02d9b9f4b8..a4afa839a5 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2644,14 +2644,18 @@ JSRuntime::createSelfHostingGlobal(JSContext* cx)
     if (!compartment)
         return nullptr;
 
-    static const Class shgClass = {
-        "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
+    static const ClassOps shgClassOps = {
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
+    static const Class shgClass = {
+        "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
+        &shgClassOps
+    };
+
     AutoCompartment ac(cx, compartment);
     Rooted shg(cx, GlobalObject::createInternal(cx, &shgClass));
     if (!shg)
diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp
index 8b88a7e0d7..5403833a48 100644
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -325,13 +325,16 @@ ShapeTable::grow(ExclusiveContext* cx)
 }
 
 void
-ShapeTable::fixupAfterMovingGC()
+ShapeTable::trace(JSTracer* trc)
 {
     for (size_t i = 0; i < capacity(); i++) {
         Entry& entry = getEntry(i);
         Shape* shape = entry.shape();
-        if (shape && IsForwarded(shape))
-            entry.setPreservingCollision(Forwarded(shape));
+        if (shape) {
+            TraceManuallyBarrieredEdge(trc, &shape, "ShapeTable shape");
+            if (shape != entry.shape())
+                entry.setPreservingCollision(shape);
+        }
     }
 }
 
@@ -1286,14 +1289,14 @@ BaseShape::adoptUnowned(UnownedBaseShape* other)
 /* static */ UnownedBaseShape*
 BaseShape::getUnowned(ExclusiveContext* cx, StackBaseShape& base)
 {
-    BaseShapeSet& table = cx->compartment()->baseShapes;
+    auto& table = cx->compartment()->baseShapes;
 
     if (!table.initialized() && !table.init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    DependentAddPtr p(cx, table, base);
+    auto p = MakeDependentAddPtr(cx, table, base);
     if (p)
         return *p;
 
@@ -1324,6 +1327,13 @@ BaseShape::assertConsistency()
 
 void
 BaseShape::traceChildren(JSTracer* trc)
+{
+    traceChildrenSkipShapeTable(trc);
+    traceShapeTable(trc);
+}
+
+void
+BaseShape::traceChildrenSkipShapeTable(JSTracer* trc)
 {
     assertConsistency();
 
@@ -1339,9 +1349,10 @@ BaseShape::traceChildren(JSTracer* trc)
 }
 
 void
-JSCompartment::sweepBaseShapeTable()
+BaseShape::traceShapeTable(JSTracer* trc)
 {
-    baseShapes.sweep();
+    if (hasTable())
+        table().trace(trc);
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
@@ -1352,7 +1363,7 @@ JSCompartment::checkBaseShapeTableAfterMovingGC()
     if (!baseShapes.initialized())
         return;
 
-    for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
+    for (decltype(baseShapes)::Enum e(baseShapes); !e.empty(); e.popFront()) {
         UnownedBaseShape* base = e.front().unbarrieredGet();
         CheckGCThingAfterMovingGC(base);
 
@@ -1420,7 +1431,7 @@ JSCompartment::checkInitialShapesTableAfterMovingGC()
      * initialShapes that points into the nursery, and that the hash table
      * entries are discoverable.
      */
-    for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
+    for (decltype(initialShapes)::Enum e(initialShapes); !e.empty(); e.popFront()) {
         InitialShapeEntry entry = e.front();
         TaggedProto proto = entry.proto.unbarrieredGet();
         Shape* shape = entry.shape.unbarrieredGet();
@@ -1459,16 +1470,15 @@ EmptyShape::getInitialShape(ExclusiveContext* cx, const Class* clasp, TaggedProt
 {
     MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
 
-    InitialShapeSet& table = cx->compartment()->initialShapes;
+    auto& table = cx->compartment()->initialShapes;
 
     if (!table.initialized() && !table.init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    typedef InitialShapeEntry::Lookup Lookup;
-    DependentAddPtr
-        p(cx, table, Lookup(clasp, proto, nfixed, objectFlags));
+    using Lookup = InitialShapeEntry::Lookup;
+    auto p = MakeDependentAddPtr(cx, table, Lookup(clasp, proto, nfixed, objectFlags));
     if (p)
         return p->shape;
 
@@ -1566,19 +1576,13 @@ EmptyShape::insertInitialShape(ExclusiveContext* cx, HandleShape shape, HandleOb
     }
 }
 
-void
-JSCompartment::sweepInitialShapeTable()
-{
-    initialShapes.sweep();
-}
-
 void
 JSCompartment::fixupInitialShapeTable()
 {
     if (!initialShapes.initialized())
         return;
 
-    for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
+    for (decltype(initialShapes)::Enum e(initialShapes); !e.empty(); e.popFront()) {
         // The shape may have been moved, but we can update that in place.
         Shape* shape = e.front().shape.unbarrieredGet();
         if (IsForwarded(shape)) {
diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h
index 4da9dedc2d..7f94d78c72 100644
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -226,7 +226,7 @@ class ShapeTable {
     template
     Entry& search(jsid id);
 
-    void fixupAfterMovingGC();
+    void trace(JSTracer* trc);
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkAfterMovingGC();
 #endif
@@ -452,8 +452,9 @@ class BaseShape : public gc::TenuredCell
     static const JS::TraceKind TraceKind = JS::TraceKind::BaseShape;
 
     void traceChildren(JSTracer* trc);
+    void traceChildrenSkipShapeTable(JSTracer* trc);
 
-    void fixupAfterMovingGC();
+    void fixupAfterMovingGC() {}
 
   private:
     static void staticAsserts() {
@@ -462,6 +463,8 @@ class BaseShape : public gc::TenuredCell
                       "Things inheriting from gc::Cell must have a size that's "
                       "a multiple of gc::CellSize");
     }
+
+    void traceShapeTable(JSTracer* trc);
 };
 
 class UnownedBaseShape : public BaseShape {};
@@ -538,6 +541,7 @@ class Shape : public gc::TenuredCell
     friend struct StackBaseShape;
     friend struct StackShape;
     friend struct JS::ubi::Concrete;
+    friend class js::gc::RelocationOverlay;
 
   protected:
     HeapPtrBaseShape    base_;
diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp
index 18dc11d1f8..079f36ed22 100644
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -346,11 +346,7 @@ const Class SharedArrayBufferObject::protoClass = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer)
 };
 
-const Class SharedArrayBufferObject::class_ = {
-    "SharedArrayBuffer",
-    JSCLASS_DELAY_METADATA_BUILDER |
-    JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
+static const ClassOps SharedArrayBufferObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -363,6 +359,14 @@ const Class SharedArrayBufferObject::class_ = {
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
+};
+
+const Class SharedArrayBufferObject::class_ = {
+    "SharedArrayBuffer",
+    JSCLASS_DELAY_METADATA_BUILDER |
+    JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
+    &SharedArrayBufferObjectClassOps,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT
 };
diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp
index e5a315fbfc..ce9c9dfa72 100644
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -894,6 +894,13 @@ AutoStableStringChars::init(JSContext* cx, JSString* s)
 
     MOZ_ASSERT(state_ == Uninitialized);
 
+    // If the chars are inline then we need to copy them since they may be moved
+    // by a compacting GC.
+    if (baseIsInline(linearString)) {
+        return linearString->hasTwoByteChars() ? copyTwoByteChars(cx, linearString)
+                                               : copyLatin1Chars(cx, linearString);
+    }
+
     if (linearString->hasLatin1Chars()) {
         state_ = Latin1;
         latin1Chars_ = linearString->rawLatin1Chars();
@@ -915,13 +922,31 @@ AutoStableStringChars::initTwoByte(JSContext* cx, JSString* s)
 
     MOZ_ASSERT(state_ == Uninitialized);
 
-    if (linearString->hasTwoByteChars()) {
-        state_ = TwoByte;
-        twoByteChars_ = linearString->rawTwoByteChars();
-        s_ = linearString;
-        return true;
-    }
+    if (linearString->hasLatin1Chars())
+        return copyAndInflateLatin1Chars(cx, linearString);
 
+    // If the chars are inline then we need to copy them since they may be moved
+    // by a compacting GC.
+    if (baseIsInline(linearString))
+        return copyTwoByteChars(cx, linearString);
+
+    state_ = TwoByte;
+    twoByteChars_ = linearString->rawTwoByteChars();
+    s_ = linearString;
+    return true;
+}
+
+bool AutoStableStringChars::baseIsInline(HandleLinearString linearString)
+{
+    JSString* base = linearString;
+    while (base->isDependent())
+        base = base->asDependent().base();
+    return base->isInline();
+}
+
+bool
+AutoStableStringChars::copyAndInflateLatin1Chars(JSContext* cx, HandleLinearString linearString)
+{
     char16_t* chars = cx->pod_malloc(linearString->length() + 1);
     if (!chars)
         return false;
@@ -937,6 +962,42 @@ AutoStableStringChars::initTwoByte(JSContext* cx, JSString* s)
     return true;
 }
 
+bool
+AutoStableStringChars::copyLatin1Chars(JSContext* cx, HandleLinearString linearString)
+{
+    size_t length = linearString->length();
+    JS::Latin1Char* chars = cx->pod_malloc(length + 1);
+    if (!chars)
+        return false;
+
+    PodCopy(chars, linearString->rawLatin1Chars(), length);
+    chars[length] = 0;
+
+    state_ = Latin1;
+    ownsChars_ = true;
+    latin1Chars_ = chars;
+    s_ = linearString;
+    return true;
+}
+
+bool
+AutoStableStringChars::copyTwoByteChars(JSContext* cx, HandleLinearString linearString)
+{
+    size_t length = linearString->length();
+    char16_t* chars = cx->pod_malloc(length + 1);
+    if (!chars)
+        return false;
+
+    PodCopy(chars, linearString->rawTwoByteChars(), length);
+    chars[length] = 0;
+
+    state_ = TwoByte;
+    ownsChars_ = true;
+    twoByteChars_ = chars;
+    s_ = linearString;
+    return true;
+}
+
 #ifdef DEBUG
 void
 JSAtom::dump()
diff --git a/js/src/vm/String.h b/js/src/vm/String.h
index 710a516e73..733708a878 100644
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -316,6 +316,8 @@ class JSString : public js::gc::TenuredCell
     /* Avoid lame compile errors in JSRope::flatten */
     friend class JSRope;
 
+    friend class js::gc::RelocationOverlay;
+
   protected:
     template 
     MOZ_ALWAYS_INLINE
@@ -472,6 +474,8 @@ class JSString : public js::gc::TenuredCell
 
     inline void finalize(js::FreeOp* fop);
 
+    void fixupAfterMovingGC() {}
+
     /* Gets the number of bytes that the chars take on the heap. */
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp
index fd25893aa3..19216ecb78 100644
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2468,7 +2468,7 @@ js::ClassCanHaveExtraProperties(const Class* clasp)
 {
     if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_)
         return false;
-    return clasp->resolve
+    return clasp->getResolve()
         || clasp->getOpsLookupProperty()
         || clasp->getOpsGetProperty()
         || IsTypedArrayClass(clasp);
@@ -3454,7 +3454,7 @@ PreliminaryObjectArray::sweep()
                 obj->setGroup(objectProto->groupRaw());
                 MOZ_ASSERT(obj->is());
                 MOZ_ASSERT(obj->getClass() == objectProto->getClass());
-                MOZ_ASSERT(!obj->getClass()->finalize);
+                MOZ_ASSERT(!obj->getClass()->hasFinalize());
             }
 
             *ptr = nullptr;
diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp
index 8438fd0022..336afda3b5 100644
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -636,25 +636,19 @@ class TypedArrayObjectTemplate : public TypedArrayObject
     }
 
     static bool
-    maybeCreateArrayBuffer(JSContext* cx, uint32_t nelements, HandleObject nonDefaultProto,
+    maybeCreateArrayBuffer(JSContext* cx, uint32_t byteLength, HandleObject nonDefaultProto,
                            MutableHandle buffer)
     {
+        MOZ_ASSERT(byteLength < INT32_MAX);
         static_assert(INLINE_BUFFER_LIMIT % sizeof(NativeType) == 0,
                       "ArrayBuffer inline storage shouldn't waste any space");
 
-        if (!nonDefaultProto && nelements <= INLINE_BUFFER_LIMIT / sizeof(NativeType)) {
+        if (!nonDefaultProto && byteLength <= INLINE_BUFFER_LIMIT) {
             // The array's data can be inline, and the buffer created lazily.
             return true;
         }
 
-        if (nelements >= INT32_MAX / sizeof(NativeType)) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
-                                 JSMSG_NEED_DIET, "size and count");
-            return false;
-        }
-
-        ArrayBufferObject* buf = ArrayBufferObject::create(cx, nelements * sizeof(NativeType),
-                                                           nonDefaultProto);
+        ArrayBufferObject* buf = ArrayBufferObject::create(cx, byteLength, nonDefaultProto);
         if (!buf)
             return false;
 
@@ -669,20 +663,25 @@ class TypedArrayObjectTemplate : public TypedArrayObject
         if (!GetPrototypeForInstance(cx, newTarget, &proto))
             return nullptr;
 
+        if (nelements >= INT32_MAX / BYTES_PER_ELEMENT) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                                 JSMSG_NEED_DIET, "size and count");
+            return nullptr;
+        }
         Rooted buffer(cx);
-        if (!maybeCreateArrayBuffer(cx, nelements, nullptr, &buffer))
+        if (!maybeCreateArrayBuffer(cx, nelements * BYTES_PER_ELEMENT, nullptr, &buffer))
             return nullptr;
 
         return makeInstance(cx, buffer, 0, nelements, proto);
     }
 
     static bool
-    AllocateArrayBuffer(JSContext* cx, HandleValue ctor, uint32_t elementLength,
+    AllocateArrayBuffer(JSContext* cx, HandleValue ctor, uint32_t byteLength,
                         MutableHandle buffer);
 
     static bool
     CloneArrayBufferNoCopy(JSContext* cx, Handle srcBuffer,
-                           bool isWrapped, uint32_t srcByteOffset,
+                           bool isWrapped, uint32_t srcByteOffset, uint32_t srcLength,
                            MutableHandle buffer);
 
     static JSObject*
@@ -734,7 +733,7 @@ struct TypedArrayObject::OfType
 template
 /* static */ bool
 TypedArrayObjectTemplate::AllocateArrayBuffer(JSContext* cx, HandleValue ctor,
-                                                 uint32_t elementLength,
+                                                 uint32_t byteLength,
                                                  MutableHandle buffer)
 {
     // ES 2016 draft Mar 25, 2016 24.1.1.1 step 1 (partially).
@@ -750,8 +749,14 @@ TypedArrayObjectTemplate::AllocateArrayBuffer(JSContext* cx, HandleValue ctor
     if (proto == arrayBufferProto)
         proto = nullptr;
 
+    if (byteLength >= INT32_MAX) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             JSMSG_NEED_DIET, "size and count");
+        return false;
+    }
+
     // ES 2016 draft Mar 25, 2016 24.1.1.1 steps 1 (remaining part), 2-6.
-    if (!maybeCreateArrayBuffer(cx, elementLength, proto, buffer))
+    if (!maybeCreateArrayBuffer(cx, byteLength, proto, buffer))
         return false;
 
     return true;
@@ -817,12 +822,13 @@ GetSpeciesConstructor(JSContext* cx, HandleObject obj, bool isWrapped, MutableHa
     return JS_WrapValue(cx, ctor);
 }
 
-// ES 2016 draft Mar 25, 2016 24.1.1.4.
+// ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 24.1.1.4.
 template
 /* static */ bool
 TypedArrayObjectTemplate::CloneArrayBufferNoCopy(JSContext* cx,
                                                     Handle srcBuffer,
                                                     bool isWrapped, uint32_t srcByteOffset,
+                                                    uint32_t srcLength,
                                                     MutableHandle buffer)
 {
     // Step 1 (skipped).
@@ -838,31 +844,21 @@ TypedArrayObjectTemplate::CloneArrayBufferNoCopy(JSContext* cx,
         return false;
     }
 
-    // Step 3 (skipped).
+    // Steps 3-4 (skipped).
 
-    // Steps 4-5.
-    uint32_t srcLength = srcBuffer->byteLength();
-    MOZ_ASSERT(srcByteOffset <= srcLength);
-
-    // Step 6.
-    uint32_t cloneLength = srcLength - srcByteOffset;
-
-    // Step 7 (skipped).
-
-    // Steps 8.
-    MOZ_ASSERT(cloneLength % BYTES_PER_ELEMENT == 0);
-    if (!AllocateArrayBuffer(cx, cloneCtor, cloneLength / BYTES_PER_ELEMENT, buffer))
+    // Steps 5.
+    if (!AllocateArrayBuffer(cx, cloneCtor, srcLength, buffer))
         return false;
 
-    // Step 9.
+    // Step 6.
     if (srcBuffer->isDetached()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return false;
     }
 
-    // Steps 10-11 (done in caller).
+    // Steps 7-8 (done in caller).
 
-    // Step 12.
+    // Step 9.
     return true;
 }
 
@@ -882,7 +878,7 @@ TypedArrayObjectTemplate::fromArray(JSContext* cx, HandleObject other,
     return fromObject(cx, other, newTarget);
 }
 
-// ES 2016 draft Mar 25, 2016 22.2.4.3.
+// ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 22.2.4.3.
 template
 /* static */ JSObject*
 TypedArrayObjectTemplate::fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped,
@@ -941,14 +937,17 @@ TypedArrayObjectTemplate::fromTypedArray(JSContext* cx, HandleObject other, b
     // Step 14.
     uint32_t srcByteOffset = srcArray->byteOffset();
 
-    // Steps 15-16 (skipped).
-    // Our AllocateArrayBuffer receives elementLength instead of byteLength.
+    // Steps 15-16.
+    uint32_t byteLength = BYTES_PER_ELEMENT * elementLength;
 
     // Steps 8-9, 17.
     Rooted buffer(cx);
     if (ArrayTypeID() == srcType) {
         // Step 17.a.
-        if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, &buffer))
+        uint32_t srcLength = srcArray->byteLength();
+
+        // Step 17.b.
+        if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, srcLength, &buffer))
             return nullptr;
     } else {
         // Step 18.a.
@@ -957,7 +956,7 @@ TypedArrayObjectTemplate::fromTypedArray(JSContext* cx, HandleObject other, b
             return nullptr;
 
         // Step 18.b.
-        if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, &buffer))
+        if (!AllocateArrayBuffer(cx, bufferCtor, byteLength, &buffer))
             return nullptr;
 
         // Step 18.c.
@@ -994,7 +993,12 @@ TypedArrayObjectTemplate::fromObject(JSContext* cx, HandleObject other, Handl
         return nullptr;
     if (!GetPrototypeForInstance(cx, newTarget, &proto))
         return nullptr;
-    if (!maybeCreateArrayBuffer(cx, len, nullptr, &buffer))
+    if (len >= INT32_MAX / BYTES_PER_ELEMENT) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             JSMSG_NEED_DIET, "size and count");
+        return nullptr;
+    }
+    if (!maybeCreateArrayBuffer(cx, len * BYTES_PER_ELEMENT, nullptr, &buffer))
         return nullptr;
 
     Rooted obj(cx, makeInstance(cx, buffer, 0, len, proto));
@@ -1147,18 +1151,7 @@ TypedArrayObject::sharedTypedArrayPrototypeClass = {
     // until we implement @@toStringTag.
     "???",
     JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
-    nullptr,                /* addProperty */
-    nullptr,                /* delProperty */
-    nullptr,                /* getProperty */
-    nullptr,                /* setProperty */
-    nullptr,                /* enumerate */
-    nullptr,                /* resolve */
-    nullptr,                /* mayResolve */
-    nullptr,                /* finalize */
-    nullptr,                /* call */
-    nullptr,                /* hasInstance */
-    nullptr,                /* construct */
-    nullptr,                /* trace */
+    JS_NULL_CLASS_OPS,
     &TypedArrayObjectSharedTypedArrayPrototypeClassSpec
 };
 
@@ -2174,6 +2167,21 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t)
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float)
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
 
+static const ClassOps TypedArrayClassOps = {
+    nullptr,                 /* addProperty */
+    nullptr,                 /* delProperty */
+    nullptr,                 /* getProperty */
+    nullptr,                 /* setProperty */
+    nullptr,                 /* enumerate   */
+    nullptr,                 /* resolve     */
+    nullptr,                 /* mayResolve  */
+    nullptr,                 /* finalize    */
+    nullptr,                 /* call        */
+    nullptr,                 /* hasInstance */
+    nullptr,                 /* construct   */
+    TypedArrayObject::trace, /* trace  */
+};
+
 #define IMPL_TYPED_ARRAY_CLASS_SPEC(_type)                                     \
 {                                                                              \
     _type##Array::createConstructor,                                           \
@@ -2205,18 +2213,7 @@ static const ClassSpec TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType]
     JSCLASS_HAS_PRIVATE |                                                      \
     JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array) |                         \
     JSCLASS_DELAY_METADATA_BUILDER,                                            \
-    nullptr,                 /* addProperty */                                 \
-    nullptr,                 /* delProperty */                                 \
-    nullptr,                 /* getProperty */                                 \
-    nullptr,                 /* setProperty */                                 \
-    nullptr,                 /* enumerate   */                                 \
-    nullptr,                 /* resolve     */                                 \
-    nullptr,                 /* mayResolve  */                                 \
-    nullptr,                 /* finalize    */                                 \
-    nullptr,                 /* call        */                                 \
-    nullptr,                 /* hasInstance */                                 \
-    nullptr,                 /* construct   */                                 \
-    TypedArrayObject::trace, /* trace  */                                      \
+    &TypedArrayClassOps,                                                       \
     &TypedArrayObjectClassSpecs[Scalar::Type::_type]                           \
 }
 
@@ -2274,18 +2271,7 @@ static const ClassSpec TypedArrayObjectProtoClassSpecs[Scalar::MaxTypedArrayView
      */ \
     #_type "ArrayPrototype", \
     JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array), \
-    nullptr, /* addProperty */ \
-    nullptr, /* delProperty */ \
-    nullptr, /* getProperty */ \
-    nullptr, /* setProperty */ \
-    nullptr, /* enumerate */ \
-    nullptr, /* resolve */ \
-    nullptr, /* mayResolve */ \
-    nullptr, /* finalize */ \
-    nullptr, /* call */ \
-    nullptr, /* hasInstance */ \
-    nullptr, /* construct */ \
-    nullptr, /* trace  */ \
+    JS_NULL_CLASS_OPS, \
     &TypedArrayObjectProtoClassSpecs[Scalar::Type::_type] \
 }
 
@@ -2314,11 +2300,7 @@ const Class DataViewObject::protoClass = {
     JSCLASS_HAS_CACHED_PROTO(JSProto_DataView)
 };
 
-const Class DataViewObject::class_ = {
-    "DataView",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
+static const ClassOps DataViewObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -2333,6 +2315,14 @@ const Class DataViewObject::class_ = {
     ArrayBufferViewObject::trace
 };
 
+const Class DataViewObject::class_ = {
+    "DataView",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
+    &DataViewObjectClassOps
+};
+
 const JSFunctionSpec DataViewObject::jsfuncs[] = {
     JS_FN("getInt8",    DataViewObject::fun_getInt8,      1,0),
     JS_FN("getUint8",   DataViewObject::fun_getUint8,     1,0),
diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp
index 3ce57218ef..1b5d0a914c 100644
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -905,6 +905,21 @@ const Class UnboxedExpandoObject::class_ = {
     0
 };
 
+static const ClassOps UnboxedPlainObjectClassOps = {
+    nullptr,        /* addProperty */
+    nullptr,        /* delProperty */
+    nullptr,        /* getProperty */
+    nullptr,        /* setProperty */
+    nullptr,        /* enumerate   */
+    nullptr,        /* resolve     */
+    nullptr,        /* mayResolve  */
+    nullptr,        /* finalize    */
+    nullptr,        /* call        */
+    nullptr,        /* hasInstance */
+    nullptr,        /* construct   */
+    UnboxedPlainObject::trace,
+};
+
 static const ObjectOps UnboxedPlainObjectObjectOps = {
     UnboxedPlainObject::obj_lookupProperty,
     UnboxedPlainObject::obj_defineProperty,
@@ -925,18 +940,7 @@ const Class UnboxedPlainObject::class_ = {
     Class::NON_NATIVE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
     JSCLASS_DELAY_METADATA_BUILDER,
-    nullptr,        /* addProperty */
-    nullptr,        /* delProperty */
-    nullptr,        /* getProperty */
-    nullptr,        /* setProperty */
-    nullptr,        /* enumerate   */
-    nullptr,        /* resolve     */
-    nullptr,        /* mayResolve  */
-    nullptr,        /* finalize    */
-    nullptr,        /* call        */
-    nullptr,        /* hasInstance */
-    nullptr,        /* construct   */
-    UnboxedPlainObject::trace,
+    &UnboxedPlainObjectClassOps,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     &UnboxedPlainObjectObjectOps
@@ -1591,6 +1595,26 @@ UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector&
     return true;
 }
 
+static const ClassOps UnboxedArrayObjectClassOps = {
+    nullptr,        /* addProperty */
+    nullptr,        /* delProperty */
+    nullptr,        /* getProperty */
+    nullptr,        /* setProperty */
+    nullptr,        /* enumerate   */
+    nullptr,        /* resolve     */
+    nullptr,        /* mayResolve  */
+    UnboxedArrayObject::finalize,
+    nullptr,        /* call        */
+    nullptr,        /* hasInstance */
+    nullptr,        /* construct   */
+    UnboxedArrayObject::trace,
+};
+
+static const ClassExtension UnboxedArrayObjectClassExtension = {
+    nullptr,    /* weakmapKeyDelegateOp */
+    UnboxedArrayObject::objectMoved
+};
+
 static const ObjectOps UnboxedArrayObjectObjectOps = {
     UnboxedArrayObject::obj_lookupProperty,
     UnboxedArrayObject::obj_defineProperty,
@@ -1611,24 +1635,9 @@ const Class UnboxedArrayObject::class_ = {
     Class::NON_NATIVE |
     JSCLASS_SKIP_NURSERY_FINALIZE |
     JSCLASS_BACKGROUND_FINALIZE,
-    nullptr,        /* addProperty */
-    nullptr,        /* delProperty */
-    nullptr,        /* getProperty */
-    nullptr,        /* setProperty */
-    nullptr,        /* enumerate   */
-    nullptr,        /* resolve     */
-    nullptr,        /* mayResolve  */
-    UnboxedArrayObject::finalize,
-    nullptr,        /* call        */
-    nullptr,        /* hasInstance */
-    nullptr,        /* construct   */
-    UnboxedArrayObject::trace,
+    &UnboxedArrayObjectClassOps,
     JS_NULL_CLASS_SPEC,
-    {
-        false,      /* isWrappedNative */
-        nullptr,    /* weakmapKeyDelegateOp */
-        UnboxedArrayObject::objectMoved
-    },
+    &UnboxedArrayObjectClassExtension,
     &UnboxedArrayObjectObjectOps
 };
 
diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp
index 33dd7ba590..5d799450c2 100644
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -551,39 +551,44 @@ sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
 
 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
 
-static const js::Class SandboxClass = {
-    "Sandbox",
-    XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
+static const js::ClassOps SandboxClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
     nullptr,        /* mayResolve */
     sandbox_finalize,
     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
+};
+
+static const js::ClassExtension SandboxClassExtension = {
+    nullptr,      /* weakmapKeyDelegateOp */
+    sandbox_moved /* objectMovedOp */
+};
+
+static const js::Class SandboxClass = {
+    "Sandbox",
+    XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
+    &SandboxClassOps,
     JS_NULL_CLASS_SPEC,
-    {
-      false,        /* isWrappedNative */
-      nullptr,      /* weakmapKeyDelegateOp */
-      sandbox_moved /* objectMovedOp */
-    },
+    &SandboxClassExtension,
     JS_NULL_OBJECT_OPS
 };
 
 // Note to whomever comes here to remove addProperty hooks: billm has promised
 // to do the work for this class.
-static const js::Class SandboxWriteToProtoClass = {
-    "Sandbox",
-    XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
+static const js::ClassOps SandboxWriteToProtoClassOps = {
     sandbox_addProperty, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
     nullptr,        /* mayResolve */
     sandbox_finalize,
     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
+};
+
+static const js::Class SandboxWriteToProtoClass = {
+    "Sandbox",
+    XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
+    &SandboxWriteToProtoClassOps,
     JS_NULL_CLASS_SPEC,
-    {
-      false,        /* isWrappedNative */
-      nullptr,      /* weakmapKeyDelegateOp */
-      sandbox_moved /* objectMovedOp */
-    },
+    &SandboxClassExtension,
     JS_NULL_OBJECT_OPS
 };
 
diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp
index b3028fbdf1..264a74800c 100644
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2129,8 +2129,8 @@ ReportZoneStats(const JS::ZoneStats& zStats,
 
 static nsresult
 ReportClassStats(const ClassInfo& classInfo, const nsACString& path,
-                 nsIHandleReportCallback* cb, nsISupports* closure,
-                 size_t& gcTotal)
+                 const nsACString& shapesPath, nsIHandleReportCallback* cb,
+                 nsISupports* closure, size_t& gcTotal)
 {
     // We deliberately don't use ZCREPORT_BYTES, so that these per-class values
     // don't go into sundries.
@@ -2200,37 +2200,37 @@ ReportClassStats(const ClassInfo& classInfo, const nsACString& path,
     }
 
     if (classInfo.shapesGCHeapTree > 0) {
-        REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("shapes/gc-heap/tree"),
+        REPORT_GC_BYTES(shapesPath + NS_LITERAL_CSTRING("shapes/gc-heap/tree"),
             classInfo.shapesGCHeapTree,
         "Shapes in a property tree.");
     }
 
     if (classInfo.shapesGCHeapDict > 0) {
-        REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
+        REPORT_GC_BYTES(shapesPath + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
             classInfo.shapesGCHeapDict,
         "Shapes in dictionary mode.");
     }
 
     if (classInfo.shapesGCHeapBase > 0) {
-        REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
+        REPORT_GC_BYTES(shapesPath + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
             classInfo.shapesGCHeapBase,
             "Base shapes, which collate data common to many shapes.");
     }
 
     if (classInfo.shapesMallocHeapTreeTables > 0) {
-        REPORT_BYTES(path + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
+        REPORT_BYTES(shapesPath + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
             KIND_HEAP, classInfo.shapesMallocHeapTreeTables,
             "Property tables of shapes in a property tree.");
     }
 
     if (classInfo.shapesMallocHeapDictTables > 0) {
-        REPORT_BYTES(path + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
+        REPORT_BYTES(shapesPath + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
             KIND_HEAP, classInfo.shapesMallocHeapDictTables,
             "Property tables of shapes in dictionary mode.");
     }
 
     if (classInfo.shapesMallocHeapTreeKids > 0) {
-        REPORT_BYTES(path + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-kids"),
+        REPORT_BYTES(shapesPath + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-kids"),
             KIND_HEAP, classInfo.shapesMallocHeapTreeKids,
             "Kid hashes of shapes in a property tree.");
     }
@@ -2278,8 +2278,12 @@ ReportCompartmentStats(const JS::CompartmentStats& cStats,
                     ? NS_LITERAL_CSTRING("classes/")
                     : NS_LITERAL_CSTRING("classes/class()/");
 
-    rv = ReportClassStats(cStats.classInfo, nonNotablePath, cb, closure,
-                          gcTotal);
+    // XXX: shapes need special treatment until bug 1265271 is fixed.
+    nsCString shapesPath = cJSPathPrefix;
+    shapesPath += NS_LITERAL_CSTRING("classes/");
+
+    rv = ReportClassStats(cStats.classInfo, nonNotablePath, shapesPath, cb,
+                          closure, gcTotal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     for (size_t i = 0; i < cStats.notableClasses.length(); i++) {
@@ -2289,7 +2293,8 @@ ReportCompartmentStats(const JS::CompartmentStats& cStats,
         nsCString classPath = cJSPathPrefix +
             nsPrintfCString("classes/class(%s)/", classInfo.className_);
 
-        rv = ReportClassStats(classInfo, classPath, cb, closure, gcTotal);
+        rv = ReportClassStats(classInfo, classPath, shapesPath, cb, closure,
+                              gcTotal);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
diff --git a/js/xpconnect/src/XPCMaps.cpp b/js/xpconnect/src/XPCMaps.cpp
index d7064906a1..55f4ca7dd2 100644
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -550,6 +550,15 @@ XPCNativeScriptableSharedMap::GetNewOrUsed(uint32_t flags,
 
     XPCNativeScriptableShared* shared = entry->key;
 
+    // XXX: this XPCNativeScriptableShared is heap-allocated, which means the
+    // js::Class it contains is also heap-allocated. This causes problems for
+    // memory reporting. See the comment above the BaseShape case in
+    // StatsCellCallback() in js/src/vm/MemoryMetrics.cpp.
+    //
+    // When the code below is removed (bug 1265271) and there are no longer any
+    // heap-allocated js::Class instances, the disabled code in
+    // StatsCellCallback() should be reinstated.
+    //
     if (!shared) {
         entry->key = shared =
             new XPCNativeScriptableShared(flags, key.TransferNameOwnership(),
diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp
index edf635fd62..401d62d8b5 100644
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -764,12 +764,16 @@ env_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
     return true;
 }
 
-static const JSClass env_class = {
-    "environment", JSCLASS_HAS_PRIVATE,
+static const JSClassOps env_classOps = {
     nullptr, nullptr, nullptr, env_setProperty,
     env_enumerate, env_resolve
 };
 
+static const JSClass env_class = {
+    "environment", JSCLASS_HAS_PRIVATE,
+    &env_classOps
+};
+
 /***************************************************************************/
 
 typedef enum JSShellErrNum {
diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp
index 1be027c6a1..65110d6341 100644
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -1431,9 +1431,7 @@ FinalizeStub(JSFreeOp* fop, JSObject* obj)
 {
 }
 
-static const JSClass XPCOutParamClass = {
-    "XPCOutParam",
-    0,
+static const JSClassOps XPCOutParamClassOps = {
     nullptr,   /* addProperty */
     nullptr,   /* delProperty */
     nullptr,   /* getProperty */
@@ -1448,6 +1446,12 @@ static const JSClass XPCOutParamClass = {
     nullptr    /* trace */
 };
 
+static const JSClass XPCOutParamClass = {
+    "XPCOutParam",
+    0,
+    &XPCOutParamClassOps
+};
+
 bool
 xpc::IsOutObject(JSContext* cx, JSObject* obj)
 {
diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp
index 04d03c39e8..5bbcee0ceb 100644
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -775,8 +775,8 @@ XPCWrappedNative::Init(const XPCNativeScriptableCreateInfo* sci)
     MOZ_ASSERT(jsclazz &&
                jsclazz->name &&
                jsclazz->flags &&
-               jsclazz->resolve &&
-               jsclazz->finalize, "bad class");
+               jsclazz->getResolve() &&
+               jsclazz->hasFinalize(), "bad class");
 
     // XXXbz JS_GetObjectPrototype wants an object, even though it then asserts
     // that this object is same-compartment with cx, which means it could just
diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
index b7d0e046ee..f03cea4712 100644
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -620,37 +620,34 @@ XPC_WN_NoHelper_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* reso
                                  resolvedp);
 }
 
-const js::Class XPC_WN_NoHelper_JSClass = {
-    "XPCWrappedNative_NoHelper",    // name;
-    WRAPPER_FLAGS |
-    JSCLASS_PRIVATE_IS_NSISUPPORTS, // flags
-
-    /* Mandatory non-null function pointer members. */
+static const js::ClassOps XPC_WN_NoHelper_JSClassOps = {
     XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
     XPC_WN_CantDeletePropertyStub,     // delProperty
     nullptr,                           // getProperty
     nullptr,                           // setProperty
-
     XPC_WN_Shared_Enumerate,           // enumerate
     XPC_WN_NoHelper_Resolve,           // resolve
     nullptr,                           // mayResolve
     XPC_WN_NoHelper_Finalize,          // finalize
+    nullptr,                           // call
+    nullptr,                           // construct
+    nullptr,                           // hasInstance
+    XPCWrappedNative::Trace,           // trace
+};
 
-    /* Optionally non-null members start here. */
-    nullptr,                         // call
-    nullptr,                         // construct
-    nullptr,                         // hasInstance
-    XPCWrappedNative::Trace,         // trace
+static const js::ClassExtension XPC_WN_JSClassExtension = {
+    nullptr, // weakmapKeyDelegateOp
+    WrappedNativeObjectMoved
+};
+
+const js::Class XPC_WN_NoHelper_JSClass = {
+    "XPCWrappedNative_NoHelper",
+    WRAPPER_FLAGS |
+    JSCLASS_IS_WRAPPED_NATIVE |
+    JSCLASS_PRIVATE_IS_NSISUPPORTS,
+    &XPC_WN_NoHelper_JSClassOps,
     JS_NULL_CLASS_SPEC,
-
-    // ClassExtension
-    {
-        true,    // isWrappedNative
-        nullptr, // weakmapKeyDelegateOp
-        WrappedNativeObjectMoved
-    },
-
-    // ObjectOps
+    &XPC_WN_JSClassExtension,
     JS_NULL_OBJECT_OPS
 };
 
@@ -967,90 +964,99 @@ XPCNativeScriptableShared::XPCNativeScriptableShared(uint32_t aFlags,
 {
     MOZ_COUNT_CTOR(XPCNativeScriptableShared);
 
+    // Initialize the js::Class.
+
     memset(&mJSClass, 0, sizeof(mJSClass));
     mJSClass.name = aName;  // take ownership
 
     if (!aPopulate)
         return;
 
-    mJSClass.flags = WRAPPER_FLAGS | JSCLASS_PRIVATE_IS_NSISUPPORTS;
+    mJSClass.flags = WRAPPER_FLAGS |
+                     JSCLASS_PRIVATE_IS_NSISUPPORTS |
+                     JSCLASS_IS_WRAPPED_NATIVE;
 
     if (mFlags.IsGlobalObject())
         mJSClass.flags |= XPCONNECT_GLOBAL_FLAGS;
 
-    JSAddPropertyOp addProperty;
-    if (mFlags.WantAddProperty())
-        addProperty = XPC_WN_Helper_AddProperty;
-    else if (mFlags.UseJSStubForAddProperty())
-        addProperty = nullptr;
-    else if (mFlags.AllowPropModsDuringResolve())
-        addProperty = XPC_WN_MaybeResolvingPropertyStub;
-    else
-        addProperty = XPC_WN_CannotModifyPropertyStub;
-    mJSClass.addProperty = addProperty;
+    // Initialize the js::ClassExtension.
 
-    JSDeletePropertyOp delProperty;
-    if (mFlags.UseJSStubForDelProperty())
-        delProperty = nullptr;
+    // This is an unusual js::ClassOps: it is heap-allocated and belongs to
+    // |this|.
+    js::ClassOps* cOps = new js::ClassOps;
+    memset(cOps, 0, sizeof(js::ClassOps));
+    mJSClass.cOps = cOps;
+
+    if (mFlags.WantAddProperty())
+        cOps->addProperty = XPC_WN_Helper_AddProperty;
+    else if (mFlags.UseJSStubForAddProperty())
+        cOps->addProperty = nullptr;
     else if (mFlags.AllowPropModsDuringResolve())
-        delProperty = XPC_WN_MaybeResolvingDeletePropertyStub;
+        cOps->addProperty = XPC_WN_MaybeResolvingPropertyStub;
     else
-        delProperty = XPC_WN_CantDeletePropertyStub;
-    mJSClass.delProperty = delProperty;
+        cOps->addProperty = XPC_WN_CannotModifyPropertyStub;
+
+    if (mFlags.UseJSStubForDelProperty())
+        cOps->delProperty = nullptr;
+    else if (mFlags.AllowPropModsDuringResolve())
+        cOps->delProperty = XPC_WN_MaybeResolvingDeletePropertyStub;
+    else
+        cOps->delProperty = XPC_WN_CantDeletePropertyStub;
 
     if (mFlags.WantGetProperty())
-        mJSClass.getProperty = XPC_WN_Helper_GetProperty;
+        cOps->getProperty = XPC_WN_Helper_GetProperty;
     else
-        mJSClass.getProperty = nullptr;
+        cOps->getProperty = nullptr;
 
-    JSSetterOp setProperty;
     if (mFlags.WantSetProperty())
-        setProperty = XPC_WN_Helper_SetProperty;
+        cOps->setProperty = XPC_WN_Helper_SetProperty;
     else if (mFlags.UseJSStubForSetProperty())
-        setProperty = nullptr;
+        cOps->setProperty = nullptr;
     else if (mFlags.AllowPropModsDuringResolve())
-        setProperty = XPC_WN_MaybeResolvingSetPropertyStub;
+        cOps->setProperty = XPC_WN_MaybeResolvingSetPropertyStub;
     else
-        setProperty = XPC_WN_CannotModifySetPropertyStub;
-    mJSClass.setProperty = setProperty;
+        cOps->setProperty = XPC_WN_CannotModifySetPropertyStub;
 
     MOZ_ASSERT_IF(mFlags.WantEnumerate(), !mFlags.WantNewEnumerate());
     MOZ_ASSERT_IF(mFlags.WantNewEnumerate(), !mFlags.WantEnumerate());
 
     // We will use ops->enumerate set below for NewEnumerate
     if (mFlags.WantNewEnumerate())
-        mJSClass.enumerate = nullptr;
+        cOps->enumerate = nullptr;
     else if (mFlags.WantEnumerate())
-        mJSClass.enumerate = XPC_WN_Helper_Enumerate;
+        cOps->enumerate = XPC_WN_Helper_Enumerate;
     else
-        mJSClass.enumerate = XPC_WN_Shared_Enumerate;
+        cOps->enumerate = XPC_WN_Shared_Enumerate;
 
     // We have to figure out resolve strategy at call time
-    mJSClass.resolve = XPC_WN_Helper_Resolve;
+    cOps->resolve = XPC_WN_Helper_Resolve;
 
     if (mFlags.WantFinalize())
-        mJSClass.finalize = XPC_WN_Helper_Finalize;
+        cOps->finalize = XPC_WN_Helper_Finalize;
     else
-        mJSClass.finalize = XPC_WN_NoHelper_Finalize;
-
-    if (mFlags.WantNewEnumerate())
-        mJSClass.ops = &XPC_WN_ObjectOpsWithEnumerate;
+        cOps->finalize = XPC_WN_NoHelper_Finalize;
 
     if (mFlags.WantCall())
-        mJSClass.call = XPC_WN_Helper_Call;
+        cOps->call = XPC_WN_Helper_Call;
     if (mFlags.WantConstruct())
-        mJSClass.construct = XPC_WN_Helper_Construct;
+        cOps->construct = XPC_WN_Helper_Construct;
 
     if (mFlags.WantHasInstance())
-        mJSClass.hasInstance = XPC_WN_Helper_HasInstance;
+        cOps->hasInstance = XPC_WN_Helper_HasInstance;
 
     if (mFlags.IsGlobalObject())
-        mJSClass.trace = JS_GlobalObjectTraceHook;
+        cOps->trace = JS_GlobalObjectTraceHook;
     else
-        mJSClass.trace = XPCWrappedNative::Trace;
+        cOps->trace = XPCWrappedNative::Trace;
 
-    mJSClass.ext.isWrappedNative = true;
-    mJSClass.ext.objectMovedOp = WrappedNativeObjectMoved;
+    // Initialize the js::ClassExtension.
+
+    mJSClass.ext = &XPC_WN_JSClassExtension;
+
+    // Initialize the js::ObjectOps.
+
+    if (mFlags.WantNewEnumerate())
+        mJSClass.oOps = &XPC_WN_ObjectOpsWithEnumerate;
 }
 
 /***************************************************************************/
@@ -1250,60 +1256,41 @@ XPC_WN_ModsAllowed_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, b
                                  JSPROP_ENUMERATE, resolvep);
 }
 
-#define XPC_WN_SHARED_PROTO_CLASS_EXT                                  \
-    {                                                                  \
-        false,      /* isWrappedNative */                              \
-        nullptr,    /* weakmapKeyDelegateOp */                         \
-        XPC_WN_Shared_Proto_ObjectMoved                                \
-    }
+static const js::ClassOps XPC_WN_ModsAllowed_Proto_JSClassOps = {
+    nullptr,                            // addProperty
+    nullptr,                            // delProperty
+    nullptr,                            // getProperty
+    nullptr,                            // setProperty
+    XPC_WN_Shared_Proto_Enumerate,      // enumerate
+    XPC_WN_ModsAllowed_Proto_Resolve,   // resolve
+    nullptr,                            // mayResolve
+    XPC_WN_Shared_Proto_Finalize,       // finalize
+    nullptr,                            // call
+    nullptr,                            // construct
+    nullptr,                            // hasInstance
+    XPC_WN_Shared_Proto_Trace,          // trace
+};
+
+static const js::ClassExtension XPC_WN_Shared_Proto_ClassExtension = {
+    nullptr,    /* weakmapKeyDelegateOp */
+    XPC_WN_Shared_Proto_ObjectMoved
+};
 
 const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = {
-    "XPC_WN_ModsAllowed_WithCall_Proto_JSClass", // name;
-    WRAPPER_FLAGS, // flags;
-
-    /* Function pointer members. */
-    nullptr,                        // addProperty;
-    nullptr,                        // delProperty;
-    nullptr,                        // getProperty;
-    nullptr,                        // setProperty;
-    XPC_WN_Shared_Proto_Enumerate,  // enumerate;
-    XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
-    nullptr,                        // mayResolve;
-    XPC_WN_Shared_Proto_Finalize,   // finalize;
-
-    /* Optionally non-null members start here. */
-    nullptr,                        // call;
-    nullptr,                        // construct;
-    nullptr,                        // hasInstance;
-    XPC_WN_Shared_Proto_Trace,      // trace;
-
+    "XPC_WN_ModsAllowed_WithCall_Proto_JSClass",
+    WRAPPER_FLAGS,
+    &XPC_WN_ModsAllowed_Proto_JSClassOps,
     JS_NULL_CLASS_SPEC,
-    XPC_WN_SHARED_PROTO_CLASS_EXT,
+    &XPC_WN_Shared_Proto_ClassExtension,
     XPC_WN_WithCall_ObjectOps
 };
 
 const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = {
-    "XPC_WN_ModsAllowed_NoCall_Proto_JSClass", // name;
-    WRAPPER_FLAGS,                  // flags;
-
-    /* Function pointer members. */
-    nullptr,                        // addProperty;
-    nullptr,                        // delProperty;
-    nullptr,                        // getProperty;
-    nullptr,                        // setProperty;
-    XPC_WN_Shared_Proto_Enumerate,  // enumerate;
-    XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
-    nullptr,                        // mayResolve;
-    XPC_WN_Shared_Proto_Finalize,   // finalize;
-
-    /* Optionally non-null members start here. */
-    nullptr,                         // call;
-    nullptr,                         // construct;
-    nullptr,                         // hasInstance;
-    XPC_WN_Shared_Proto_Trace,      // trace;
-
+    "XPC_WN_ModsAllowed_NoCall_Proto_JSClass",
+    WRAPPER_FLAGS,
+    &XPC_WN_ModsAllowed_Proto_JSClassOps,
     JS_NULL_CLASS_SPEC,
-    XPC_WN_SHARED_PROTO_CLASS_EXT,
+    &XPC_WN_Shared_Proto_ClassExtension,
     XPC_WN_NoCall_ObjectOps
 };
 
@@ -1360,53 +1347,36 @@ XPC_WN_NoMods_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool*
                                  JSPROP_ENUMERATE, resolvedp);
 }
 
+static const js::ClassOps XPC_WN_NoMods_Proto_JSClassOps = {
+    XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty
+    XPC_WN_CantDeletePropertyStub,             // delProperty
+    nullptr,                                   // getProperty
+    nullptr,                                   // setProperty
+    XPC_WN_Shared_Proto_Enumerate,             // enumerate
+    XPC_WN_NoMods_Proto_Resolve,               // resolve
+    nullptr,                                   // mayResolve
+    XPC_WN_Shared_Proto_Finalize,              // finalize
+    nullptr,                                   // call
+    nullptr,                                   // construct
+    nullptr,                                   // hasInstance
+    XPC_WN_Shared_Proto_Trace,                 // trace
+};
+
 const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = {
-    "XPC_WN_NoMods_WithCall_Proto_JSClass",    // name;
-    WRAPPER_FLAGS,                             // flags;
-
-    /* Mandatory non-null function pointer members. */
-    XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty;
-    XPC_WN_CantDeletePropertyStub,             // delProperty;
-    nullptr,                                   // getProperty;
-    nullptr,                                   // setProperty;
-    XPC_WN_Shared_Proto_Enumerate,             // enumerate;
-    XPC_WN_NoMods_Proto_Resolve,               // resolve;
-    nullptr,                                   // mayResolve;
-    XPC_WN_Shared_Proto_Finalize,              // finalize;
-
-    /* Optionally non-null members start here. */
-    nullptr,                         // call;
-    nullptr,                         // construct;
-    nullptr,                         // hasInstance;
-    XPC_WN_Shared_Proto_Trace,      // trace;
-
+    "XPC_WN_NoMods_WithCall_Proto_JSClass",
+    WRAPPER_FLAGS,
+    &XPC_WN_NoMods_Proto_JSClassOps,
     JS_NULL_CLASS_SPEC,
-    XPC_WN_SHARED_PROTO_CLASS_EXT,
+    &XPC_WN_Shared_Proto_ClassExtension,
     XPC_WN_WithCall_ObjectOps
 };
 
 const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = {
-    "XPC_WN_NoMods_NoCall_Proto_JSClass",      // name;
-    WRAPPER_FLAGS,                             // flags;
-
-    /* Mandatory non-null function pointer members. */
-    XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty;
-    XPC_WN_CantDeletePropertyStub,             // delProperty;
-    nullptr,                                   // getProperty;
-    nullptr,                                   // setProperty;
-    XPC_WN_Shared_Proto_Enumerate,             // enumerate;
-    XPC_WN_NoMods_Proto_Resolve,               // resolve;
-    nullptr,                                   // mayResolve;
-    XPC_WN_Shared_Proto_Finalize,              // finalize;
-
-    /* Optionally non-null members start here. */
-    nullptr,                         // call;
-    nullptr,                         // construct;
-    nullptr,                         // hasInstance;
-    XPC_WN_Shared_Proto_Trace,      // trace;
-
+    "XPC_WN_NoMods_NoCall_Proto_JSClass",
+    WRAPPER_FLAGS,
+    &XPC_WN_NoMods_Proto_JSClassOps,
     JS_NULL_CLASS_SPEC,
-    XPC_WN_SHARED_PROTO_CLASS_EXT,
+    &XPC_WN_Shared_Proto_ClassExtension,
     XPC_WN_NoCall_ObjectOps
 };
 
@@ -1481,30 +1451,31 @@ static_assert(((WRAPPER_FLAGS >> JSCLASS_RESERVED_SLOTS_SHIFT) &
                JSCLASS_RESERVED_SLOTS_MASK) == 0,
               "WRAPPER_FLAGS should not include any reserved slots");
 
-const js::Class XPC_WN_Tearoff_JSClass = {
-    "WrappedNative_TearOff",                   // name;
-    WRAPPER_FLAGS |
-    JSCLASS_HAS_RESERVED_SLOTS(XPC_WN_TEAROFF_RESERVED_SLOTS), // flags;
-    XPC_WN_OnlyIWrite_AddPropertyStub,         // addProperty;
-    XPC_WN_CantDeletePropertyStub,             // delProperty;
-    nullptr,                                   // getProperty;
-    nullptr,                                   // setProperty;
-    XPC_WN_TearOff_Enumerate,                  // enumerate;
-    XPC_WN_TearOff_Resolve,                    // resolve;
-    nullptr,                                   // mayResolve;
-    XPC_WN_TearOff_Finalize,                   // finalize;
-
-    /* Optionally non-null members start here. */
-    nullptr,                                   // call
-    nullptr,                                   // construct
-    nullptr,                                   // hasInstance
-    nullptr,                                   // trace
-    JS_NULL_CLASS_SPEC,
-
-    // ClassExtension
-    {
-        false,                                 // isWrappedNative
-        nullptr,                               // weakmapKeyDelegateOp
-        XPC_WN_TearOff_ObjectMoved
-    },
+static const js::ClassOps XPC_WN_Tearoff_JSClassOps = {
+    XPC_WN_OnlyIWrite_AddPropertyStub,  // addProperty
+    XPC_WN_CantDeletePropertyStub,      // delProperty
+    nullptr,                            // getProperty
+    nullptr,                            // setProperty
+    XPC_WN_TearOff_Enumerate,           // enumerate
+    XPC_WN_TearOff_Resolve,             // resolve
+    nullptr,                            // mayResolve
+    XPC_WN_TearOff_Finalize,            // finalize
+    nullptr,                            // call
+    nullptr,                            // construct
+    nullptr,                            // hasInstance
+    nullptr,                            // trace
+};
+
+static const js::ClassExtension XPC_WN_Tearoff_JSClassExtension = {
+    nullptr,                            // weakmapKeyDelegateOp
+    XPC_WN_TearOff_ObjectMoved
+};
+
+const js::Class XPC_WN_Tearoff_JSClass = {
+    "WrappedNative_TearOff",
+    WRAPPER_FLAGS |
+    JSCLASS_HAS_RESERVED_SLOTS(XPC_WN_TEAROFF_RESERVED_SLOTS),
+    &XPC_WN_Tearoff_JSClassOps,
+    JS_NULL_CLASS_SPEC,
+    &XPC_WN_Tearoff_JSClassExtension
 };
diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp
index 59942ea16d..dac625cef5 100644
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -385,7 +385,7 @@ CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal,
     // Verify that the right trace hook is called. Note that this doesn't
     // work right for wrapped globals, since the tracing situation there is
     // more complicated. Manual inspection shows that they do the right thing.
-    if (!((const js::Class*)clasp)->ext.isWrappedNative)
+    if (!((const js::Class*)clasp)->isWrappedNative())
     {
         VerifyTraceProtoAndIfaceCacheCalledTracer trc(JS_GetRuntime(cx));
         TraceChildren(&trc, GCCellPtr(global.get()));
diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h
index b433234656..4e2025f3b0 100644
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -225,7 +225,7 @@ template class JS_PUBLIC_API(JS::WeakMapPtr);
 // wrappednative wrapper, holding the XPCWrappedNative in its private slot.
 static inline bool IS_WN_CLASS(const js::Class* clazz)
 {
-    return clazz->ext.isWrappedNative;
+    return clazz->isWrappedNative();
 }
 
 static inline bool IS_WN_REFLECTOR(JSObject* obj)
@@ -1657,8 +1657,8 @@ public:
     XPCNativeScriptableShared(uint32_t aFlags, char* aName, bool aPopulate);
 
     ~XPCNativeScriptableShared() {
-        if (mJSClass.name)
-            free((void*)mJSClass.name);
+        free((void*)mJSClass.name);
+        free((void*)mJSClass.cOps);
         MOZ_COUNT_DTOR(XPCNativeScriptableShared);
     }
 
@@ -1674,6 +1674,10 @@ public:
 
 private:
     XPCNativeScriptableFlags mFlags;
+
+    // This is an unusual js::Class instance: its name and cOps members are
+    // heap-allocated, unlike all other instances for which they are statically
+    // allocated. So we must free them in the destructor.
     js::Class mJSClass;
 };
 
diff --git a/js/xpconnect/tests/chrome/chrome.ini b/js/xpconnect/tests/chrome/chrome.ini
index 7d081f8558..ac08b58ab9 100644
--- a/js/xpconnect/tests/chrome/chrome.ini
+++ b/js/xpconnect/tests/chrome/chrome.ini
@@ -12,6 +12,23 @@ support-files =
   outoflinexulscript.js
   subscript.js
   utf8_subscript.js
+  !/js/xpconnect/tests/mochitest/bug500931_helper.html
+  !/js/xpconnect/tests/mochitest/bug571849_helper.html
+  !/js/xpconnect/tests/mochitest/chrome_wrappers_helper.html
+  !/js/xpconnect/tests/mochitest/file_bug706301.html
+  !/js/xpconnect/tests/mochitest/file_bug738244.html
+  !/js/xpconnect/tests/mochitest/file_bug760131.html
+  !/js/xpconnect/tests/mochitest/file_bug795275.html
+  !/js/xpconnect/tests/mochitest/file_bug795275.xml
+  !/js/xpconnect/tests/mochitest/file_bug799348.html
+  !/js/xpconnect/tests/mochitest/file_bug860494.html
+  !/js/xpconnect/tests/mochitest/file_documentdomain.html
+  !/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html
+  !/js/xpconnect/tests/mochitest/file_empty.html
+  !/js/xpconnect/tests/mochitest/file_exnstack.html
+  !/js/xpconnect/tests/mochitest/file_expandosharing.html
+  !/js/xpconnect/tests/mochitest/file_nodelists.html
+  !/js/xpconnect/tests/mochitest/file_evalInSandbox.html
 
 [test_APIExposer.xul]
 [test_bug361111.xul]
diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp
index bf87cd9bfb..fa98407f07 100644
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1057,11 +1057,15 @@ ExpandoObjectFinalize(JSFreeOp* fop, JSObject* obj)
     NS_RELEASE(principal);
 }
 
+static const JSClassOps ExpandoObjectClassOps = {
+    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, ExpandoObjectFinalize
+};
+
 const JSClass ExpandoObjectClass = {
     "XrayExpandoObject",
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, ExpandoObjectFinalize
+    &ExpandoObjectClassOps
 };
 
 bool
@@ -1813,14 +1817,15 @@ DOMXrayTraits::call(JSContext* cx, HandleObject wrapper,
     // are using "legacycaller", which basically means plug-ins.  We want to
     // call those on the content compartment.
     if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) {
-        if (!clasp->call) {
+        if (JSNative call = clasp->getCall()) {
+            // call it on the Xray compartment
+            if (!call(cx, args.length(), args.base()))
+                return false;
+        } else {
             RootedValue v(cx, ObjectValue(*wrapper));
             js::ReportIsNotFunction(cx, v);
             return false;
         }
-        // call it on the Xray compartment
-        if (!clasp->call(cx, args.length(), args.base()))
-            return false;
     } else {
         // This is only reached for WebIDL instance objects, and in practice
         // only for plugins.  Just call them on the content compartment.
@@ -1839,13 +1844,14 @@ DOMXrayTraits::construct(JSContext* cx, HandleObject wrapper,
     const js::Class* clasp = js::GetObjectClass(obj);
     // See comments in DOMXrayTraits::call() explaining what's going on here.
     if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) {
-        if (!clasp->construct) {
+        if (JSNative construct = clasp->getConstruct()) {
+            if (!construct(cx, args.length(), args.base()))
+                return false;
+        } else {
             RootedValue v(cx, ObjectValue(*wrapper));
             js::ReportIsNotFunction(cx, v);
             return false;
         }
-        if (!clasp->construct(cx, args.length(), args.base()))
-            return false;
     } else {
         if (!baseInstance.construct(cx, wrapper, args))
             return false;
diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp
index 5fae8f90b3..23cd01f717 100644
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -3634,43 +3634,49 @@ ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
 {
   bool snapOpaque;
   nsRegion opaque = aItem->GetOpaqueRegion(mBuilder, &snapOpaque);
-  nsIntRegion opaquePixels;
-  if (!opaque.IsEmpty()) {
-    nsRegion opaqueClipped;
-    for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
-      opaqueClipped.Or(opaqueClipped,
-                       aClip.ApproximateIntersectInward(iter.Get()));
-    }
-    if (aAnimatedGeometryRoot == mContainerAnimatedGeometryRoot &&
-        opaqueClipped.Contains(mContainerBounds)) {
-      *aHideAllLayersBelow = true;
-      aList->SetIsOpaque();
-    }
-    // Add opaque areas to the "exclude glass" region. Only do this when our
-    // container layer is going to be the rootmost layer, otherwise transforms
-    // etc will mess us up (and opaque contributions from other containers are
-    // not needed).
-    if (!nsLayoutUtils::GetCrossDocParentFrame(mContainerFrame)) {
-      mBuilder->AddWindowOpaqueRegion(opaqueClipped);
-    }
-    opaquePixels = ScaleRegionToInsidePixels(opaqueClipped, snapOpaque);
+  if (opaque.IsEmpty()) {
+    return nsIntRegion();
+  }
 
-    nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot);
-    if (sf) {
-      nsRect displayport;
-      bool usingDisplayport =
-        nsLayoutUtils::GetDisplayPort((*aAnimatedGeometryRoot)->GetContent(), &displayport,
-          RelativeTo::ScrollFrame);
-      if (!usingDisplayport) {
-        // No async scrolling, so all that matters is that the layer contents
-        // cover the scrollport.
-        displayport = sf->GetScrollPortRect();
-      }
-      nsIFrame* scrollFrame = do_QueryFrame(sf);
-      displayport += scrollFrame->GetOffsetToCrossDoc(mContainerReferenceFrame);
-      if (opaque.Contains(displayport)) {
-        *aOpaqueForAnimatedGeometryRootParent = true;
-      }
+  nsIntRegion opaquePixels;
+  nsRegion opaqueClipped;
+  for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
+    opaqueClipped.Or(opaqueClipped,
+                     aClip.ApproximateIntersectInward(iter.Get()));
+  }
+  if (aAnimatedGeometryRoot == mContainerAnimatedGeometryRoot &&
+      opaqueClipped.Contains(mContainerBounds)) {
+    *aHideAllLayersBelow = true;
+    aList->SetIsOpaque();
+  }
+  // Add opaque areas to the "exclude glass" region. Only do this when our
+  // container layer is going to be the rootmost layer, otherwise transforms
+  // etc will mess us up (and opaque contributions from other containers are
+  // not needed).
+  if (!nsLayoutUtils::GetCrossDocParentFrame(mContainerFrame)) {
+    mBuilder->AddWindowOpaqueRegion(opaqueClipped);
+  }
+  opaquePixels = ScaleRegionToInsidePixels(opaqueClipped, snapOpaque);
+
+  if (IsInInactiveLayer()) {
+    return opaquePixels;
+  }
+
+  nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot);
+  if (sf) {
+    nsRect displayport;
+    bool usingDisplayport =
+      nsLayoutUtils::GetDisplayPort((*aAnimatedGeometryRoot)->GetContent(), &displayport,
+        RelativeTo::ScrollFrame);
+    if (!usingDisplayport) {
+      // No async scrolling, so all that matters is that the layer contents
+      // cover the scrollport.
+      displayport = sf->GetScrollPortRect();
+    }
+    nsIFrame* scrollFrame = do_QueryFrame(sf);
+    displayport += scrollFrame->GetOffsetToCrossDoc(mContainerReferenceFrame);
+    if (opaque.Contains(displayport)) {
+      *aOpaqueForAnimatedGeometryRootParent = true;
     }
   }
   return opaquePixels;
@@ -3945,7 +3951,8 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
       // So we'll do a little hand holding and pass the clip instead of the
       // visible rect for the two important cases.
       nscolor uniformColor = NS_RGBA(0,0,0,0);
-      nscolor* uniformColorPtr = !mayDrawOutOfOrder ? &uniformColor : nullptr;
+      nscolor* uniformColorPtr = (mayDrawOutOfOrder || IsInInactiveLayer()) ? nullptr :
+                                                                              &uniformColor;
       nsIntRect clipRectUntyped;
       const DisplayItemClip& layerClip = shouldFixToViewport ? fixedToViewportClip : itemClip;
       ParentLayerIntRect layerClipRect;
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index 400da24b29..c58269d55b 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2537,27 +2537,28 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
 
     child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
 
-    if (!pseudoStackingContext) {
-      // THIS IS THE COMMON CASE.
-      // Not a pseudo or real stacking context. Do the simple thing and
-      // return early.
-
-      if (aBuilder->IsBuildingLayerEventRegions()) {
-        // If this frame has a different animated geometry root than its parent,
-        // make sure we accumulate event regions for its layer.
-        if (buildingForChild.IsAnimatedGeometryRoot()) {
-          nsDisplayLayerEventRegions* eventRegions =
-            new (aBuilder) nsDisplayLayerEventRegions(aBuilder, child);
-          eventRegions->AddFrame(aBuilder, child);
-          aBuilder->SetLayerEventRegions(eventRegions);
-          aLists.BorderBackground()->AppendNewToTop(eventRegions);
-        }
+    if (aBuilder->IsBuildingLayerEventRegions()) {
+      // If this frame has a different animated geometry root than its parent,
+      // make sure we accumulate event regions for its layer.
+      if (buildingForChild.IsAnimatedGeometryRoot()) {
+        nsDisplayLayerEventRegions* eventRegions =
+          new (aBuilder) nsDisplayLayerEventRegions(aBuilder, child);
+        eventRegions->AddFrame(aBuilder, child);
+        aBuilder->SetLayerEventRegions(eventRegions);
+        aLists.BorderBackground()->AppendNewToTop(eventRegions);
       }
 
       nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions();
       if (eventRegions) {
         eventRegions->AddFrame(aBuilder, child);
       }
+    }
+
+    if (!pseudoStackingContext) {
+      // THIS IS THE COMMON CASE.
+      // Not a pseudo or real stacking context. Do the simple thing and
+      // return early.
+
       aBuilder->AdjustWindowDraggingRegion(child);
       child->BuildDisplayList(aBuilder, dirty, aLists);
       aBuilder->DisplayCaret(child, dirty, aLists.Content());
@@ -2572,13 +2573,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
     // stacking context's positioned descendant list, because they might be
     // z-index:non-auto
     nsDisplayListCollection pseudoStack;
-    if (aBuilder->IsBuildingLayerEventRegions()) {
-      nsDisplayLayerEventRegions* eventRegions =
-        new (aBuilder) nsDisplayLayerEventRegions(aBuilder, child);
-      eventRegions->AddFrame(aBuilder, child);
-      aBuilder->SetLayerEventRegions(eventRegions);
-      pseudoStack.BorderBackground()->AppendNewToTop(eventRegions);
-    }
     aBuilder->AdjustWindowDraggingRegion(child);
     child->BuildDisplayList(aBuilder, dirty, pseudoStack);
     aBuilder->DisplayCaret(child, dirty, pseudoStack.Content());
@@ -5279,6 +5273,40 @@ nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
 
 /*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
 
+static bool
+DoesLayerHaveOutOfDateFrameMetrics(Layer* aLayer)
+{
+  for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) {
+    const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
+    if (!metrics.IsScrollable()) {
+      continue;
+    }
+    nsIScrollableFrame* scrollableFrame =
+      nsLayoutUtils::FindScrollableFrameFor(metrics.GetScrollId());
+    if (!scrollableFrame) {
+      // This shouldn't happen, so let's do the safe thing and trigger a full
+      // paint if it does.
+      return true;
+    }
+    nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
+    if (metrics.GetScrollOffset() != CSSPoint::FromAppUnits(scrollPosition)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool
+DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(Layer* aLayer)
+{
+  for (Layer* layer = aLayer; layer; layer = layer->GetParent()) {
+    if (DoesLayerHaveOutOfDateFrameMetrics(layer)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 bool
 nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult)
 {
@@ -5291,6 +5319,14 @@ nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult)
     return false;
   }
 
+  if (DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(layer)) {
+    // At least one scroll frame that can affect the position of this layer
+    // has changed its scroll offset since the last paint. Schedule a full
+    // paint to make sure that this layer's transform and all the frame
+    // metrics that affect it are in sync.
+    return false;
+  }
+
   gfx::Matrix4x4 transform3d;
   if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) {
     // We're not able to compute a layer transform that we know would
diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp
index d0d6642fd7..fc71698570 100644
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -21,6 +21,7 @@
 #include "mozilla/Snprintf.h"
 #include "mozilla/Telemetry.h"
 #include "nsCORSListenerProxy.h"
+#include "nsContentPolicyUtils.h"
 #include "nsCSSParser.h"
 #include "nsDeviceContext.h"
 #include "nsFontFaceLoader.h"
@@ -597,10 +598,9 @@ FontFaceSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
                                             aFontFaceSrc->mURI,
                                             mDocument,
                                             aUserFontEntry->GetPrincipal(),
-                                            nsILoadInfo::SEC_NORMAL,
+                                            nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
                                             nsIContentPolicy::TYPE_FONT,
                                             loadGroup);
-
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr fontLoader =
@@ -646,25 +646,9 @@ FontFaceSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
                                nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
                                loadGroup);
 
-  bool inherits = false;
-  rv = NS_URIChainHasFlags(aFontFaceSrc->mURI,
-                           nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
-                           &inherits);
-  if (NS_SUCCEEDED(rv) && inherits) {
-    // allow data, javascript, etc URI's
-    rv = channel->AsyncOpen(streamLoader, nullptr);
-  } else {
-    RefPtr listener =
-      new nsCORSListenerProxy(streamLoader, aUserFontEntry->GetPrincipal(), false);
-    // Doesn't matter what data: URI handling we use here, since we
-    // don't even use a CORS listener proxy for the data: case.
-    rv = listener->Init(channel, DataURIHandling::Disallow);
-    if (NS_SUCCEEDED(rv)) {
-      rv = channel->AsyncOpen(listener, nullptr);
-    }
-    if (NS_FAILED(rv)) {
-      fontLoader->DropChannel();  // explicitly need to break ref cycle
-    }
+  rv = channel->AsyncOpen2(streamLoader);
+  if (NS_FAILED(rv)) {
+    fontLoader->DropChannel();  // explicitly need to break ref cycle
   }
 
   if (NS_SUCCEEDED(rv)) {
@@ -1340,13 +1324,6 @@ FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
     *aPrincipal = aFontFaceSrc->mOriginPrincipal;
   }
 
-  nsresult rv = nsFontFaceLoader::CheckLoadAllowed(*aPrincipal,
-                                                   aFontFaceSrc->mURI,
-                                                   mDocument);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
   *aBypassCache = false;
 
   nsCOMPtr docShell = mDocument->GetDocShell();
@@ -1365,7 +1342,28 @@ FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
     }
   }
 
-  return rv;
+  return NS_OK;
+}
+
+
+// @arg aPrincipal: generally this is mDocument->NodePrincipal() but
+// might also be the original principal which enables user stylesheets
+// to load font files via @font-face rules.
+bool
+FontFaceSet::IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal)
+{
+  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+  nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_FONT,
+                                          aFontLocation,
+                                          aPrincipal,
+                                          mDocument,
+                                          EmptyCString(), // mime type
+                                          nullptr, // aExtra
+                                          &shouldLoad,
+                                          nsContentUtils::GetContentPolicy(),
+                                          nsContentUtils::GetSecurityManager());
+
+  return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
 }
 
 nsresult
@@ -1381,18 +1379,21 @@ FontFaceSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
   // node and a principal.  This is because the document where the font is
   // being loaded might have a different origin from the principal of the
   // stylesheet that initiated the font load.
+  // Further, we only get here for data: loads, so it doesn't really matter
+  // whether we use SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS or not, to be more
+  // restrictive we use SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS.
   rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
                                             aFontFaceSrc->mURI,
                                             mDocument,
                                             aFontToLoad->GetPrincipal(),
-                                            nsILoadInfo::SEC_NORMAL,
+                                            nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
                                             nsIContentPolicy::TYPE_FONT);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // blocking stream is OK for data URIs
   nsCOMPtr stream;
-  rv = channel->Open(getter_AddRefs(stream));
+  rv = channel->Open2(getter_AddRefs(stream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint64_t bufferLength64;
@@ -1744,6 +1745,14 @@ FontFaceSet::UserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
   return mFontFaceSet->CheckFontLoad(aFontFaceSrc, aPrincipal, aBypassCache);
 }
 
+/* virtual */ bool
+FontFaceSet::UserFontSet::IsFontLoadAllowed(nsIURI* aFontLocation,
+                                            nsIPrincipal* aPrincipal)
+{
+  return mFontFaceSet &&
+         mFontFaceSet->IsFontLoadAllowed(aFontLocation, aPrincipal);
+}
+
 /* virtual */ nsresult
 FontFaceSet::UserFontSet::StartLoad(gfxUserFontEntry* aUserFontEntry,
                                     const gfxFontFaceSrc* aFontFaceSrc)
diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h
index 2f0b47df5a..37fa29871b 100644
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -65,6 +65,10 @@ public:
     virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
                                    nsIPrincipal** aPrincipal,
                                    bool* aBypassCache) override;
+
+    virtual bool IsFontLoadAllowed(nsIURI* aFontLocation,
+                                   nsIPrincipal* aPrincipal) override;
+
     virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
                                const gfxFontFaceSrc* aFontFaceSrc) override;
 
@@ -257,6 +261,7 @@ private:
   nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
                          nsIPrincipal** aPrincipal,
                          bool* aBypassCache);
+  bool IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal);
   bool GetPrivateBrowsing();
   nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
                             const gfxFontFaceSrc* aFontFaceSrc,
diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp
index 15c97d3055..acb229b208 100644
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -297,43 +297,6 @@ nsFontFaceLoader::Cancel()
   mChannel->Cancel(NS_BINDING_ABORTED);
 }
 
-/* static */ nsresult
-nsFontFaceLoader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
-                                   nsIURI* aTargetURI,
-                                   nsISupports* aContext)
-{
-  nsresult rv;
-
-  if (!aSourcePrincipal)
-    return NS_OK;
-
-  // check with the security manager
-  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-  rv = secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI,
-                                        nsIScriptSecurityManager::STANDARD);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // check content policy
-  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_FONT,
-                                 aTargetURI,
-                                 aSourcePrincipal,
-                                 aContext,
-                                 EmptyCString(), // mime type
-                                 nullptr,
-                                 &shouldLoad,
-                                 nsContentUtils::GetContentPolicy(),
-                                 nsContentUtils::GetSecurityManager());
-
-  if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
-    return NS_ERROR_CONTENT_BLOCKED;
-  }
-
-  return NS_OK;
-}
-
 uint8_t
 nsFontFaceLoader::GetFontDisplay()
 {
diff --git a/layout/style/nsFontFaceLoader.h b/layout/style/nsFontFaceLoader.h
index d9d607702c..34ff640394 100644
--- a/layout/style/nsFontFaceLoader.h
+++ b/layout/style/nsFontFaceLoader.h
@@ -42,9 +42,6 @@ public:
 
   static void LoadTimerCallback(nsITimer* aTimer, void* aClosure);
 
-  static nsresult CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
-                                   nsIURI* aTargetURI,
-                                   nsISupports* aContext);
   gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; }
 
 protected:
diff --git a/layout/tools/reftest/Makefile.in b/layout/tools/reftest/Makefile.in
index 21c56c4eb2..e1aa7014dd 100644
--- a/layout/tools/reftest/Makefile.in
+++ b/layout/tools/reftest/Makefile.in
@@ -18,6 +18,7 @@ _HARNESS_FILES = \
   $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py \
   $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py \
   $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/droid.py \
+  $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/version_codes.py \
   $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/Zeroconf.py \
   $(topsrcdir)/testing/mozbase/moznetwork/moznetwork/moznetwork.py \
   $(topsrcdir)/build/mobile/b2gautomation.py \
diff --git a/layout/tools/reftest/mach_commands.py b/layout/tools/reftest/mach_commands.py
index d12c753213..b3b2bc5ebd 100644
--- a/layout/tools/reftest/mach_commands.py
+++ b/layout/tools/reftest/mach_commands.py
@@ -230,7 +230,7 @@ class ReftestRunner(MozbuildObject):
             kwargs["tests"] = [os.path.join(*default_manifest[kwargs["suite"]])]
 
         kwargs["extraProfileFiles"].append(
-            os.path.join(self.topobjdir, "dist", "bin", "res", "fonts"))
+            os.path.join(self.topsrcdir, "mobile", "android", "fonts"))
 
         if not kwargs["httpdPath"]:
             kwargs["httpdPath"] = os.path.join(self.tests_dir, "modules")
@@ -244,6 +244,9 @@ class ReftestRunner(MozbuildObject):
         kwargs["ignoreWindowSize"] = True
         kwargs["printDeviceInfo"] = False
 
+        from mozrunner.devices.android_device import grant_runtime_permissions
+        grant_runtime_permissions(self, kwargs['app'])
+
         # A symlink and some path manipulations are required so that test
         # manifests can be found both locally and remotely (via a url)
         # using the same relative path.
diff --git a/layout/tools/reftest/remotereftest.py b/layout/tools/reftest/remotereftest.py
index 840282df37..9218a9b6d5 100644
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -269,6 +269,7 @@ class RemoteReftest(RefTest):
 
         try:
             self._devicemanager.pushDir(profileDir, options.remoteProfile)
+            self._devicemanager.chmodDir(options.remoteProfile)
         except devicemanager.DMError:
             print "Automation Error: Failed to copy profiledir to device"
             raise
@@ -280,6 +281,7 @@ class RemoteReftest(RefTest):
         RefTest.copyExtraFilesToProfile(self, options, profile)
         try:
             self._devicemanager.pushDir(profileDir, options.remoteProfile)
+            self._devicemanager.chmodDir(options.remoteProfile)
         except devicemanager.DMError:
             print "Automation Error: Failed to copy extra files to device"
             raise
diff --git a/modules/libjar/test/mochitest/mochitest.ini b/modules/libjar/test/mochitest/mochitest.ini
index b31b07a39a..297689cb17 100644
--- a/modules/libjar/test/mochitest/mochitest.ini
+++ b/modules/libjar/test/mochitest/mochitest.ini
@@ -4,6 +4,7 @@ support-files =
   bug403331.zip
   bug403331.zip^headers^
   openredirect.sjs
+  !/dom/base/test/file_bug945152.jar
 
 [test_bug403331.html]
 [test_bug1034143_mapped.html]
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index f745478ebf..65ccc3a763 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1471,6 +1471,7 @@ pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 150);
 pref("javascript.options.mem.gc_low_frequency_heap_growth", 150);
 pref("javascript.options.mem.gc_dynamic_heap_growth", true);
 pref("javascript.options.mem.gc_dynamic_mark_slice", true);
+pref("javascript.options.mem.gc_refresh_frame_slices_enabled", true);
 pref("javascript.options.mem.gc_allocation_threshold_mb", 30);
 pref("javascript.options.mem.gc_decommit_threshold_mb", 32);
 pref("javascript.options.mem.gc_min_empty_chunk_count", 1);
diff --git a/moz.build b/moz.build
index 7b4d1c2249..56e77b7a0f 100644
--- a/moz.build
+++ b/moz.build
@@ -71,6 +71,8 @@ if CONFIG['COMPILE_ENVIRONMENT'] and not CONFIG['LIBXUL_SDK']:
     if CONFIG['USE_ICU']:
         DIRS += ['config/external/icu']
     DIRS += ['js/src']
+else:
+    TEST_DIRS += ['js/src/tests']
 
 if not CONFIG['JS_STANDALONE'] and CONFIG['MOZ_BUILD_APP']:
     # Bring in the configuration for the configured application.
diff --git a/netwerk/base/ProxyAutoConfig.cpp b/netwerk/base/ProxyAutoConfig.cpp
index 55c9955b74..ee01665662 100644
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -665,15 +665,19 @@ private:
   }
 };
 
-const JSClass JSRuntimeWrapper::sGlobalClass = {
-  "PACResolutionThreadGlobal",
-  JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps sJSRuntimeWrapperGlobalClassOps = {
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr,
   JS_GlobalObjectTraceHook
 };
 
+const JSClass JSRuntimeWrapper::sGlobalClass = {
+  "PACResolutionThreadGlobal",
+  JSCLASS_GLOBAL_FLAGS,
+  &sJSRuntimeWrapperGlobalClassOps
+};
+
 void
 ProxyAutoConfig::SetThreadLocalIndex(uint32_t index)
 {
diff --git a/netwerk/cookie/test/unit_ipc/xpcshell.ini b/netwerk/cookie/test/unit_ipc/xpcshell.ini
index 6ca62e6539..cd0c0e3155 100644
--- a/netwerk/cookie/test/unit_ipc/xpcshell.ini
+++ b/netwerk/cookie/test/unit_ipc/xpcshell.ini
@@ -2,6 +2,9 @@
 head = 
 tail = 
 skip-if = toolkit == 'android' || toolkit == 'gonk'
+support-files =
+  !/netwerk/cookie/test/unit/test_parser_0001.js
+  !/netwerk/cookie/test/unit/test_parser_0019.js
 
 [test_ipc_parser_0001.js]
 [test_ipc_parser_0019.js]
diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp
index 3d4fbfa7a8..8f6c0fc816 100644
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -2337,7 +2337,27 @@ HttpBaseChannel::SetRedirectMode(uint32_t aMode)
 NS_IMETHODIMP
 HttpBaseChannel::GetFetchCacheMode(uint32_t* aFetchCacheMode)
 {
-  *aFetchCacheMode = mFetchCacheMode;
+  NS_ENSURE_ARG_POINTER(aFetchCacheMode);
+
+  // If the fetch cache mode is overriden, then use it directly.
+  if (mFetchCacheMode != nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT) {
+    *aFetchCacheMode = mFetchCacheMode;
+    return NS_OK;
+  }
+
+  // Otherwise try to guess an appropriate cache mode from the load flags.
+  if (mLoadFlags & (INHIBIT_CACHING | LOAD_BYPASS_CACHE)) {
+    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE;
+  } else if (mLoadFlags & LOAD_BYPASS_CACHE) {
+    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD;
+  } else if (mLoadFlags & VALIDATE_ALWAYS) {
+    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE;
+  } else if (mLoadFlags & LOAD_FROM_CACHE) {
+    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE;
+  } else {
+    *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT;
+  }
+
   return NS_OK;
 }
 
diff --git a/netwerk/test/unit_ipc/xpcshell.ini b/netwerk/test/unit_ipc/xpcshell.ini
index 590544a538..b018541c73 100644
--- a/netwerk/test/unit_ipc/xpcshell.ini
+++ b/netwerk/test/unit_ipc/xpcshell.ini
@@ -3,6 +3,56 @@ head = head_channels_clone.js head_cc.js
 tail =
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files = child_app_offline.js
+  child_app_offline_notifications.js
+  !/netwerk/test/unit/test_XHR_redirects.js
+  !/netwerk/test/unit/test_bug248970_cookie.js
+  !/netwerk/test/unit/test_bug528292.js
+  !/netwerk/test/unit/test_cache_jar.js
+  !/netwerk/test/unit/test_cacheflags.js
+  !/netwerk/test/unit/test_channel_close.js
+  !/netwerk/test/unit/test_cookie_header.js
+  !/netwerk/test/unit/test_cookiejars.js
+  !/netwerk/test/unit/test_dns_cancel.js
+  !/netwerk/test/unit/test_dns_per_interface.js
+  !/netwerk/test/unit/test_dns_service.js
+  !/netwerk/test/unit/test_duplicate_headers.js
+  !/netwerk/test/unit/test_event_sink.js
+  !/netwerk/test/unit/test_getHost.js
+  !/netwerk/test/unit/test_head.js
+  !/netwerk/test/unit/test_headers.js
+  !/netwerk/test/unit/test_httpsuspend.js
+  !/netwerk/test/unit/test_post.js
+  !/netwerk/test/unit/test_predictor.js
+  !/netwerk/test/unit/test_progress.js
+  !/netwerk/test/unit/test_redirect-caching_canceled.js
+  !/netwerk/test/unit/test_redirect-caching_failure.js
+  !/netwerk/test/unit/test_redirect-caching_passing.js
+  !/netwerk/test/unit/test_redirect_canceled.js
+  !/netwerk/test/unit/test_redirect_different-protocol.js
+  !/netwerk/test/unit/test_redirect_failure.js
+  !/netwerk/test/unit/test_redirect_from_script.js
+  !/netwerk/test/unit/test_redirect_history.js
+  !/netwerk/test/unit/test_redirect_passing.js
+  !/netwerk/test/unit/test_reentrancy.js
+  !/netwerk/test/unit/test_reply_without_content_type.js
+  !/netwerk/test/unit/test_resumable_channel.js
+  !/netwerk/test/unit/test_simple.js
+  !/netwerk/test/unit/test_synthesized_response.js
+  !/netwerk/test/unit/test_xmlhttprequest.js
+  !/netwerk/test/unit/head_channels.js
+  !/netwerk/test/unit/head_cache2.js
+  !/netwerk/test/unit/data/image.png
+  !/netwerk/test/unit/data/system_root.lnk
+  !/netwerk/test/unit/data/test_psl.txt
+  !/netwerk/test/unit/data/test_readline1.txt
+  !/netwerk/test/unit/data/test_readline2.txt
+  !/netwerk/test/unit/data/test_readline3.txt
+  !/netwerk/test/unit/data/test_readline4.txt
+  !/netwerk/test/unit/data/test_readline5.txt
+  !/netwerk/test/unit/data/test_readline6.txt
+  !/netwerk/test/unit/data/test_readline7.txt
+  !/netwerk/test/unit/data/test_readline8.txt
+  !/netwerk/test/unit/data/signed_win.exe
 
 [test_bug528292_wrap.js]
 [test_bug248970_cookie_wrap.js]
diff --git a/services/crypto/tests/unit/xpcshell.ini b/services/crypto/tests/unit/xpcshell.ini
index c2346fbde2..86077130b1 100644
--- a/services/crypto/tests/unit/xpcshell.ini
+++ b/services/crypto/tests/unit/xpcshell.ini
@@ -3,6 +3,8 @@ head = head_helpers.js ../../../common/tests/unit/head_helpers.js
 tail =
 firefox-appdir = browser
 skip-if = toolkit == 'gonk'
+support-files =
+  !/services/common/tests/unit/head_helpers.js
 
 [test_load_modules.js]
 
diff --git a/services/fxaccounts/tests/xpcshell/xpcshell.ini b/services/fxaccounts/tests/xpcshell/xpcshell.ini
index c01b990233..ced340e1fd 100644
--- a/services/fxaccounts/tests/xpcshell/xpcshell.ini
+++ b/services/fxaccounts/tests/xpcshell/xpcshell.ini
@@ -1,7 +1,10 @@
 [DEFAULT]
 head = head.js ../../../common/tests/unit/head_helpers.js ../../../common/tests/unit/head_http.js
 tail =
-skip-if = toolkit == 'android'
+skip-if = (toolkit == 'android' || appname == 'thunderbird')
+support-files =
+  !/services/common/tests/unit/head_helpers.js
+  !/services/common/tests/unit/head_http.js
 
 [test_accounts.js]
 [test_client.js]
diff --git a/services/sync/tests/unit/xpcshell.ini b/services/sync/tests/unit/xpcshell.ini
index eaf28ae552..132549215b 100644
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -11,6 +11,8 @@ support-files =
   missing-xpi-search.xml
   places_v10_from_v11.sqlite
   rewrite-search.xml
+  !/services/common/tests/unit/head_helpers.js
+  !/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
 
 # The manifest is roughly ordered from low-level to high-level. When making
 # systemic sweeping changes, this makes it easier to identify errors closer to
diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json
index a94ece76a0..a6e0d7c1fe 100644
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -15529,6 +15529,10 @@
         "path": "html/semantics/embedded-content/the-canvas-element/context.unrecognised.badname.html",
         "url": "/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badname.html"
       },
+      {
+        "path": "fetch/api/request/request-cache.html",
+        "url": "/fetch/api/request/request-cache.html"
+      },
       {
         "path": "html/semantics/embedded-content/the-canvas-element/context.unrecognised.badsuffix.html",
         "url": "/html/semantics/embedded-content/the-canvas-element/context.unrecognised.badsuffix.html"
@@ -21612,6 +21616,602 @@
             "path": "dom/nodes/Element-insertAdjacentText.html",
             "url": "/dom/nodes/Element-insertAdjacentText.html"
           }
+        ],
+        "fetch/api/request/request-cache.html": [
+          {
+            "path": "fetch/api/request/request-cache.html",
+            "timeout": "long",
+            "url": "/fetch/api/request/request-cache.html"
+          }
+        ],
+        "service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html": [
+          {
+            "path": "service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html",
+            "url": "/service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html"
+          }
+        ],
+        "service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html": [
+          {
+            "path": "service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html",
+            "url": "/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html"
+          }
+        ],
+        "service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html": [
+          {
+            "path": "service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html",
+            "url": "/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html"
+          }
+        ],
+        "service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html": [
+          {
+            "path": "service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html",
+            "url": "/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html"
+          }
+        ],
+        "service-workers/service-worker/activate-event-after-install-state-change.https.html": [
+          {
+            "path": "service-workers/service-worker/activate-event-after-install-state-change.https.html",
+            "url": "/service-workers/service-worker/activate-event-after-install-state-change.https.html"
+          }
+        ],
+        "service-workers/service-worker/activation-after-registration.https.html": [
+          {
+            "path": "service-workers/service-worker/activation-after-registration.https.html",
+            "url": "/service-workers/service-worker/activation-after-registration.https.html"
+          }
+        ],
+        "service-workers/service-worker/active.https.html": [
+          {
+            "path": "service-workers/service-worker/active.https.html",
+            "url": "/service-workers/service-worker/active.https.html"
+          }
+        ],
+        "service-workers/service-worker/appcache-ordering-main.https.html": [
+          {
+            "path": "service-workers/service-worker/appcache-ordering-main.https.html",
+            "url": "/service-workers/service-worker/appcache-ordering-main.https.html"
+          }
+        ],
+        "service-workers/service-worker/claim-not-using-registration.https.html": [
+          {
+            "path": "service-workers/service-worker/claim-not-using-registration.https.html",
+            "url": "/service-workers/service-worker/claim-not-using-registration.https.html"
+          }
+        ],
+        "service-workers/service-worker/claim-using-registration.https.html": [
+          {
+            "path": "service-workers/service-worker/claim-using-registration.https.html",
+            "url": "/service-workers/service-worker/claim-using-registration.https.html"
+          }
+        ],
+        "service-workers/service-worker/clients-get-cross-origin.https.html": [
+          {
+            "path": "service-workers/service-worker/clients-get-cross-origin.https.html",
+            "url": "/service-workers/service-worker/clients-get-cross-origin.https.html"
+          }
+        ],
+        "service-workers/service-worker/clients-get.https.html": [
+          {
+            "path": "service-workers/service-worker/clients-get.https.html",
+            "url": "/service-workers/service-worker/clients-get.https.html"
+          }
+        ],
+        "service-workers/service-worker/clients-matchall-client-types.https.html": [
+          {
+            "path": "service-workers/service-worker/clients-matchall-client-types.https.html",
+            "url": "/service-workers/service-worker/clients-matchall-client-types.https.html"
+          }
+        ],
+        "service-workers/service-worker/clients-matchall-include-uncontrolled.https.html": [
+          {
+            "path": "service-workers/service-worker/clients-matchall-include-uncontrolled.https.html",
+            "url": "/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html"
+          }
+        ],
+        "service-workers/service-worker/clients-matchall.https.html": [
+          {
+            "path": "service-workers/service-worker/clients-matchall.https.html",
+            "url": "/service-workers/service-worker/clients-matchall.https.html"
+          }
+        ],
+        "service-workers/service-worker/controller-on-load.https.html": [
+          {
+            "path": "service-workers/service-worker/controller-on-load.https.html",
+            "url": "/service-workers/service-worker/controller-on-load.https.html"
+          }
+        ],
+        "service-workers/service-worker/controller-on-reload.https.html": [
+          {
+            "path": "service-workers/service-worker/controller-on-reload.https.html",
+            "url": "/service-workers/service-worker/controller-on-reload.https.html"
+          }
+        ],
+        "service-workers/service-worker/extendable-event-async-waituntil.https.html": [
+          {
+            "path": "service-workers/service-worker/extendable-event-async-waituntil.https.html",
+            "url": "/service-workers/service-worker/extendable-event-async-waituntil.https.html"
+          }
+        ],
+        "service-workers/service-worker/extendable-event-waituntil.https.html": [
+          {
+            "path": "service-workers/service-worker/extendable-event-waituntil.https.html",
+            "url": "/service-workers/service-worker/extendable-event-waituntil.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-canvas-tainting-cache.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-canvas-tainting-cache.https.html",
+            "url": "/service-workers/service-worker/fetch-canvas-tainting-cache.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-canvas-tainting.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-canvas-tainting.https.html",
+            "url": "/service-workers/service-worker/fetch-canvas-tainting.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-cors-xhr.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-cors-xhr.https.html",
+            "url": "/service-workers/service-worker/fetch-cors-xhr.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-csp.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-csp.https.html",
+            "url": "/service-workers/service-worker/fetch-csp.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-event-after-navigation-within-page.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-event-after-navigation-within-page.https.html",
+            "url": "/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-event-async-respond-with.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-event-async-respond-with.https.html",
+            "url": "/service-workers/service-worker/fetch-event-async-respond-with.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-event-network-error.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-event-network-error.https.html",
+            "url": "/service-workers/service-worker/fetch-event-network-error.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-event-redirect.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-event-redirect.https.html",
+            "timeout": "long",
+            "url": "/service-workers/service-worker/fetch-event-redirect.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html",
+            "url": "/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-event.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-event.https.html",
+            "url": "/service-workers/service-worker/fetch-event.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-frame-resource.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-frame-resource.https.html",
+            "timeout": "long",
+            "url": "/service-workers/service-worker/fetch-frame-resource.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-header-visibility.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-header-visibility.https.html",
+            "url": "/service-workers/service-worker/fetch-header-visibility.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-mixed-content-to-inscope.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-mixed-content-to-inscope.https.html",
+            "timeout": "long",
+            "url": "/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-mixed-content-to-outscope.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-mixed-content-to-outscope.https.html",
+            "timeout": "long",
+            "url": "/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-request-css-base-url.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-request-css-base-url.https.html",
+            "url": "/service-workers/service-worker/fetch-request-css-base-url.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-request-css-images.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-request-css-images.https.html",
+            "url": "/service-workers/service-worker/fetch-request-css-images.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-request-fallback.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-request-fallback.https.html",
+            "url": "/service-workers/service-worker/fetch-request-fallback.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-request-no-freshness-headers.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-request-no-freshness-headers.https.html",
+            "url": "/service-workers/service-worker/fetch-request-no-freshness-headers.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-request-redirect.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-request-redirect.https.html",
+            "timeout": "long",
+            "url": "/service-workers/service-worker/fetch-request-redirect.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-request-resources.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-request-resources.https.html",
+            "url": "/service-workers/service-worker/fetch-request-resources.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-request-xhr.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-request-xhr.https.html",
+            "url": "/service-workers/service-worker/fetch-request-xhr.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-response-xhr.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-response-xhr.https.html",
+            "url": "/service-workers/service-worker/fetch-response-xhr.https.html"
+          }
+        ],
+        "service-workers/service-worker/fetch-waits-for-activate.https.html": [
+          {
+            "path": "service-workers/service-worker/fetch-waits-for-activate.https.html",
+            "timeout": "long",
+            "url": "/service-workers/service-worker/fetch-waits-for-activate.https.html"
+          }
+        ],
+        "service-workers/service-worker/getregistration.https.html": [
+          {
+            "path": "service-workers/service-worker/getregistration.https.html",
+            "url": "/service-workers/service-worker/getregistration.https.html"
+          }
+        ],
+        "service-workers/service-worker/getregistrations.https.html": [
+          {
+            "path": "service-workers/service-worker/getregistrations.https.html",
+            "url": "/service-workers/service-worker/getregistrations.https.html"
+          }
+        ],
+        "service-workers/service-worker/indexeddb.https.html": [
+          {
+            "path": "service-workers/service-worker/indexeddb.https.html",
+            "url": "/service-workers/service-worker/indexeddb.https.html"
+          }
+        ],
+        "service-workers/service-worker/install-event-type.https.html": [
+          {
+            "path": "service-workers/service-worker/install-event-type.https.html",
+            "url": "/service-workers/service-worker/install-event-type.https.html"
+          }
+        ],
+        "service-workers/service-worker/installing.https.html": [
+          {
+            "path": "service-workers/service-worker/installing.https.html",
+            "url": "/service-workers/service-worker/installing.https.html"
+          }
+        ],
+        "service-workers/service-worker/interfaces.https.html": [
+          {
+            "path": "service-workers/service-worker/interfaces.https.html",
+            "url": "/service-workers/service-worker/interfaces.https.html"
+          }
+        ],
+        "service-workers/service-worker/invalid-blobtype.https.html": [
+          {
+            "path": "service-workers/service-worker/invalid-blobtype.https.html",
+            "url": "/service-workers/service-worker/invalid-blobtype.https.html"
+          }
+        ],
+        "service-workers/service-worker/invalid-header.https.html": [
+          {
+            "path": "service-workers/service-worker/invalid-header.https.html",
+            "url": "/service-workers/service-worker/invalid-header.https.html"
+          }
+        ],
+        "service-workers/service-worker/multiple-register.https.html": [
+          {
+            "path": "service-workers/service-worker/multiple-register.https.html",
+            "url": "/service-workers/service-worker/multiple-register.https.html"
+          }
+        ],
+        "service-workers/service-worker/multiple-update.https.html": [
+          {
+            "path": "service-workers/service-worker/multiple-update.https.html",
+            "url": "/service-workers/service-worker/multiple-update.https.html"
+          }
+        ],
+        "service-workers/service-worker/navigation-redirect.https.html": [
+          {
+            "path": "service-workers/service-worker/navigation-redirect.https.html",
+            "url": "/service-workers/service-worker/navigation-redirect.https.html"
+          }
+        ],
+        "service-workers/service-worker/onactivate-script-error.https.html": [
+          {
+            "path": "service-workers/service-worker/onactivate-script-error.https.html",
+            "url": "/service-workers/service-worker/onactivate-script-error.https.html"
+          }
+        ],
+        "service-workers/service-worker/oninstall-script-error.https.html": [
+          {
+            "path": "service-workers/service-worker/oninstall-script-error.https.html",
+            "url": "/service-workers/service-worker/oninstall-script-error.https.html"
+          }
+        ],
+        "service-workers/service-worker/performance-timeline.https.html": [
+          {
+            "path": "service-workers/service-worker/performance-timeline.https.html",
+            "url": "/service-workers/service-worker/performance-timeline.https.html"
+          }
+        ],
+        "service-workers/service-worker/postmessage-msgport-to-client.https.html": [
+          {
+            "path": "service-workers/service-worker/postmessage-msgport-to-client.https.html",
+            "url": "/service-workers/service-worker/postmessage-msgport-to-client.https.html"
+          }
+        ],
+        "service-workers/service-worker/postmessage-to-client.https.html": [
+          {
+            "path": "service-workers/service-worker/postmessage-to-client.https.html",
+            "url": "/service-workers/service-worker/postmessage-to-client.https.html"
+          }
+        ],
+        "service-workers/service-worker/postmessage.https.html": [
+          {
+            "path": "service-workers/service-worker/postmessage.https.html",
+            "url": "/service-workers/service-worker/postmessage.https.html"
+          }
+        ],
+        "service-workers/service-worker/ready.https.html": [
+          {
+            "path": "service-workers/service-worker/ready.https.html",
+            "url": "/service-workers/service-worker/ready.https.html"
+          }
+        ],
+        "service-workers/service-worker/referer.https.html": [
+          {
+            "path": "service-workers/service-worker/referer.https.html",
+            "url": "/service-workers/service-worker/referer.https.html"
+          }
+        ],
+        "service-workers/service-worker/register-closed-window.https.html": [
+          {
+            "path": "service-workers/service-worker/register-closed-window.https.html",
+            "timeout": "long",
+            "url": "/service-workers/service-worker/register-closed-window.https.html"
+          }
+        ],
+        "service-workers/service-worker/register-default-scope.https.html": [
+          {
+            "path": "service-workers/service-worker/register-default-scope.https.html",
+            "url": "/service-workers/service-worker/register-default-scope.https.html"
+          }
+        ],
+        "service-workers/service-worker/register-same-scope-different-script-url.https.html": [
+          {
+            "path": "service-workers/service-worker/register-same-scope-different-script-url.https.html",
+            "url": "/service-workers/service-worker/register-same-scope-different-script-url.https.html"
+          }
+        ],
+        "service-workers/service-worker/register-wait-forever-in-install-worker.https.html": [
+          {
+            "path": "service-workers/service-worker/register-wait-forever-in-install-worker.https.html",
+            "url": "/service-workers/service-worker/register-wait-forever-in-install-worker.https.html"
+          }
+        ],
+        "service-workers/service-worker/registration-end-to-end.https.html": [
+          {
+            "path": "service-workers/service-worker/registration-end-to-end.https.html",
+            "url": "/service-workers/service-worker/registration-end-to-end.https.html"
+          }
+        ],
+        "service-workers/service-worker/registration-events.https.html": [
+          {
+            "path": "service-workers/service-worker/registration-events.https.html",
+            "url": "/service-workers/service-worker/registration-events.https.html"
+          }
+        ],
+        "service-workers/service-worker/registration-iframe.https.html": [
+          {
+            "path": "service-workers/service-worker/registration-iframe.https.html",
+            "url": "/service-workers/service-worker/registration-iframe.https.html"
+          }
+        ],
+        "service-workers/service-worker/registration-service-worker-attributes.https.html": [
+          {
+            "path": "service-workers/service-worker/registration-service-worker-attributes.https.html",
+            "url": "/service-workers/service-worker/registration-service-worker-attributes.https.html"
+          }
+        ],
+        "service-workers/service-worker/registration.https.html": [
+          {
+            "path": "service-workers/service-worker/registration.https.html",
+            "url": "/service-workers/service-worker/registration.https.html"
+          }
+        ],
+        "service-workers/service-worker/rejections.https.html": [
+          {
+            "path": "service-workers/service-worker/rejections.https.html",
+            "url": "/service-workers/service-worker/rejections.https.html"
+          }
+        ],
+        "service-workers/service-worker/request-end-to-end.https.html": [
+          {
+            "path": "service-workers/service-worker/request-end-to-end.https.html",
+            "url": "/service-workers/service-worker/request-end-to-end.https.html"
+          }
+        ],
+        "service-workers/service-worker/resource-timing.https.html": [
+          {
+            "path": "service-workers/service-worker/resource-timing.https.html",
+            "url": "/service-workers/service-worker/resource-timing.https.html"
+          }
+        ],
+        "service-workers/service-worker/service-worker-csp-connect.https.html": [
+          {
+            "path": "service-workers/service-worker/service-worker-csp-connect.https.html",
+            "url": "/service-workers/service-worker/service-worker-csp-connect.https.html"
+          }
+        ],
+        "service-workers/service-worker/service-worker-csp-default.https.html": [
+          {
+            "path": "service-workers/service-worker/service-worker-csp-default.https.html",
+            "url": "/service-workers/service-worker/service-worker-csp-default.https.html"
+          }
+        ],
+        "service-workers/service-worker/service-worker-csp-script.https.html": [
+          {
+            "path": "service-workers/service-worker/service-worker-csp-script.https.html",
+            "url": "/service-workers/service-worker/service-worker-csp-script.https.html"
+          }
+        ],
+        "service-workers/service-worker/serviceworkerobject-scripturl.https.html": [
+          {
+            "path": "service-workers/service-worker/serviceworkerobject-scripturl.https.html",
+            "url": "/service-workers/service-worker/serviceworkerobject-scripturl.https.html"
+          }
+        ],
+        "service-workers/service-worker/shared-worker-controlled.https.html": [
+          {
+            "path": "service-workers/service-worker/shared-worker-controlled.https.html",
+            "url": "/service-workers/service-worker/shared-worker-controlled.https.html"
+          }
+        ],
+        "service-workers/service-worker/skip-waiting-installed.https.html": [
+          {
+            "path": "service-workers/service-worker/skip-waiting-installed.https.html",
+            "url": "/service-workers/service-worker/skip-waiting-installed.https.html"
+          }
+        ],
+        "service-workers/service-worker/skip-waiting-using-registration.https.html": [
+          {
+            "path": "service-workers/service-worker/skip-waiting-using-registration.https.html",
+            "url": "/service-workers/service-worker/skip-waiting-using-registration.https.html"
+          }
+        ],
+        "service-workers/service-worker/skip-waiting-without-client.https.html": [
+          {
+            "path": "service-workers/service-worker/skip-waiting-without-client.https.html",
+            "url": "/service-workers/service-worker/skip-waiting-without-client.https.html"
+          }
+        ],
+        "service-workers/service-worker/skip-waiting-without-using-registration.https.html": [
+          {
+            "path": "service-workers/service-worker/skip-waiting-without-using-registration.https.html",
+            "url": "/service-workers/service-worker/skip-waiting-without-using-registration.https.html"
+          }
+        ],
+        "service-workers/service-worker/skip-waiting.https.html": [
+          {
+            "path": "service-workers/service-worker/skip-waiting.https.html",
+            "url": "/service-workers/service-worker/skip-waiting.https.html"
+          }
+        ],
+        "service-workers/service-worker/state.https.html": [
+          {
+            "path": "service-workers/service-worker/state.https.html",
+            "url": "/service-workers/service-worker/state.https.html"
+          }
+        ],
+        "service-workers/service-worker/synced-state.https.html": [
+          {
+            "path": "service-workers/service-worker/synced-state.https.html",
+            "url": "/service-workers/service-worker/synced-state.https.html"
+          }
+        ],
+        "service-workers/service-worker/uncontrolled-page.https.html": [
+          {
+            "path": "service-workers/service-worker/uncontrolled-page.https.html",
+            "url": "/service-workers/service-worker/uncontrolled-page.https.html"
+          }
+        ],
+        "service-workers/service-worker/unregister-controller.https.html": [
+          {
+            "path": "service-workers/service-worker/unregister-controller.https.html",
+            "url": "/service-workers/service-worker/unregister-controller.https.html"
+          }
+        ],
+        "service-workers/service-worker/unregister-then-register-new-script.https.html": [
+          {
+            "path": "service-workers/service-worker/unregister-then-register-new-script.https.html",
+            "url": "/service-workers/service-worker/unregister-then-register-new-script.https.html"
+          }
+        ],
+        "service-workers/service-worker/unregister-then-register.https.html": [
+          {
+            "path": "service-workers/service-worker/unregister-then-register.https.html",
+            "url": "/service-workers/service-worker/unregister-then-register.https.html"
+          }
+        ],
+        "service-workers/service-worker/unregister.https.html": [
+          {
+            "path": "service-workers/service-worker/unregister.https.html",
+            "url": "/service-workers/service-worker/unregister.https.html"
+          }
+        ],
+        "service-workers/service-worker/update-after-navigation-fetch-event.https.html": [
+          {
+            "path": "service-workers/service-worker/update-after-navigation-fetch-event.https.html",
+            "url": "/service-workers/service-worker/update-after-navigation-fetch-event.https.html"
+          }
+        ],
+        "service-workers/service-worker/update-after-oneday.https.html": [
+          {
+            "path": "service-workers/service-worker/update-after-oneday.https.html",
+            "url": "/service-workers/service-worker/update-after-oneday.https.html"
+          }
+        ],
+        "service-workers/service-worker/update.https.html": [
+          {
+            "path": "service-workers/service-worker/update.https.html",
+            "url": "/service-workers/service-worker/update.https.html"
+          }
+        ],
+        "service-workers/service-worker/waiting.https.html": [
+          {
+            "path": "service-workers/service-worker/waiting.https.html",
+            "url": "/service-workers/service-worker/waiting.https.html"
+          }
+        ],
+        "service-workers/service-worker/websocket.https.html": [
+          {
+            "path": "service-workers/service-worker/websocket.https.html",
+            "url": "/service-workers/service-worker/websocket.https.html"
+          }
+        ],
+        "service-workers/service-worker/worker-interception.https.html": [
+          {
+            "path": "service-workers/service-worker/worker-interception.https.html",
+            "url": "/service-workers/service-worker/worker-interception.https.html"
+          }
+        ],
+        "service-workers/service-worker/xhr.https.html": [
+          {
+            "path": "service-workers/service-worker/xhr.https.html",
+            "url": "/service-workers/service-worker/xhr.https.html"
+          }
         ]
       }
     }
diff --git a/testing/web-platform/meta/fetch/api/policies/referrer-origin-worker.html.ini b/testing/web-platform/meta/fetch/api/policies/referrer-origin-worker.html.ini
new file mode 100644
index 0000000000..3265e37b9c
--- /dev/null
+++ b/testing/web-platform/meta/fetch/api/policies/referrer-origin-worker.html.ini
@@ -0,0 +1,6 @@
+[referrer-origin-worker.html]
+  type: testharness
+  [Request's referrer is origin]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1251378
+
diff --git a/testing/web-platform/meta/fetch/api/request/request-error.html.ini b/testing/web-platform/meta/fetch/api/request/request-error.html.ini
new file mode 100644
index 0000000000..7c9b0cf8a0
--- /dev/null
+++ b/testing/web-platform/meta/fetch/api/request/request-error.html.ini
@@ -0,0 +1,8 @@
+[request-error.html]
+  type: testharness
+  [RequestInit's window is not null]
+    expected: FAIL
+
+  [RequestInit's mode is no-cors and integrity is not empty]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/fetch/api/request/request-idl.html.ini b/testing/web-platform/meta/fetch/api/request/request-idl.html.ini
new file mode 100644
index 0000000000..bfcf39dbe4
--- /dev/null
+++ b/testing/web-platform/meta/fetch/api/request/request-idl.html.ini
@@ -0,0 +1,20 @@
+[request-idl.html]
+  type: testharness
+  [Request interface: attribute type]
+    expected: FAIL
+
+  [Request interface: attribute destination]
+    expected: FAIL
+
+  [Request interface: attribute integrity]
+    expected: FAIL
+
+  [Request interface: new Request("") must inherit property "type" with the proper type (3)]
+    expected: FAIL
+
+  [Request interface: new Request("") must inherit property "destination" with the proper type (4)]
+    expected: FAIL
+
+  [Request interface: new Request("") must inherit property "integrity" with the proper type (11)]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/fetch/api/request/request-init-001.sub.html.ini b/testing/web-platform/meta/fetch/api/request/request-init-001.sub.html.ini
new file mode 100644
index 0000000000..1efcdfaf49
--- /dev/null
+++ b/testing/web-platform/meta/fetch/api/request/request-init-001.sub.html.ini
@@ -0,0 +1,8 @@
+[request-init-001.sub.html]
+  type: testharness
+  [Check integrity init value of  and associated getter]
+    expected: FAIL
+
+  [Check integrity init value of AZERTYUIOP1234567890 and associated getter]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/fetch/api/request/request-structure.html.ini b/testing/web-platform/meta/fetch/api/request/request-structure.html.ini
new file mode 100644
index 0000000000..1764b2e7d5
--- /dev/null
+++ b/testing/web-platform/meta/fetch/api/request/request-structure.html.ini
@@ -0,0 +1,11 @@
+[request-structure.html]
+  type: testharness
+  [Check type attribute]
+    expected: FAIL
+
+  [Check destination attribute]
+    expected: FAIL
+
+  [Check integrity attribute]
+    expected: FAIL
+
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/__dir__.ini b/testing/web-platform/meta/service-workers/service-worker/__dir__.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/__dir__.ini
rename to testing/web-platform/meta/service-workers/service-worker/__dir__.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/fetch-event.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/fetch-event.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-images.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/fetch-request-css-images.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-images.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/fetch-request-css-images.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/performance-timeline.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/performance-timeline.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/performance-timeline.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/performance-timeline.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/resource-timing.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/resource-timing.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/resource-timing.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/resource-timing.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/service-worker-csp-connect.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/service-worker-csp-connect.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/service-worker-csp-connect.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/service-worker-csp-connect.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/service-worker-csp-default.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/service-worker-csp-default.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/service-worker-csp-default.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/service-worker-csp-default.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/service-worker-csp-script.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/service-worker-csp-script.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/service-worker-csp-script.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/service-worker-csp-script.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/shared-worker-controlled.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/shared-worker-controlled.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/shared-worker-controlled.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/shared-worker-controlled.https.html.ini
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/update-after-oneday.https.html.ini
similarity index 100%
rename from testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini
rename to testing/web-platform/meta/service-workers/service-worker/update-after-oneday.https.html.ini
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html
deleted file mode 100644
index 76104600e0..0000000000
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html
+++ /dev/null
@@ -1,245 +0,0 @@
-
-
-
-
-
-
-
diff --git a/testing/web-platform/tests/fetch/api/request/request-cache.html b/testing/web-platform/tests/fetch/api/request/request-cache.html
new file mode 100644
index 0000000000..f6cce74d68
--- /dev/null
+++ b/testing/web-platform/tests/fetch/api/request/request-cache.html
@@ -0,0 +1,480 @@
+
+
+  
+    
+    Request cache
+    
+    
+    
+    
+    
+  
+  
+    
+  
+
diff --git a/testing/web-platform/tests/fetch/api/request/request-idl.html b/testing/web-platform/tests/fetch/api/request/request-idl.html
new file mode 100644
index 0000000000..4f1590c756
--- /dev/null
+++ b/testing/web-platform/tests/fetch/api/request/request-idl.html
@@ -0,0 +1,86 @@
+
+
+  
+    
+    Request idl interface
+    
+    
+    
+    
+    
+    
+  
+  
+    
+    
+    
+  
+
diff --git a/testing/web-platform/tests/fetch/api/request/request-init-002.html b/testing/web-platform/tests/fetch/api/request/request-init-002.html
new file mode 100644
index 0000000000..f08c06d0d7
--- /dev/null
+++ b/testing/web-platform/tests/fetch/api/request/request-init-002.html
@@ -0,0 +1,72 @@
+
+
+  
+    
+    Request init: headers and body
+    
+    
+    
+    
+  
+  
+    
+  
+
diff --git a/testing/web-platform/tests/fetch/api/request/resources/cache.py b/testing/web-platform/tests/fetch/api/request/resources/cache.py
new file mode 100644
index 0000000000..f0fe4fe247
--- /dev/null
+++ b/testing/web-platform/tests/fetch/api/request/resources/cache.py
@@ -0,0 +1,53 @@
+def main(request, response):
+    token = request.GET.first("token", None)
+    if "querystate" in request.GET:
+        from json import JSONEncoder
+        response.headers.set("Content-Type", "text/plain")
+        return JSONEncoder().encode(request.server.stash.take(token))
+    content = request.GET.first("content", None)
+    tag = request.GET.first("tag", None)
+    date = request.GET.first("date", None)
+    expires = request.GET.first("expires", None)
+    vary = request.GET.first("vary", None)
+    cc = request.GET.first("cache_control", None)
+    inm = request.headers.get("If-None-Match", None)
+    ims = request.headers.get("If-Modified-Since", None)
+    pragma = request.headers.get("Pragma", None)
+    cache_control = request.headers.get("Cache-Control", None)
+    ignore = "ignore" in request.GET
+
+    server_state = request.server.stash.take(token)
+    if not server_state:
+        server_state = []
+    state = dict()
+    if not ignore:
+        if inm:
+            state["If-None-Match"] = inm
+        if ims:
+            state["If-Modified-Since"] = ims
+        if pragma:
+            state["Pragma"] = pragma
+        if cache_control:
+            state["Cache-Control"] = cache_control
+    server_state.append(state)
+    request.server.stash.put(token, server_state)
+
+    if tag:
+        response.headers.set("ETag", '"%s"' % tag)
+    elif date:
+        response.headers.set("Last-Modified", date)
+    if expires:
+        response.headers.set("Expires", expires)
+    if vary:
+        response.headers.set("Vary", vary)
+    if cc:
+        response.headers.set("Cache-Control", cc)
+
+    if ((inm is not None and inm == tag) or
+        (ims is not None and ims == date)):
+        response.status = (304, "Not Modified")
+        return ""
+    else:
+        response.status = (200, "OK")
+        response.headers.set("Content-Type", "text/plain")
+        return content
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html b/testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html
rename to testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html b/testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html
rename to testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/close-worker.js b/testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/close-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/close-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/close-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/registration-attribute-worker.js b/testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/registration-attribute-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/registration-attribute-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/registration-attribute-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/unregister-worker.js b/testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/unregister-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/unregister-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/resources/unregister-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html b/testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
rename to testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html b/testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html
rename to testing/web-platform/tests/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/activate-event-after-install-state-change.https.html b/testing/web-platform/tests/service-workers/service-worker/activate-event-after-install-state-change.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/activate-event-after-install-state-change.https.html
rename to testing/web-platform/tests/service-workers/service-worker/activate-event-after-install-state-change.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/activation-after-registration.https.html b/testing/web-platform/tests/service-workers/service-worker/activation-after-registration.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/activation-after-registration.https.html
rename to testing/web-platform/tests/service-workers/service-worker/activation-after-registration.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/active.https.html b/testing/web-platform/tests/service-workers/service-worker/active.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/active.https.html
rename to testing/web-platform/tests/service-workers/service-worker/active.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html b/testing/web-platform/tests/service-workers/service-worker/appcache-ordering-main.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html
rename to testing/web-platform/tests/service-workers/service-worker/appcache-ordering-main.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/claim-not-using-registration.https.html b/testing/web-platform/tests/service-workers/service-worker/claim-not-using-registration.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/claim-not-using-registration.https.html
rename to testing/web-platform/tests/service-workers/service-worker/claim-not-using-registration.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/claim-using-registration.https.html b/testing/web-platform/tests/service-workers/service-worker/claim-using-registration.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/claim-using-registration.https.html
rename to testing/web-platform/tests/service-workers/service-worker/claim-using-registration.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-get-cross-origin.https.html b/testing/web-platform/tests/service-workers/service-worker/clients-get-cross-origin.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/clients-get-cross-origin.https.html
rename to testing/web-platform/tests/service-workers/service-worker/clients-get-cross-origin.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-get.https.html b/testing/web-platform/tests/service-workers/service-worker/clients-get.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/clients-get.https.html
rename to testing/web-platform/tests/service-workers/service-worker/clients-get.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-client-types.https.html b/testing/web-platform/tests/service-workers/service-worker/clients-matchall-client-types.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-client-types.https.html
rename to testing/web-platform/tests/service-workers/service-worker/clients-matchall-client-types.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html b/testing/web-platform/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html
rename to testing/web-platform/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall.https.html b/testing/web-platform/tests/service-workers/service-worker/clients-matchall.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall.https.html
rename to testing/web-platform/tests/service-workers/service-worker/clients-matchall.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html b/testing/web-platform/tests/service-workers/service-worker/controller-on-load.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html
rename to testing/web-platform/tests/service-workers/service-worker/controller-on-load.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-reload.https.html b/testing/web-platform/tests/service-workers/service-worker/controller-on-reload.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-reload.https.html
rename to testing/web-platform/tests/service-workers/service-worker/controller-on-reload.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/extendable-event-async-waituntil.https.html b/testing/web-platform/tests/service-workers/service-worker/extendable-event-async-waituntil.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/extendable-event-async-waituntil.https.html
rename to testing/web-platform/tests/service-workers/service-worker/extendable-event-async-waituntil.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/extendable-event-waituntil.https.html b/testing/web-platform/tests/service-workers/service-worker/extendable-event-waituntil.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/extendable-event-waituntil.https.html
rename to testing/web-platform/tests/service-workers/service-worker/extendable-event-waituntil.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-canvas-tainting-cache.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-canvas-tainting-cache.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-canvas-tainting-cache.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-canvas-tainting-cache.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-canvas-tainting.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-canvas-tainting.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-canvas-tainting.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-canvas-tainting.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-cors-xhr.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-cors-xhr.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-cors-xhr.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-cors-xhr.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-csp.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-csp.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-event-after-navigation-within-page.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-async-respond-with.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-event-async-respond-with.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-async-respond-with.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-event-async-respond-with.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-network-error.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-event-network-error.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-network-error.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-event-network-error.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-event-redirect.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-event-redirect.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html
diff --git a/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html
new file mode 100644
index 0000000000..cbb842d2c7
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html
@@ -0,0 +1,532 @@
+
+
+
+
+
+
+
+
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-frame-resource.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-frame-resource.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-header-visibility.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-header-visibility.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-mixed-content-to-inscope.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-base-url.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-request-css-base-url.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-base-url.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-request-css-base-url.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-images.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-request-css-images.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-images.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-request-css-images.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-fallback.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-request-fallback.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-fallback.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-request-fallback.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-no-freshness-headers.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-request-no-freshness-headers.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-no-freshness-headers.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-request-no-freshness-headers.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-redirect.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-request-redirect.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-redirect.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-request-redirect.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-resources.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-request-resources.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-resources.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-request-resources.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-xhr.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-request-xhr.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-xhr.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-request-xhr.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-response-xhr.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-response-xhr.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-response-xhr.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-response-xhr.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-waits-for-activate.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html
rename to testing/web-platform/tests/service-workers/service-worker/fetch-waits-for-activate.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistration.https.html b/testing/web-platform/tests/service-workers/service-worker/getregistration.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/getregistration.https.html
rename to testing/web-platform/tests/service-workers/service-worker/getregistration.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistrations.https.html b/testing/web-platform/tests/service-workers/service-worker/getregistrations.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/getregistrations.https.html
rename to testing/web-platform/tests/service-workers/service-worker/getregistrations.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/indexeddb.https.html b/testing/web-platform/tests/service-workers/service-worker/indexeddb.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/indexeddb.https.html
rename to testing/web-platform/tests/service-workers/service-worker/indexeddb.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/install-event-type.https.html b/testing/web-platform/tests/service-workers/service-worker/install-event-type.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/install-event-type.https.html
rename to testing/web-platform/tests/service-workers/service-worker/install-event-type.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/installing.https.html b/testing/web-platform/tests/service-workers/service-worker/installing.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/installing.https.html
rename to testing/web-platform/tests/service-workers/service-worker/installing.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/interfaces.https.html b/testing/web-platform/tests/service-workers/service-worker/interfaces.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/interfaces.https.html
rename to testing/web-platform/tests/service-workers/service-worker/interfaces.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-blobtype.https.html b/testing/web-platform/tests/service-workers/service-worker/invalid-blobtype.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-blobtype.https.html
rename to testing/web-platform/tests/service-workers/service-worker/invalid-blobtype.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-header.https.html b/testing/web-platform/tests/service-workers/service-worker/invalid-header.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-header.https.html
rename to testing/web-platform/tests/service-workers/service-worker/invalid-header.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-register.https.html b/testing/web-platform/tests/service-workers/service-worker/multiple-register.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-register.https.html
rename to testing/web-platform/tests/service-workers/service-worker/multiple-register.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-update.https.html b/testing/web-platform/tests/service-workers/service-worker/multiple-update.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-update.https.html
rename to testing/web-platform/tests/service-workers/service-worker/multiple-update.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/navigation-redirect.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-redirect.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/navigation-redirect.https.html
rename to testing/web-platform/tests/service-workers/service-worker/navigation-redirect.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/onactivate-script-error.https.html b/testing/web-platform/tests/service-workers/service-worker/onactivate-script-error.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/onactivate-script-error.https.html
rename to testing/web-platform/tests/service-workers/service-worker/onactivate-script-error.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/oninstall-script-error.https.html b/testing/web-platform/tests/service-workers/service-worker/oninstall-script-error.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/oninstall-script-error.https.html
rename to testing/web-platform/tests/service-workers/service-worker/oninstall-script-error.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/performance-timeline.https.html b/testing/web-platform/tests/service-workers/service-worker/performance-timeline.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/performance-timeline.https.html
rename to testing/web-platform/tests/service-workers/service-worker/performance-timeline.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html b/testing/web-platform/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html
rename to testing/web-platform/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-to-client.https.html b/testing/web-platform/tests/service-workers/service-worker/postmessage-to-client.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-to-client.https.html
rename to testing/web-platform/tests/service-workers/service-worker/postmessage-to-client.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage.https.html b/testing/web-platform/tests/service-workers/service-worker/postmessage.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage.https.html
rename to testing/web-platform/tests/service-workers/service-worker/postmessage.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/ready.https.html b/testing/web-platform/tests/service-workers/service-worker/ready.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/ready.https.html
rename to testing/web-platform/tests/service-workers/service-worker/ready.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/referer.https.html b/testing/web-platform/tests/service-workers/service-worker/referer.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/referer.https.html
rename to testing/web-platform/tests/service-workers/service-worker/referer.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/register-closed-window.https.html b/testing/web-platform/tests/service-workers/service-worker/register-closed-window.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/register-closed-window.https.html
rename to testing/web-platform/tests/service-workers/service-worker/register-closed-window.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/register-default-scope.https.html b/testing/web-platform/tests/service-workers/service-worker/register-default-scope.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/register-default-scope.https.html
rename to testing/web-platform/tests/service-workers/service-worker/register-default-scope.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/register-same-scope-different-script-url.https.html b/testing/web-platform/tests/service-workers/service-worker/register-same-scope-different-script-url.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/register-same-scope-different-script-url.https.html
rename to testing/web-platform/tests/service-workers/service-worker/register-same-scope-different-script-url.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/register-wait-forever-in-install-worker.https.html b/testing/web-platform/tests/service-workers/service-worker/register-wait-forever-in-install-worker.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/register-wait-forever-in-install-worker.https.html
rename to testing/web-platform/tests/service-workers/service-worker/register-wait-forever-in-install-worker.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/registration-end-to-end.https.html b/testing/web-platform/tests/service-workers/service-worker/registration-end-to-end.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/registration-end-to-end.https.html
rename to testing/web-platform/tests/service-workers/service-worker/registration-end-to-end.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/registration-events.https.html b/testing/web-platform/tests/service-workers/service-worker/registration-events.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/registration-events.https.html
rename to testing/web-platform/tests/service-workers/service-worker/registration-events.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/registration-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/registration-iframe.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/registration-iframe.https.html
rename to testing/web-platform/tests/service-workers/service-worker/registration-iframe.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/registration-service-worker-attributes.https.html b/testing/web-platform/tests/service-workers/service-worker/registration-service-worker-attributes.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/registration-service-worker-attributes.https.html
rename to testing/web-platform/tests/service-workers/service-worker/registration-service-worker-attributes.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/registration.https.html b/testing/web-platform/tests/service-workers/service-worker/registration.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/registration.https.html
rename to testing/web-platform/tests/service-workers/service-worker/registration.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/rejections.https.html b/testing/web-platform/tests/service-workers/service-worker/rejections.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/rejections.https.html
rename to testing/web-platform/tests/service-workers/service-worker/rejections.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/request-end-to-end.https.html b/testing/web-platform/tests/service-workers/service-worker/request-end-to-end.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/request-end-to-end.https.html
rename to testing/web-platform/tests/service-workers/service-worker/request-end-to-end.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resource-timing.https.html b/testing/web-platform/tests/service-workers/service-worker/resource-timing.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resource-timing.https.html
rename to testing/web-platform/tests/service-workers/service-worker/resource-timing.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/appcache-ordering.install.html b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.install.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/appcache-ordering.install.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.install.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.html b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.js b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/appcache-ordering.manifest b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.manifest
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/appcache-ordering.manifest
rename to testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.manifest
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/blank.html b/testing/web-platform/tests/service-workers/service-worker/resources/blank.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/blank.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/blank.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/claim-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/claim-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/claim-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/claim-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-get-frame.html b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-frame.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-get-frame.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/clients-get-frame.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-get-other-origin.html b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-other-origin.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-get-other-origin.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/clients-get-other-origin.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-get-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-get-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/clients-get-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-client-types-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-client-types-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/clients-matchall-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-shared-worker-interceptor.js b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-shared-worker-interceptor.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-shared-worker-interceptor.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/dummy-shared-worker-interceptor.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-script.py b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-script.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-script.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-script.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy.html b/testing/web-platform/tests/service-workers/service-worker/resources/dummy.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/dummy.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/empty-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/empty-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/empty-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/empty-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/silence.oga b/testing/web-platform/tests/service-workers/service-worker/resources/empty.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/silence.oga
rename to testing/web-platform/tests/service-workers/service-worker/resources/empty.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/end-to-end-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/end-to-end-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/end-to-end-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/end-to-end-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/events-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/events-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/events-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/events-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/extendable-event-waituntil.js b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-waituntil.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/extendable-event-waituntil.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-waituntil.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fail-on-fetch-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fail-on-fetch-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fail-on-fetch-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fail-on-fetch-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-access-control-login.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control-login.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-access-control-login.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control-login.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-canvas-tainting-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-canvas-tainting-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-canvas-tainting-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-canvas-tainting-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-cors-xhr-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-cors-xhr-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-cors-xhr-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-cors-xhr-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-after-navigation-within-page-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-after-navigation-within-page-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-after-navigation-within-page-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-after-navigation-within-page-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-network-error-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-network-error-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-respond-with-stops-propagation-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-respond-with-stops-propagation-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-respond-with-stops-propagation-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-respond-with-stops-propagation-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
similarity index 66%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
index 1443681f41..44ea828c68 100644
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
@@ -11,6 +11,17 @@ function handleReferrer(event) {
     ['Referrer: ' + event.request.referrer])));
 }
 
+function handleReferrerPolicy(event) {
+  event.respondWith(new Response(new Blob(
+    ['ReferrerPolicy: ' + event.request.referrerPolicy])));
+}
+
+function handleReferrerFull(event) {
+  event.respondWith(new Response(new Blob(
+    ['Referrer: ' + event.request.referrer + '\n' +
+     'ReferrerPolicy: ' + event.request.referrerPolicy])));
+}
+
 function handleClientId(event) {
   var body;
   if (event.clientId !== null) {
@@ -70,11 +81,43 @@ function handleUsedCheck(event) {
   }
 }
 
+function handleFragmentCheck(event) {
+  var body;
+  if (event.request.url.indexOf('#') === -1) {
+    body = 'Fragment Not Found';
+  } else {
+    body = 'Fragment Found';
+  }
+  event.respondWith(new Response(body));
+}
+
+function handleCache(event) {
+  event.respondWith(new Response(event.request.cache));
+}
+
+function handleEventSource(event) {
+  if (event.request.mode === 'navigate') {
+    return;
+  }
+  var data = {
+    mode: event.request.mode,
+    cache: event.request.cache,
+    credentials: event.request.credentials
+  };
+  var body = 'data:' + JSON.stringify(data) + '\n\n';
+  event.respondWith(new Response(body, {
+      headers: { 'Content-Type': 'text/event-stream' }
+    }
+  ));
+}
+
 self.addEventListener('fetch', function(event) {
     var url = event.request.url;
     var handlers = [
       { pattern: '?string', fn: handleString },
       { pattern: '?blob', fn: handleBlob },
+      { pattern: '?referrerFull', fn: handleReferrerFull },
+      { pattern: '?referrerPolicy', fn: handleReferrerPolicy },
       { pattern: '?referrer', fn: handleReferrer },
       { pattern: '?clientId', fn: handleClientId },
       { pattern: '?ignore', fn: function() {} },
@@ -82,7 +125,10 @@ self.addEventListener('fetch', function(event) {
       { pattern: '?fetch', fn: handleFetch },
       { pattern: '?form-post', fn: handleFormPost },
       { pattern: '?multiple-respond-with', fn: handleMultipleRespondWith },
-      { pattern: '?used-check', fn: handleUsedCheck }
+      { pattern: '?used-check', fn: handleUsedCheck },
+      { pattern: '?fragment-check', fn: handleFragmentCheck },
+      { pattern: '?cache', fn: handleCache },
+      { pattern: '?eventsource', fn: handleEventSource },
     ];
 
     var handler = null;
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-header-visibility-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-header-visibility-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-header-visibility-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-header-visibility-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-inscope.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-inscope.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-inscope.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-inscope.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-outscope.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-outscope.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-outscope.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-outscope.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-style.css b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-style.css
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-style.css
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-style.css
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-fallback-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-fallback-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-fallback-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-fallback-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-redirect-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-redirect-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-redirect-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-redirect-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-xhr-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-xhr-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-response-xhr-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-response-xhr-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-waits-for-activate-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-waits-for-activate-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-waits-for-activate-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/fetch-waits-for-activate-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/frame-for-getregistrations.html b/testing/web-platform/tests/service-workers/service-worker/resources/frame-for-getregistrations.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/frame-for-getregistrations.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/frame-for-getregistrations.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/get-host-info.sub.js b/testing/web-platform/tests/service-workers/service-worker/resources/get-host-info.sub.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/get-host-info.sub.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/get-host-info.sub.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/indexeddb-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/indexeddb-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/indexeddb-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/indexeddb-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/install-event-type-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/install-event-type-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/install-event-type-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/install-event-type-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/interfaces-worker.sub.js b/testing/web-platform/tests/service-workers/service-worker/resources/interfaces-worker.sub.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/interfaces-worker.sub.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/interfaces-worker.sub.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/interfaces.js b/testing/web-platform/tests/service-workers/service-worker/resources/interfaces.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/interfaces.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/interfaces.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-header-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-iframe.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-header-iframe.https.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-iframe.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-header-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-header-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/load_worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/load_worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/load_worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/load_worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/malformed-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/malformed-worker.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/malformed-worker.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/malformed-worker.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/mime-type-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/mime-type-worker.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/mime-type-worker.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/mime-type-worker.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/navigation-redirect-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-from-nested-event-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-from-nested-event-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-from-nested-event-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-from-nested-event-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-then-cancel-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-cancel-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-then-cancel-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-cancel-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-then-prevent-default-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-prevent-default-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-then-prevent-default-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-prevent-default-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-with-empty-onerror-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-with-empty-onerror-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-with-empty-onerror-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-with-empty-onerror-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/onactivate-throw-error-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-from-nested-event-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-from-nested-event-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-from-nested-event-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-from-nested-event-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-then-cancel-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-cancel-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-then-cancel-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-cancel-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-then-prevent-default-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-prevent-default-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-then-prevent-default-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-prevent-default-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-with-empty-onerror-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-with-empty-onerror-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-with-empty-onerror-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-with-empty-onerror-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/oninstall-throw-error-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/other.html b/testing/web-platform/tests/service-workers/service-worker/resources/other.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/other.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/other.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/override_assert_object_equals.js b/testing/web-platform/tests/service-workers/service-worker/resources/override_assert_object_equals.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/override_assert_object_equals.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/override_assert_object_equals.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/postmessage-msgport-to-client-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-msgport-to-client-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/postmessage-msgport-to-client-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/postmessage-msgport-to-client-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/postmessage-to-client-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-to-client-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/postmessage-to-client-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/postmessage-to-client-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/postmessage-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/postmessage-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/postmessage-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/redirect.py b/testing/web-platform/tests/service-workers/service-worker/resources/redirect.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/redirect.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/redirect.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/referer-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/referer-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/referer-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/referer-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/register-closed-window-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/register-closed-window-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/registration-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/registration-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/registration-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/registration-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/reject-install-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/reject-install-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/reject-install-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/reject-install-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/request-end-to-end-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/request-end-to-end-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/request-end-to-end-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/request-end-to-end-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/resource-timing-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-iframe.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/resource-timing-iframe.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-iframe.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/resource-timing-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/resource-timing-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/shared-worker-controlled.js b/testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-controlled.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/shared-worker-controlled.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-controlled.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/shared-worker-import.js b/testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-import.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/shared-worker-import.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-import.js
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/silence.oga b/testing/web-platform/tests/service-workers/service-worker/resources/silence.oga
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/simple-intercept-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/simple-intercept-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/simple-intercept-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/simple-intercept-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/simple.html b/testing/web-platform/tests/service-workers/service-worker/resources/simple.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/simple.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/simple.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/simple.txt b/testing/web-platform/tests/service-workers/service-worker/resources/simple.txt
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/simple.txt
rename to testing/web-platform/tests/service-workers/service-worker/resources/simple.txt
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/square.png b/testing/web-platform/tests/service-workers/service-worker/resources/square.png
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/square.png
rename to testing/web-platform/tests/service-workers/service-worker/resources/square.png
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/success.py b/testing/web-platform/tests/service-workers/service-worker/resources/success.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/success.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/success.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/test-helpers.sub.js b/testing/web-platform/tests/service-workers/service-worker/resources/test-helpers.sub.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/test-helpers.sub.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/test-helpers.sub.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/testharness-helpers.js b/testing/web-platform/tests/service-workers/service-worker/resources/testharness-helpers.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/testharness-helpers.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/testharness-helpers.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/unregister-controller-page.html b/testing/web-platform/tests/service-workers/service-worker/resources/unregister-controller-page.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/unregister-controller-page.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/unregister-controller-page.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-nocookie-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/update-nocookie-worker.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-nocookie-worker.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/update-nocookie-worker.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/update-worker.py
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-worker.py
rename to testing/web-platform/tests/service-workers/service-worker/resources/update-worker.py
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update/update-after-oneday.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/update/update-after-oneday.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update/update-after-oneday.https.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/update/update-after-oneday.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/wait-forever-in-install-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/wait-forever-in-install-worker.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/wait-forever-in-install-worker.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/wait-forever-in-install-worker.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/websocket.js b/testing/web-platform/tests/service-workers/service-worker/resources/websocket.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/websocket.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/websocket.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html
rename to testing/web-platform/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-load-interceptor.js b/testing/web-platform/tests/service-workers/service-worker/resources/worker-load-interceptor.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-load-interceptor.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/worker-load-interceptor.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-testharness.js b/testing/web-platform/tests/service-workers/service-worker/resources/worker-testharness.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-testharness.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/worker-testharness.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/xhr.js b/testing/web-platform/tests/service-workers/service-worker/resources/xhr.js
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/resources/xhr.js
rename to testing/web-platform/tests/service-workers/service-worker/resources/xhr.js
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/service-worker-csp-connect.https.html b/testing/web-platform/tests/service-workers/service-worker/service-worker-csp-connect.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/service-worker-csp-connect.https.html
rename to testing/web-platform/tests/service-workers/service-worker/service-worker-csp-connect.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/service-worker-csp-default.https.html b/testing/web-platform/tests/service-workers/service-worker/service-worker-csp-default.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/service-worker-csp-default.https.html
rename to testing/web-platform/tests/service-workers/service-worker/service-worker-csp-default.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/service-worker-csp-script.https.html b/testing/web-platform/tests/service-workers/service-worker/service-worker-csp-script.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/service-worker-csp-script.https.html
rename to testing/web-platform/tests/service-workers/service-worker/service-worker-csp-script.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/serviceworkerobject-scripturl.https.html b/testing/web-platform/tests/service-workers/service-worker/serviceworkerobject-scripturl.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/serviceworkerobject-scripturl.https.html
rename to testing/web-platform/tests/service-workers/service-worker/serviceworkerobject-scripturl.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/shared-worker-controlled.https.html b/testing/web-platform/tests/service-workers/service-worker/shared-worker-controlled.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/shared-worker-controlled.https.html
rename to testing/web-platform/tests/service-workers/service-worker/shared-worker-controlled.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-installed.https.html b/testing/web-platform/tests/service-workers/service-worker/skip-waiting-installed.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-installed.https.html
rename to testing/web-platform/tests/service-workers/service-worker/skip-waiting-installed.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-using-registration.https.html b/testing/web-platform/tests/service-workers/service-worker/skip-waiting-using-registration.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-using-registration.https.html
rename to testing/web-platform/tests/service-workers/service-worker/skip-waiting-using-registration.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-without-client.https.html b/testing/web-platform/tests/service-workers/service-worker/skip-waiting-without-client.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-without-client.https.html
rename to testing/web-platform/tests/service-workers/service-worker/skip-waiting-without-client.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-without-using-registration.https.html b/testing/web-platform/tests/service-workers/service-worker/skip-waiting-without-using-registration.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-without-using-registration.https.html
rename to testing/web-platform/tests/service-workers/service-worker/skip-waiting-without-using-registration.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting.https.html b/testing/web-platform/tests/service-workers/service-worker/skip-waiting.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting.https.html
rename to testing/web-platform/tests/service-workers/service-worker/skip-waiting.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/state.https.html b/testing/web-platform/tests/service-workers/service-worker/state.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/state.https.html
rename to testing/web-platform/tests/service-workers/service-worker/state.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/synced-state.https.html b/testing/web-platform/tests/service-workers/service-worker/synced-state.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/synced-state.https.html
rename to testing/web-platform/tests/service-workers/service-worker/synced-state.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/uncontrolled-page.https.html b/testing/web-platform/tests/service-workers/service-worker/uncontrolled-page.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/uncontrolled-page.https.html
rename to testing/web-platform/tests/service-workers/service-worker/uncontrolled-page.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-controller.https.html b/testing/web-platform/tests/service-workers/service-worker/unregister-controller.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-controller.https.html
rename to testing/web-platform/tests/service-workers/service-worker/unregister-controller.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register-new-script.https.html b/testing/web-platform/tests/service-workers/service-worker/unregister-then-register-new-script.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register-new-script.https.html
rename to testing/web-platform/tests/service-workers/service-worker/unregister-then-register-new-script.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register.https.html b/testing/web-platform/tests/service-workers/service-worker/unregister-then-register.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register.https.html
rename to testing/web-platform/tests/service-workers/service-worker/unregister-then-register.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/unregister.https.html b/testing/web-platform/tests/service-workers/service-worker/unregister.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/unregister.https.html
rename to testing/web-platform/tests/service-workers/service-worker/unregister.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/update-after-navigation-fetch-event.https.html b/testing/web-platform/tests/service-workers/service-worker/update-after-navigation-fetch-event.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/update-after-navigation-fetch-event.https.html
rename to testing/web-platform/tests/service-workers/service-worker/update-after-navigation-fetch-event.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/update-after-oneday.https.html b/testing/web-platform/tests/service-workers/service-worker/update-after-oneday.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/update-after-oneday.https.html
rename to testing/web-platform/tests/service-workers/service-worker/update-after-oneday.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/update.https.html b/testing/web-platform/tests/service-workers/service-worker/update.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/update.https.html
rename to testing/web-platform/tests/service-workers/service-worker/update.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/waiting.https.html b/testing/web-platform/tests/service-workers/service-worker/waiting.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/waiting.https.html
rename to testing/web-platform/tests/service-workers/service-worker/waiting.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/websocket.https.html b/testing/web-platform/tests/service-workers/service-worker/websocket.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/websocket.https.html
rename to testing/web-platform/tests/service-workers/service-worker/websocket.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/worker-interception.https.html b/testing/web-platform/tests/service-workers/service-worker/worker-interception.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/worker-interception.https.html
rename to testing/web-platform/tests/service-workers/service-worker/worker-interception.https.html
diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/xhr.https.html b/testing/web-platform/tests/service-workers/service-worker/xhr.https.html
similarity index 100%
rename from testing/web-platform/mozilla/tests/service-workers/service-worker/xhr.https.html
rename to testing/web-platform/tests/service-workers/service-worker/xhr.https.html
diff --git a/toolkit/components/commandlines/test/unit_unix/xpcshell.ini b/toolkit/components/commandlines/test/unit_unix/xpcshell.ini
index fba15bb079..17d931bd48 100644
--- a/toolkit/components/commandlines/test/unit_unix/xpcshell.ini
+++ b/toolkit/components/commandlines/test/unit_unix/xpcshell.ini
@@ -2,5 +2,8 @@
 head = 
 tail = 
 skip-if = toolkit == 'android' || toolkit == 'gonk'
+support-files =
+  !/toolkit/components/commandlines/test/unit/data/test_bug410156.desktop
+  !/toolkit/components/commandlines/test/unit/data/test_bug410156.url
 
 [test_bug410156.js]
diff --git a/toolkit/components/commandlines/test/unit_win/xpcshell.ini b/toolkit/components/commandlines/test/unit_win/xpcshell.ini
index 9044d4fea5..efc2cfccf6 100644
--- a/toolkit/components/commandlines/test/unit_win/xpcshell.ini
+++ b/toolkit/components/commandlines/test/unit_win/xpcshell.ini
@@ -1,5 +1,8 @@
 [DEFAULT]
 head = 
-tail = 
+tail =
+support-files =
+  !/toolkit/components/commandlines/test/unit/data/test_bug410156.desktop
+  !/toolkit/components/commandlines/test/unit/data/test_bug410156.url
 
 [test_bug410156.js]
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_remove_non_watched.js b/toolkit/components/filewatcher/tests/xpcshell/test_remove_non_watched.js
index 247707f52e..1375584a38 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_remove_non_watched.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_remove_non_watched.js
@@ -1,39 +1,39 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files.
-  do_get_profile();
-
-  // Start executing the tests.
-  run_next_test();
-}
-
-/**
- * Test removing non watched path
- */
-add_task(function* test_remove_not_watched() {
-  let nonExistingDir =
-    OS.Path.join(OS.Constants.Path.profileDir, "absolutelyNotExisting");
-
-  // Instantiate the native watcher.
-  let watcher = makeWatcher();
-
-  // Try to un-watch a path which wasn't being watched.
-  watcher.removePath(
-    nonExistingDir,
-    function(changed) {
-      do_throw("No change is expected in this test.");
-    },
-    function(xpcomError, osError) {
-      // When removing a resource which wasn't being watched, it should silently
-      // ignore the request.
-      do_throw("Unexpected exception: "
-               + xpcomError + " (XPCOM) "
-               + osError + " (OS Error)");
-    }
-  );
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files.
+  do_get_profile();
+
+  // Start executing the tests.
+  run_next_test();
+}
+
+/**
+ * Test removing non watched path
+ */
+add_task(function* test_remove_not_watched() {
+  let nonExistingDir =
+    OS.Path.join(OS.Constants.Path.profileDir, "absolutelyNotExisting");
+
+  // Instantiate the native watcher.
+  let watcher = makeWatcher();
+
+  // Try to un-watch a path which wasn't being watched.
+  watcher.removePath(
+    nonExistingDir,
+    function(changed) {
+      do_throw("No change is expected in this test.");
+    },
+    function(xpcomError, osError) {
+      // When removing a resource which wasn't being watched, it should silently
+      // ignore the request.
+      do_throw("Unexpected exception: "
+               + xpcomError + " (XPCOM) "
+               + osError + " (OS Error)");
+    }
+  );
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_shared_callback.js b/toolkit/components/filewatcher/tests/xpcshell/test_shared_callback.js
index e5d8222f7d..482ba6b8b1 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_shared_callback.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_shared_callback.js
@@ -1,62 +1,62 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files.
-  do_get_profile();
-
-  // Start executing the tests.
-  run_next_test();
-}
-
-/**
- * Test the watcher correctly handles two watches sharing the same
- * change callback.
- */
-add_task(function* test_watch_with_shared_callback() {
-
-  // Create and watch two sub-directories of the profile directory so we don't
-  // catch notifications we're not interested in (i.e. "startupCache").
-  let watchedDirs =
-    [
-      OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground"),
-      OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground2")
-    ];
-
-  yield OS.File.makeDir(watchedDirs[0]);
-  yield OS.File.makeDir(watchedDirs[1]);
-
-  let tempFileName = "test_filecreation.tmp";
-
-  // Instantiate and initialize the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  // Watch both directories using the same callbacks.
-  yield promiseAddPath(watcher, watchedDirs[0], deferred.resolve, deferred.reject);
-  yield promiseAddPath(watcher, watchedDirs[1], deferred.resolve, deferred.reject);
-
-  // Remove the watch for the first directory, but keep watching
-  // for changes in the second: we need to make sure the callback
-  // survives the removal of the first watch.
-  watcher.removePath(watchedDirs[0], deferred.resolve, deferred.reject);
-
-  // Create a file within the watched directory.
-  let tmpFilePath = OS.Path.join(watchedDirs[1], tempFileName);
-  yield OS.File.writeAtomic(tmpFilePath, "some data");
-
-  // Wait until the watcher informs us that the file was created.
-  let changed = yield deferred.promise;
-  do_check_eq(changed, tmpFilePath);
-
-  // Remove the watch and free the associated memory (we need to
-  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
-  watcher.removePath(watchedDirs[1], deferred.resolve, deferred.reject);
-
-  // Remove the test directories and all of their content.
-  yield OS.File.removeDir(watchedDirs[0]);
-  yield OS.File.removeDir(watchedDirs[1]);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files.
+  do_get_profile();
+
+  // Start executing the tests.
+  run_next_test();
+}
+
+/**
+ * Test the watcher correctly handles two watches sharing the same
+ * change callback.
+ */
+add_task(function* test_watch_with_shared_callback() {
+
+  // Create and watch two sub-directories of the profile directory so we don't
+  // catch notifications we're not interested in (i.e. "startupCache").
+  let watchedDirs =
+    [
+      OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground"),
+      OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground2")
+    ];
+
+  yield OS.File.makeDir(watchedDirs[0]);
+  yield OS.File.makeDir(watchedDirs[1]);
+
+  let tempFileName = "test_filecreation.tmp";
+
+  // Instantiate and initialize the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  // Watch both directories using the same callbacks.
+  yield promiseAddPath(watcher, watchedDirs[0], deferred.resolve, deferred.reject);
+  yield promiseAddPath(watcher, watchedDirs[1], deferred.resolve, deferred.reject);
+
+  // Remove the watch for the first directory, but keep watching
+  // for changes in the second: we need to make sure the callback
+  // survives the removal of the first watch.
+  watcher.removePath(watchedDirs[0], deferred.resolve, deferred.reject);
+
+  // Create a file within the watched directory.
+  let tmpFilePath = OS.Path.join(watchedDirs[1], tempFileName);
+  yield OS.File.writeAtomic(tmpFilePath, "some data");
+
+  // Wait until the watcher informs us that the file was created.
+  let changed = yield deferred.promise;
+  do_check_eq(changed, tmpFilePath);
+
+  // Remove the watch and free the associated memory (we need to
+  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
+  watcher.removePath(watchedDirs[1], deferred.resolve, deferred.reject);
+
+  // Remove the test directories and all of their content.
+  yield OS.File.removeDir(watchedDirs[0]);
+  yield OS.File.removeDir(watchedDirs[1]);
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_directory_creation_single.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_directory_creation_single.js
index ae26e473b0..a434ec751a 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_directory_creation_single.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_directory_creation_single.js
@@ -1,49 +1,49 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files
-  do_get_profile();
-
-  // Start executing the tests
-  run_next_test();
-}
-
-/**
- * Tests that the watcher correctly notifies of a directory creation when watching
- * a single path.
- */
-add_task(function* test_watch_single_path_directory_creation() {
-
-  // Create and watch a sub-directory of the profile directory so we don't
-  // catch notifications we're not interested in (i.e. "startupCache").
-  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
-  yield OS.File.makeDir(watchedDir);
-
-  let tmpDirPath = OS.Path.join(watchedDir, "testdir");
-
-  // Instantiate and initialize the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  // Add the profile directory to the watch list and wait for the file watcher
-  // to start watching.
-  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Once ready, create a directory within the watched directory.
-  yield OS.File.makeDir(tmpDirPath);
-
-  // Wait until the watcher informs us that the file has changed.
-  let changed = yield deferred.promise;
-  do_check_eq(changed, tmpDirPath);
-
-  // Remove the watch and free the associated memory (we need to
-  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
-  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Clean up the test directory.
-  yield OS.File.removeDir(watchedDir);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files
+  do_get_profile();
+
+  // Start executing the tests
+  run_next_test();
+}
+
+/**
+ * Tests that the watcher correctly notifies of a directory creation when watching
+ * a single path.
+ */
+add_task(function* test_watch_single_path_directory_creation() {
+
+  // Create and watch a sub-directory of the profile directory so we don't
+  // catch notifications we're not interested in (i.e. "startupCache").
+  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
+  yield OS.File.makeDir(watchedDir);
+
+  let tmpDirPath = OS.Path.join(watchedDir, "testdir");
+
+  // Instantiate and initialize the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  // Add the profile directory to the watch list and wait for the file watcher
+  // to start watching.
+  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Once ready, create a directory within the watched directory.
+  yield OS.File.makeDir(tmpDirPath);
+
+  // Wait until the watcher informs us that the file has changed.
+  let changed = yield deferred.promise;
+  do_check_eq(changed, tmpDirPath);
+
+  // Remove the watch and free the associated memory (we need to
+  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
+  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Clean up the test directory.
+  yield OS.File.removeDir(watchedDir);
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_directory_deletion_single.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_directory_deletion_single.js
index 2b0b805ef6..2c74a93616 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_directory_deletion_single.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_directory_deletion_single.js
@@ -1,46 +1,46 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files.
-  do_get_profile();
-
-  // Start executing the tests.
-  run_next_test();
-}
-
-/**
- * Tests that the watcher correctly notifies of a directory deletion when watching
- * a single path.
- */
-add_task(function* test_watch_single_path_directory_deletion() {
-
-  let watchedDir = OS.Constants.Path.profileDir;
-  let tempDirName = "test";
-  let tmpDirPath = OS.Path.join(watchedDir, tempDirName);
-
-  // Instantiate and initialize the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  // Create a directory within the watched directory.
-  yield OS.File.makeDir(tmpDirPath);
-
-  // Add the profile directory to the watch list and wait for the file watcher
-  // to start watching it.
-  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Remove the directory.
-  OS.File.removeDir(tmpDirPath);
-
-  // Wait until the watcher informs us that the file has changed.
-  let changed = yield deferred.promise;
-  do_check_eq(changed, tmpDirPath);
-
-  // Remove the watch and free the associated memory (we need to
-  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
-  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files.
+  do_get_profile();
+
+  // Start executing the tests.
+  run_next_test();
+}
+
+/**
+ * Tests that the watcher correctly notifies of a directory deletion when watching
+ * a single path.
+ */
+add_task(function* test_watch_single_path_directory_deletion() {
+
+  let watchedDir = OS.Constants.Path.profileDir;
+  let tempDirName = "test";
+  let tmpDirPath = OS.Path.join(watchedDir, tempDirName);
+
+  // Instantiate and initialize the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  // Create a directory within the watched directory.
+  yield OS.File.makeDir(tmpDirPath);
+
+  // Add the profile directory to the watch list and wait for the file watcher
+  // to start watching it.
+  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Remove the directory.
+  OS.File.removeDir(tmpDirPath);
+
+  // Wait until the watcher informs us that the file has changed.
+  let changed = yield deferred.promise;
+  do_check_eq(changed, tmpDirPath);
+
+  // Remove the watch and free the associated memory (we need to
+  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
+  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_creation_single.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_creation_single.js
index 26345964f0..9f87793f44 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_creation_single.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_creation_single.js
@@ -1,51 +1,51 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files.
-  do_get_profile();
-
-  // Start executing the tests.
-  run_next_test();
-}
-
-/**
- * Test the watcher correctly notifies of a file creation when watching
- * a single path.
- */
-add_task(function* test_watch_single_path_file_creation() {
-
-  // Create and watch a sub-directory of the profile directory so we don't
-  // catch notifications we're not interested in (i.e. "startupCache").
-  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
-  yield OS.File.makeDir(watchedDir);
-
-  let tempFileName = "test_filecreation.tmp";
-
-  // Instantiate and initialize the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  let tmpFilePath = OS.Path.join(watchedDir, tempFileName);
-
-  // Add the profile directory to the watch list and wait for the file watcher
-  // to start watching.
-  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // create the file within the watched directory.
-  yield OS.File.writeAtomic(tmpFilePath, "some data");
-
-  // Wait until the watcher informs us that the file was created.
-  let changed = yield deferred.promise;
-  do_check_eq(changed, tmpFilePath);
-
-  // Remove the watch and free the associated memory (we need to
-  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
-  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Remove the test directory and all of its content.
-  yield OS.File.removeDir(watchedDir);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files.
+  do_get_profile();
+
+  // Start executing the tests.
+  run_next_test();
+}
+
+/**
+ * Test the watcher correctly notifies of a file creation when watching
+ * a single path.
+ */
+add_task(function* test_watch_single_path_file_creation() {
+
+  // Create and watch a sub-directory of the profile directory so we don't
+  // catch notifications we're not interested in (i.e. "startupCache").
+  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
+  yield OS.File.makeDir(watchedDir);
+
+  let tempFileName = "test_filecreation.tmp";
+
+  // Instantiate and initialize the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  let tmpFilePath = OS.Path.join(watchedDir, tempFileName);
+
+  // Add the profile directory to the watch list and wait for the file watcher
+  // to start watching.
+  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // create the file within the watched directory.
+  yield OS.File.writeAtomic(tmpFilePath, "some data");
+
+  // Wait until the watcher informs us that the file was created.
+  let changed = yield deferred.promise;
+  do_check_eq(changed, tmpFilePath);
+
+  // Remove the watch and free the associated memory (we need to
+  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
+  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Remove the test directory and all of its content.
+  yield OS.File.removeDir(watchedDir);
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_deletion_single.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_deletion_single.js
index ec97540742..97d2d61bc2 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_deletion_single.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_deletion_single.js
@@ -1,54 +1,54 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files.
-  do_get_profile();
-
-  // Start executing the tests.
-  run_next_test();
-}
-/**
- * Test the watcher correctly notifies of a file deletion when watching
- * a single path.
- */
-add_task(function* test_watch_single_path_file_deletion() {
-
-  // Create and watch a sub-directory of the profile directory so we don't
-  // catch notifications we're not interested in (i.e. "startupCache").
-  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
-  yield OS.File.makeDir(watchedDir);
-
-  let tempFileName = "test_filedeletion.tmp";
-
-  // Instantiate and initialize the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  // Create a file within the directory to be watched. We do this
-  // before watching the directory so we do not get the creation notification.
-  let tmpFilePath = OS.Path.join(watchedDir, tempFileName);
-  yield OS.File.writeAtomic(tmpFilePath, "some data");
-
-  // Add the profile directory to the watch list and wait for the file watcher
-  // to start watching it.
-  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Remove the file we created (should trigger a notification).
-  do_print('Removing ' + tmpFilePath);
-  yield OS.File.remove(tmpFilePath);
-
-  // Wait until the watcher informs us that the file was deleted.
-  let changed = yield deferred.promise;
-  do_check_eq(changed, tmpFilePath);
-
-  // Remove the watch and free the associated memory (we need to
-  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
-  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Remove the test directory and all of its content.
-  yield OS.File.removeDir(watchedDir);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files.
+  do_get_profile();
+
+  // Start executing the tests.
+  run_next_test();
+}
+/**
+ * Test the watcher correctly notifies of a file deletion when watching
+ * a single path.
+ */
+add_task(function* test_watch_single_path_file_deletion() {
+
+  // Create and watch a sub-directory of the profile directory so we don't
+  // catch notifications we're not interested in (i.e. "startupCache").
+  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
+  yield OS.File.makeDir(watchedDir);
+
+  let tempFileName = "test_filedeletion.tmp";
+
+  // Instantiate and initialize the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  // Create a file within the directory to be watched. We do this
+  // before watching the directory so we do not get the creation notification.
+  let tmpFilePath = OS.Path.join(watchedDir, tempFileName);
+  yield OS.File.writeAtomic(tmpFilePath, "some data");
+
+  // Add the profile directory to the watch list and wait for the file watcher
+  // to start watching it.
+  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Remove the file we created (should trigger a notification).
+  do_print('Removing ' + tmpFilePath);
+  yield OS.File.remove(tmpFilePath);
+
+  // Wait until the watcher informs us that the file was deleted.
+  let changed = yield deferred.promise;
+  do_check_eq(changed, tmpFilePath);
+
+  // Remove the watch and free the associated memory (we need to
+  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
+  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Remove the test directory and all of its content.
+  yield OS.File.removeDir(watchedDir);
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_modification_single.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_modification_single.js
index cb690517bc..ba25fdff63 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_modification_single.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_file_modification_single.js
@@ -1,54 +1,54 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files
-  do_get_profile();
-
-  // Start executing the tests
-  run_next_test();
-}
-
-/**
- * Tests that the watcher correctly notifies of a file modification when watching
- * a single path.
- */
-add_task(function* test_watch_single_path_file_modification() {
-
-  // Create and watch a sub-directory of the profile directory so we don't
-  // catch notifications we're not interested in (i.e. "startupCache").
-  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
-  yield OS.File.makeDir(watchedDir);
-
-  let tempFileName = "test_filemodification.tmp";
-
-  // Instantiate and initialize the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  // Create a file within the directory to be watched. We do this
-  // before watching the directory so we do not get the creation notification.
-  let tmpFilePath = OS.Path.join(watchedDir, tempFileName);
-  yield OS.File.writeAtomic(tmpFilePath, "some data");
-
-  // Add the profile directory to the watch list and wait for the file watcher
-  // to start watching it.
-  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Once ready, modify the file to trigger the notification.
-  yield OS.File.writeAtomic(tmpFilePath, "some new data");
-
-  // Wait until the watcher informs us that the file has changed.
-  let changed = yield deferred.promise;
-  do_check_eq(changed, tmpFilePath);
-
-  // Remove the watch and free the associated memory (we need to
-  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
-  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Remove the test directory and all of its content.
-  yield OS.File.removeDir(watchedDir);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files
+  do_get_profile();
+
+  // Start executing the tests
+  run_next_test();
+}
+
+/**
+ * Tests that the watcher correctly notifies of a file modification when watching
+ * a single path.
+ */
+add_task(function* test_watch_single_path_file_modification() {
+
+  // Create and watch a sub-directory of the profile directory so we don't
+  // catch notifications we're not interested in (i.e. "startupCache").
+  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
+  yield OS.File.makeDir(watchedDir);
+
+  let tempFileName = "test_filemodification.tmp";
+
+  // Instantiate and initialize the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  // Create a file within the directory to be watched. We do this
+  // before watching the directory so we do not get the creation notification.
+  let tmpFilePath = OS.Path.join(watchedDir, tempFileName);
+  yield OS.File.writeAtomic(tmpFilePath, "some data");
+
+  // Add the profile directory to the watch list and wait for the file watcher
+  // to start watching it.
+  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Once ready, modify the file to trigger the notification.
+  yield OS.File.writeAtomic(tmpFilePath, "some new data");
+
+  // Wait until the watcher informs us that the file has changed.
+  let changed = yield deferred.promise;
+  do_check_eq(changed, tmpFilePath);
+
+  // Remove the watch and free the associated memory (we need to
+  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
+  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Remove the test directory and all of its content.
+  yield OS.File.removeDir(watchedDir);
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_many_changes.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_many_changes.js
index d63be5c0e9..c236c6e1de 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_many_changes.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_many_changes.js
@@ -1,73 +1,73 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files
-  do_get_profile();
-
-  // Start executing the tests
-  run_next_test();
-}
-
-/**
- * Test that we correctly handle watching directories when hundreds of files
- * change simultaneously.
- */
-add_task(function* test_fill_notification_buffer() {
-
-  // Create and watch a sub-directory of the profile directory so we don't
-  // catch notifications we're not interested in (i.e. "startupCache").
-  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
-  yield OS.File.makeDir(watchedDir);
-
-  // The number of files to create.
-  let numberOfFiles = 100;
-  let fileNameBase = "testFile";
-
-  // This will be used to keep track of the number of changes within the watched
-  // directory.
-  let detectedChanges = 0;
-
-  // We expect at least the following notifications for each file:
-  //  - File creation
-  //  - File deletion
-  let expectedChanges = numberOfFiles * 2;
-
-  // Instantiate the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  // Initialise the change callback.
-  let changeCallback = function(changed) {
-      do_print(changed + " has changed.");
-
-      detectedChanges += 1;
-
-      // Resolve the promise if we get all the expected changes.
-      if (detectedChanges >= expectedChanges) {
-        deferred.resolve();
-      }
-    };
-
-  // Add the profile directory to the watch list and wait for the file watcher
-  // to start watching it.
-  yield promiseAddPath(watcher, watchedDir, changeCallback, deferred.reject);
-
-  // Create and then remove the files within the watched directory.
-  for (let i = 0; i < numberOfFiles; i++) {
-    let tmpFilePath = OS.Path.join(watchedDir, fileNameBase + i);
-    yield OS.File.writeAtomic(tmpFilePath, "test content");
-    yield OS.File.remove(tmpFilePath);
-  }
-
-  // Wait until the watcher informs us that all the files were
-  // created, modified and removed.
-  yield deferred.promise;
-
-  // Remove the watch and free the associated memory (we need to
-  // reuse 'changeCallback' and 'errorCallback' to unregister).
-  yield promiseRemovePath(watcher, watchedDir, changeCallback, deferred.reject);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files
+  do_get_profile();
+
+  // Start executing the tests
+  run_next_test();
+}
+
+/**
+ * Test that we correctly handle watching directories when hundreds of files
+ * change simultaneously.
+ */
+add_task(function* test_fill_notification_buffer() {
+
+  // Create and watch a sub-directory of the profile directory so we don't
+  // catch notifications we're not interested in (i.e. "startupCache").
+  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
+  yield OS.File.makeDir(watchedDir);
+
+  // The number of files to create.
+  let numberOfFiles = 100;
+  let fileNameBase = "testFile";
+
+  // This will be used to keep track of the number of changes within the watched
+  // directory.
+  let detectedChanges = 0;
+
+  // We expect at least the following notifications for each file:
+  //  - File creation
+  //  - File deletion
+  let expectedChanges = numberOfFiles * 2;
+
+  // Instantiate the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  // Initialise the change callback.
+  let changeCallback = function(changed) {
+      do_print(changed + " has changed.");
+
+      detectedChanges += 1;
+
+      // Resolve the promise if we get all the expected changes.
+      if (detectedChanges >= expectedChanges) {
+        deferred.resolve();
+      }
+    };
+
+  // Add the profile directory to the watch list and wait for the file watcher
+  // to start watching it.
+  yield promiseAddPath(watcher, watchedDir, changeCallback, deferred.reject);
+
+  // Create and then remove the files within the watched directory.
+  for (let i = 0; i < numberOfFiles; i++) {
+    let tmpFilePath = OS.Path.join(watchedDir, fileNameBase + i);
+    yield OS.File.writeAtomic(tmpFilePath, "test content");
+    yield OS.File.remove(tmpFilePath);
+  }
+
+  // Wait until the watcher informs us that all the files were
+  // created, modified and removed.
+  yield deferred.promise;
+
+  // Remove the watch and free the associated memory (we need to
+  // reuse 'changeCallback' and 'errorCallback' to unregister).
+  yield promiseRemovePath(watcher, watchedDir, changeCallback, deferred.reject);
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_multi_paths.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_multi_paths.js
index 5467e10ba9..c55b262f19 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_multi_paths.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_multi_paths.js
@@ -1,114 +1,114 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files
-  do_get_profile();
-
-  // Start executing the tests
-  run_next_test();
-}
-
-/**
- * Tests the watcher by watching several resources.
- * This test creates the specified number of directory inside the profile
- * directory, adds each one of them to the watched list the creates
- * a file in them in order to trigger the notification.
- * The test keeps track of the number of times the changes callback is
- * called in order to verify the success of the test.
- */
-add_task(function* test_watch_multi_paths() {
-
-  // The number of resources to watch. We expect changes for
-  // creating a file within each directory.
-  let resourcesToWatch = 5;
-  let watchedDir = OS.Constants.Path.profileDir;
-
-  // The directories to be watched will be created with.
-  let tempDirNameBase = "FileWatcher_Test_";
-  let tempFileName = "test.tmp";
-
-  // Instantiate the native watcher.
-  let watcher = makeWatcher();
-
-  // This will be used to keep track of the number of changes within the watched
-  // resources.
-  let detectedChanges = 0;
-  let watchedResources = 0;
-  let unwatchedResources = 0;
-
-  let deferredChanges = Promise.defer();
-  let deferredSuccesses = Promise.defer();
-  let deferredShutdown = Promise.defer();
-
-  // Define the change callback function.
-  let changeCallback = function(changed) {
-      do_print(changed + " has changed.");
-
-      detectedChanges += 1;
-
-      // Resolve the promise if we get all the expected changes.
-      if (detectedChanges === resourcesToWatch) {
-        deferredChanges.resolve();
-      }
-    };
-
-  // Define the watch success callback function.
-  let watchSuccessCallback = function(resourcePath) {
-      do_print(resourcePath + " is being watched.");
-
-      watchedResources += 1;
-
-      // Resolve the promise when all the resources are being
-      // watched.
-      if (watchedResources === resourcesToWatch) {
-        deferredSuccesses.resolve();
-      }
-    };
-
-  // Define the watch success callback function.
-  let unwatchSuccessCallback = function(resourcePath) {
-      do_print(resourcePath + " is being un-watched.");
-
-      unwatchedResources += 1;
-
-      // Resolve the promise when all the resources are being
-      // watched.
-      if (unwatchedResources === resourcesToWatch) {
-        deferredShutdown.resolve();
-      }
-    };
-
-  // Create the directories and add them to the watched resources list.
-  for (let i = 0; i < resourcesToWatch; i++) {
-    let tmpSubDirPath = OS.Path.join(watchedDir, tempDirNameBase + i);
-    do_print("Creating the " + tmpSubDirPath + " directory.");
-    yield OS.File.makeDir(tmpSubDirPath);
-    watcher.addPath(tmpSubDirPath, changeCallback, deferredChanges.reject, watchSuccessCallback);
-  }
-
-  // Wait until the watcher informs us that all the desired resources
-  // are being watched.
-  yield deferredSuccesses.promise;
-
-  // Create a file within each watched directory.
-  for (let i = 0; i < resourcesToWatch; i++) {
-    let tmpFilePath = OS.Path.join(watchedDir, tempDirNameBase + i, tempFileName);
-    yield OS.File.writeAtomic(tmpFilePath, "test content");
-  }
-
-  // Wait until the watcher informs us that all the files were created.
-  yield deferredChanges.promise;
-
-  // Remove the directories we have created.
-  for (let i = 0; i < resourcesToWatch; i++) {
-    let tmpSubDirPath = OS.Path.join(watchedDir, tempDirNameBase + i);
-    watcher.removePath(tmpSubDirPath, changeCallback, deferredChanges.reject, unwatchSuccessCallback);
-  }
-
-  // Wait until the watcher un-watches the resources.
-  yield deferredShutdown.promise;
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files
+  do_get_profile();
+
+  // Start executing the tests
+  run_next_test();
+}
+
+/**
+ * Tests the watcher by watching several resources.
+ * This test creates the specified number of directory inside the profile
+ * directory, adds each one of them to the watched list the creates
+ * a file in them in order to trigger the notification.
+ * The test keeps track of the number of times the changes callback is
+ * called in order to verify the success of the test.
+ */
+add_task(function* test_watch_multi_paths() {
+
+  // The number of resources to watch. We expect changes for
+  // creating a file within each directory.
+  let resourcesToWatch = 5;
+  let watchedDir = OS.Constants.Path.profileDir;
+
+  // The directories to be watched will be created with.
+  let tempDirNameBase = "FileWatcher_Test_";
+  let tempFileName = "test.tmp";
+
+  // Instantiate the native watcher.
+  let watcher = makeWatcher();
+
+  // This will be used to keep track of the number of changes within the watched
+  // resources.
+  let detectedChanges = 0;
+  let watchedResources = 0;
+  let unwatchedResources = 0;
+
+  let deferredChanges = Promise.defer();
+  let deferredSuccesses = Promise.defer();
+  let deferredShutdown = Promise.defer();
+
+  // Define the change callback function.
+  let changeCallback = function(changed) {
+      do_print(changed + " has changed.");
+
+      detectedChanges += 1;
+
+      // Resolve the promise if we get all the expected changes.
+      if (detectedChanges === resourcesToWatch) {
+        deferredChanges.resolve();
+      }
+    };
+
+  // Define the watch success callback function.
+  let watchSuccessCallback = function(resourcePath) {
+      do_print(resourcePath + " is being watched.");
+
+      watchedResources += 1;
+
+      // Resolve the promise when all the resources are being
+      // watched.
+      if (watchedResources === resourcesToWatch) {
+        deferredSuccesses.resolve();
+      }
+    };
+
+  // Define the watch success callback function.
+  let unwatchSuccessCallback = function(resourcePath) {
+      do_print(resourcePath + " is being un-watched.");
+
+      unwatchedResources += 1;
+
+      // Resolve the promise when all the resources are being
+      // watched.
+      if (unwatchedResources === resourcesToWatch) {
+        deferredShutdown.resolve();
+      }
+    };
+
+  // Create the directories and add them to the watched resources list.
+  for (let i = 0; i < resourcesToWatch; i++) {
+    let tmpSubDirPath = OS.Path.join(watchedDir, tempDirNameBase + i);
+    do_print("Creating the " + tmpSubDirPath + " directory.");
+    yield OS.File.makeDir(tmpSubDirPath);
+    watcher.addPath(tmpSubDirPath, changeCallback, deferredChanges.reject, watchSuccessCallback);
+  }
+
+  // Wait until the watcher informs us that all the desired resources
+  // are being watched.
+  yield deferredSuccesses.promise;
+
+  // Create a file within each watched directory.
+  for (let i = 0; i < resourcesToWatch; i++) {
+    let tmpFilePath = OS.Path.join(watchedDir, tempDirNameBase + i, tempFileName);
+    yield OS.File.writeAtomic(tmpFilePath, "test content");
+  }
+
+  // Wait until the watcher informs us that all the files were created.
+  yield deferredChanges.promise;
+
+  // Remove the directories we have created.
+  for (let i = 0; i < resourcesToWatch; i++) {
+    let tmpSubDirPath = OS.Path.join(watchedDir, tempDirNameBase + i);
+    watcher.removePath(tmpSubDirPath, changeCallback, deferredChanges.reject, unwatchSuccessCallback);
+  }
+
+  // Wait until the watcher un-watches the resources.
+  yield deferredShutdown.promise;
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_recursively.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_recursively.js
index 6775e37d30..13a3de8d36 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_recursively.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_recursively.js
@@ -1,55 +1,55 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files.
-  do_get_profile();
-
-  // Start executing the tests.
-  run_next_test();
-}
-
-/**
- * Test the watcher correctly notifies of a file creation in a subdirectory
- * of the watched sub-directory (recursion).
- */
-add_task(function* test_watch_recursively() {
-
-  // Create and watch a sub-directory of the profile directory so we don't
-  // catch notifications we're not interested in (i.e. "startupCache").
-  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
-  yield OS.File.makeDir(watchedDir);
-
-  // We need at least 2 levels of directories to test recursion.
-  let subdirectory = OS.Path.join(watchedDir, "level1");
-  yield OS.File.makeDir(subdirectory);
-
-  let tempFileName = "test_filecreation.tmp";
-
-  // Instantiate and initialize the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  let tmpFilePath = OS.Path.join(subdirectory, tempFileName);
-
-  // Add the profile directory to the watch list and wait for the file watcher
-  // to start watching it.
-  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Create a file within the subdirectory of the watched directory.
-  yield OS.File.writeAtomic(tmpFilePath, "some data");
-
-  // Wait until the watcher informs us that the file was created.
-  let changed = yield deferred.promise;
-  do_check_eq(changed, tmpFilePath);
-
-  // Remove the watch and free the associated memory (we need to
-  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
-  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
-
-  // Remove the test directory and all of its content.
-  yield OS.File.removeDir(watchedDir);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files.
+  do_get_profile();
+
+  // Start executing the tests.
+  run_next_test();
+}
+
+/**
+ * Test the watcher correctly notifies of a file creation in a subdirectory
+ * of the watched sub-directory (recursion).
+ */
+add_task(function* test_watch_recursively() {
+
+  // Create and watch a sub-directory of the profile directory so we don't
+  // catch notifications we're not interested in (i.e. "startupCache").
+  let watchedDir = OS.Path.join(OS.Constants.Path.profileDir, "filewatcher_playground");
+  yield OS.File.makeDir(watchedDir);
+
+  // We need at least 2 levels of directories to test recursion.
+  let subdirectory = OS.Path.join(watchedDir, "level1");
+  yield OS.File.makeDir(subdirectory);
+
+  let tempFileName = "test_filecreation.tmp";
+
+  // Instantiate and initialize the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  let tmpFilePath = OS.Path.join(subdirectory, tempFileName);
+
+  // Add the profile directory to the watch list and wait for the file watcher
+  // to start watching it.
+  yield promiseAddPath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Create a file within the subdirectory of the watched directory.
+  yield OS.File.writeAtomic(tmpFilePath, "some data");
+
+  // Wait until the watcher informs us that the file was created.
+  let changed = yield deferred.promise;
+  do_check_eq(changed, tmpFilePath);
+
+  // Remove the watch and free the associated memory (we need to
+  // reuse 'deferred.resolve' and 'deferred.reject' to unregister).
+  yield promiseRemovePath(watcher, watchedDir, deferred.resolve, deferred.reject);
+
+  // Remove the test directory and all of its content.
+  yield OS.File.removeDir(watchedDir);
+});
diff --git a/toolkit/components/filewatcher/tests/xpcshell/test_watch_resource.js b/toolkit/components/filewatcher/tests/xpcshell/test_watch_resource.js
index ffbcf8dfed..fffdff24b7 100644
--- a/toolkit/components/filewatcher/tests/xpcshell/test_watch_resource.js
+++ b/toolkit/components/filewatcher/tests/xpcshell/test_watch_resource.js
@@ -1,32 +1,32 @@
-/* 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/. */
-
-"use strict";
-
-function run_test() {
-  // Set up profile. We will use profile path create some test files
-  do_get_profile();
-
-  // Start executing the tests
-  run_next_test();
-}
-
-/**
- * Test watching non-existing path
- */
-add_task(function* test_watching_non_existing() {
-  let notExistingDir =
-    OS.Path.join(OS.Constants.Path.profileDir, "absolutelyNotExisting");
-
-  // Instantiate the native watcher.
-  let watcher = makeWatcher();
-  let deferred = Promise.defer();
-
-  // Try watch a path which doesn't exist.
-  watcher.addPath(notExistingDir, deferred.reject, deferred.resolve);
-
-  // Wait until the watcher informs us that there was an error.
-  let error = yield deferred.promise;
-  do_check_eq(error, Components.results.NS_ERROR_FILE_NOT_FOUND);
-});
+/* 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/. */
+
+"use strict";
+
+function run_test() {
+  // Set up profile. We will use profile path create some test files
+  do_get_profile();
+
+  // Start executing the tests
+  run_next_test();
+}
+
+/**
+ * Test watching non-existing path
+ */
+add_task(function* test_watching_non_existing() {
+  let notExistingDir =
+    OS.Path.join(OS.Constants.Path.profileDir, "absolutelyNotExisting");
+
+  // Instantiate the native watcher.
+  let watcher = makeWatcher();
+  let deferred = Promise.defer();
+
+  // Try watch a path which doesn't exist.
+  watcher.addPath(notExistingDir, deferred.reject, deferred.resolve);
+
+  // Wait until the watcher informs us that there was an error.
+  let error = yield deferred.promise;
+  do_check_eq(error, Components.results.NS_ERROR_FILE_NOT_FOUND);
+});
diff --git a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
index febe5f6d28..1cee9dd3c8 100644
--- a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
+++ b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
@@ -116,9 +116,7 @@ void Finalize(JSFreeOp *fop, JSObject *objSelf)
   // during shutdown. In that case, there is not much we can do.
 }
 
-static const JSClass sWitnessClass = {
-  "FinalizationWitness",
-  JSCLASS_HAS_RESERVED_SLOTS(WITNESS_INSTANCES_SLOTS),
+static const JSClassOps sWitnessClassOps = {
   nullptr /* addProperty */,
   nullptr /* delProperty */,
   nullptr /* getProperty */,
@@ -129,6 +127,12 @@ static const JSClass sWitnessClass = {
   Finalize /* finalize */
 };
 
+static const JSClass sWitnessClass = {
+  "FinalizationWitness",
+  JSCLASS_HAS_RESERVED_SLOTS(WITNESS_INSTANCES_SLOTS),
+  &sWitnessClassOps
+};
+
 bool IsWitness(JS::Handle v)
 {
   return v.isObject() && JS_GetClass(&v.toObject()) == &sWitnessClass;
diff --git a/toolkit/components/microformats/microformat-shiv.js b/toolkit/components/microformats/microformat-shiv.js
index 95a388fed6..06e22e2f81 100644
--- a/toolkit/components/microformats/microformat-shiv.js
+++ b/toolkit/components/microformats/microformat-shiv.js
@@ -22,311 +22,311 @@ var Microformats; // jshint ignore:line
     var modules = {};
 
 
-	modules.version = '1.3.3';
-	modules.livingStandard = '2015-09-25T12:26:04Z';
+    modules.version = '1.3.3';
+    modules.livingStandard = '2015-09-25T12:26:04Z';
 
-	/**
-	 * constructor
-	 *
-	 */
-	modules.Parser = function () {
-		this.rootPrefix = 'h-';
-		this.propertyPrefixes = ['p-', 'dt-', 'u-', 'e-'];
-		this.excludeTags = ['br', 'hr'];
-	};
+    /**
+     * constructor
+     *
+     */
+    modules.Parser = function () {
+        this.rootPrefix = 'h-';
+        this.propertyPrefixes = ['p-', 'dt-', 'u-', 'e-'];
+        this.excludeTags = ['br', 'hr'];
+    };
 
 
-	// create objects incase the v1 map modules don't load
-	modules.maps = (modules.maps)? modules.maps : {};
-	modules.rels = (modules.rels)? modules.rels : {};
+    // create objects incase the v1 map modules don't load
+    modules.maps = (modules.maps)? modules.maps : {};
+    modules.rels = (modules.rels)? modules.rels : {};
 
 
-	modules.Parser.prototype = {
+    modules.Parser.prototype = {
 
-		init: function(){
-			this.rootNode = null;
-			this.document = null;
-			this.options = {
-				'baseUrl': '',
-				'filters': [],
-				'textFormat': 'whitespacetrimmed',
-				'dateFormat': 'auto', // html5 for testing
-				'overlappingVersions': false,
-				'impliedPropertiesByVersion': true,
-				'parseLatLonGeo': false
-			};
-			this.rootID = 0;
-			this.errors = [];
-			this.noContentErr = 'No options.node or options.html was provided and no document object could be found.';
-		},
+        init: function(){
+            this.rootNode = null;
+            this.document = null;
+            this.options = {
+                'baseUrl': '',
+                'filters': [],
+                'textFormat': 'whitespacetrimmed',
+                'dateFormat': 'auto', // html5 for testing
+                'overlappingVersions': false,
+                'impliedPropertiesByVersion': true,
+                'parseLatLonGeo': false
+            };
+            this.rootID = 0;
+            this.errors = [];
+            this.noContentErr = 'No options.node or options.html was provided and no document object could be found.';
+        },
 
 
-		/**
-		 * internal parse function
-		 *
-		 * @param  {Object} options
-		 * @return {Object}
-		 */
-		get: function(options) {
-			var out = this.formatEmpty(),
-				data = [],
-				rels;
+        /**
+         * internal parse function
+         *
+         * @param  {Object} options
+         * @return {Object}
+         */
+        get: function(options) {
+            var out = this.formatEmpty(),
+                data = [],
+                rels;
 
-			this.init();
-			options = (options)? options : {};
-			this.mergeOptions(options);
-			this.getDOMContext( options );
+            this.init();
+            options = (options)? options : {};
+            this.mergeOptions(options);
+            this.getDOMContext( options );
 
-			// if we do not have any context create error
-			if(!this.rootNode || !this.document){
-				this.errors.push(this.noContentErr);
-			}else{
+            // if we do not have any context create error
+            if(!this.rootNode || !this.document){
+                this.errors.push(this.noContentErr);
+            }else{
 
-				// only parse h-* microformats if we need to
-				// this is added to speed up parsing
-				if(this.hasMicroformats(this.rootNode, options)){
-					this.prepareDOM( options );
+                // only parse h-* microformats if we need to
+                // this is added to speed up parsing
+                if(this.hasMicroformats(this.rootNode, options)){
+                    this.prepareDOM( options );
 
-					if(this.options.filters.length > 0){
-						// parse flat list of items
-						var newRootNode = this.findFilterNodes(this.rootNode, this.options.filters);
-						data = this.walkRoot(newRootNode);
-					}else{
-						// parse whole document from root
-						data = this.walkRoot(this.rootNode);
-					}
+                    if(this.options.filters.length > 0){
+                        // parse flat list of items
+                        var newRootNode = this.findFilterNodes(this.rootNode, this.options.filters);
+                        data = this.walkRoot(newRootNode);
+                    }else{
+                        // parse whole document from root
+                        data = this.walkRoot(this.rootNode);
+                    }
 
-					out.items = data;
-					// don't clear-up DOM if it was cloned
-					if(modules.domUtils.canCloneDocument(this.document) === false){
-						this.clearUpDom(this.rootNode);
-					}
-				}
+                    out.items = data;
+                    // don't clear-up DOM if it was cloned
+                    if(modules.domUtils.canCloneDocument(this.document) === false){
+                        this.clearUpDom(this.rootNode);
+                    }
+                }
 
-				// find any rels
-				if(this.findRels){
-					rels = this.findRels(this.rootNode);
-					out.rels = rels.rels;
-					out['rel-urls'] = rels['rel-urls'];
-				}
+                // find any rels
+                if(this.findRels){
+                    rels = this.findRels(this.rootNode);
+                    out.rels = rels.rels;
+                    out['rel-urls'] = rels['rel-urls'];
+                }
 
-			}
+            }
 
-			if(this.errors.length > 0){
-				return this.formatError();
-			}
-			return out;
-		},
+            if(this.errors.length > 0){
+                return this.formatError();
+            }
+            return out;
+        },
 
 
-		/**
-		 * parse to get parent microformat of passed node
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Object} options
-		 * @return {Object}
-		 */
-		getParent: function(node, options) {
-			this.init();
-			options = (options)? options : {};
+        /**
+         * parse to get parent microformat of passed node
+         *
+         * @param  {DOM Node} node
+         * @param  {Object} options
+         * @return {Object}
+         */
+        getParent: function(node, options) {
+            this.init();
+            options = (options)? options : {};
 
-			if(node){
-				return this.getParentTreeWalk(node, options);
-			}else{
-				this.errors.push(this.noContentErr);
-				return this.formatError();
-			}
-		},
+            if(node){
+                return this.getParentTreeWalk(node, options);
+            }else{
+                this.errors.push(this.noContentErr);
+                return this.formatError();
+            }
+        },
 
 
-	    /**
-		 * get the count of microformats
-		 *
-		 * @param  {DOM Node} rootNode
-		 * @return {Int}
-		 */
-		count: function( options ) {
-			var out = {},
-				items,
-				classItems,
-				x,
-				i;
+        /**
+         * get the count of microformats
+         *
+         * @param  {DOM Node} rootNode
+         * @return {Int}
+         */
+        count: function( options ) {
+            var out = {},
+                items,
+                classItems,
+                x,
+                i;
 
-			this.init();
-			options = (options)? options : {};
-			this.getDOMContext( options );
+            this.init();
+            options = (options)? options : {};
+            this.getDOMContext( options );
 
-			// if we do not have any context create error
-			if(!this.rootNode || !this.document){
-				return {'errors': [this.noContentErr]};
-			}else{
+            // if we do not have any context create error
+            if(!this.rootNode || !this.document){
+                return {'errors': [this.noContentErr]};
+            }else{
 
-				items = this.findRootNodes( this.rootNode, true );
-				i = items.length;
-				while(i--) {
-					classItems = modules.domUtils.getAttributeList(items[i], 'class');
-					x = classItems.length;
-					while(x--) {
-						// find v2 names
-						if(modules.utils.startWith( classItems[x], 'h-' )){
-							this.appendCount(classItems[x], 1, out);
-						}
-						// find v1 names
-						for(var key in modules.maps) {
-							// dont double count if v1 and v2 roots are present
-							if(modules.maps[key].root === classItems[x] && classItems.indexOf(key) === -1) {
-								this.appendCount(key, 1, out);
-							}
-						}
-					}
-				}
-				var relCount = this.countRels( this.rootNode );
-				if(relCount > 0){
-					out.rels = relCount;
-				}
+                items = this.findRootNodes( this.rootNode, true );
+                i = items.length;
+                while(i--) {
+                    classItems = modules.domUtils.getAttributeList(items[i], 'class');
+                    x = classItems.length;
+                    while(x--) {
+                        // find v2 names
+                        if(modules.utils.startWith( classItems[x], 'h-' )){
+                            this.appendCount(classItems[x], 1, out);
+                        }
+                        // find v1 names
+                        for(var key in modules.maps) {
+                            // dont double count if v1 and v2 roots are present
+                            if(modules.maps[key].root === classItems[x] && classItems.indexOf(key) === -1) {
+                                this.appendCount(key, 1, out);
+                            }
+                        }
+                    }
+                }
+                var relCount = this.countRels( this.rootNode );
+                if(relCount > 0){
+                    out.rels = relCount;
+                }
 
-				return out;
-			}
-		},
+                return out;
+            }
+        },
 
 
-		/**
-		 * does a node have a class that marks it as a microformats root
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Objecte} options
-		 * @return {Boolean}
-		 */
-		isMicroformat: function( node, options ) {
-			var classes,
-				i;
+        /**
+         * does a node have a class that marks it as a microformats root
+         *
+         * @param  {DOM Node} node
+         * @param  {Objecte} options
+         * @return {Boolean}
+         */
+        isMicroformat: function( node, options ) {
+            var classes,
+                i;
 
-			if(!node){
-				return false;
-			}
+            if(!node){
+                return false;
+            }
 
-			// if documemt gets topmost node
-			node = modules.domUtils.getTopMostNode( node );
+            // if documemt gets topmost node
+            node = modules.domUtils.getTopMostNode( node );
 
-			// look for h-* microformats
-			classes = this.getUfClassNames(node);
-			if(options && options.filters && modules.utils.isArray(options.filters)){
-				i = options.filters.length;
-				while(i--) {
-					if(classes.root.indexOf(options.filters[i]) > -1){
-						return true;
-					}
-				}
-				return false;
-			}else{
-				return (classes.root.length > 0);
-			}
-		},
+            // look for h-* microformats
+            classes = this.getUfClassNames(node);
+            if(options && options.filters && modules.utils.isArray(options.filters)){
+                i = options.filters.length;
+                while(i--) {
+                    if(classes.root.indexOf(options.filters[i]) > -1){
+                        return true;
+                    }
+                }
+                return false;
+            }else{
+                return (classes.root.length > 0);
+            }
+        },
 
 
-		/**
-		 * does a node or its children have microformats
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Objecte} options
-		 * @return {Boolean}
-		 */
-		hasMicroformats: function( node, options ) {
-			var items,
-				i;
+        /**
+         * does a node or its children have microformats
+         *
+         * @param  {DOM Node} node
+         * @param  {Objecte} options
+         * @return {Boolean}
+         */
+        hasMicroformats: function( node, options ) {
+            var items,
+                i;
 
-			if(!node){
-				return false;
-			}
+            if(!node){
+                return false;
+            }
 
-			// if browser based documemt get topmost node
-			node = modules.domUtils.getTopMostNode( node );
+            // if browser based documemt get topmost node
+            node = modules.domUtils.getTopMostNode( node );
 
-			// returns all microformat roots
-			items = this.findRootNodes( node, true );
-			if(options && options.filters && modules.utils.isArray(options.filters)){
-				i = items.length;
-				while(i--) {
-					if( this.isMicroformat( items[i], options ) ){
-						return true;
-					}
-				}
-				return false;
-			}else{
-				return (items.length > 0);
-			}
-		},
+            // returns all microformat roots
+            items = this.findRootNodes( node, true );
+            if(options && options.filters && modules.utils.isArray(options.filters)){
+                i = items.length;
+                while(i--) {
+                    if( this.isMicroformat( items[i], options ) ){
+                        return true;
+                    }
+                }
+                return false;
+            }else{
+                return (items.length > 0);
+            }
+        },
 
 
-		/**
-		 * add a new v1 mapping object to parser
-		 *
-		 * @param  {Array} maps
-		 */
-		add: function( maps ){
-			maps.forEach(function(map){
-				if(map && map.root && map.name && map.properties){
-				modules.maps[map.name] = JSON.parse(JSON.stringify(map));
-				}
-			});
-		},
+        /**
+         * add a new v1 mapping object to parser
+         *
+         * @param  {Array} maps
+         */
+        add: function( maps ){
+            maps.forEach(function(map){
+                if(map && map.root && map.name && map.properties){
+                modules.maps[map.name] = JSON.parse(JSON.stringify(map));
+                }
+            });
+        },
 
 
-		/**
-		 * internal parse to get parent microformats by walking up the tree
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Object} options
-		 * @param  {Int} recursive
-		 * @return {Object}
-		 */
-		getParentTreeWalk: function (node, options, recursive) {
-			options = (options)? options : {};
+        /**
+         * internal parse to get parent microformats by walking up the tree
+         *
+         * @param  {DOM Node} node
+         * @param  {Object} options
+         * @param  {Int} recursive
+         * @return {Object}
+         */
+        getParentTreeWalk: function (node, options, recursive) {
+            options = (options)? options : {};
 
-			// recursive calls
-		    if (recursive === undefined) {
-		        if (node.parentNode && node.nodeName !== 'HTML'){
-		            return this.getParentTreeWalk(node.parentNode, options, true);
-				}else{
-		            return this.formatEmpty();
-				}
-		    }
-		    if (node !== null && node !== undefined && node.parentNode) {
-		        if (this.isMicroformat( node, options )) {
-					// if we have a match return microformat
-					options.node = node;
-		            return this.get( options );
-		        }else{
-		            return this.getParentTreeWalk(node.parentNode, options, true);
-		        }
-		    }else{
-		        return this.formatEmpty();
-		    }
-		},
+            // recursive calls
+            if (recursive === undefined) {
+                if (node.parentNode && node.nodeName !== 'HTML'){
+                    return this.getParentTreeWalk(node.parentNode, options, true);
+                }else{
+                    return this.formatEmpty();
+                }
+            }
+            if (node !== null && node !== undefined && node.parentNode) {
+                if (this.isMicroformat( node, options )) {
+                    // if we have a match return microformat
+                    options.node = node;
+                    return this.get( options );
+                }else{
+                    return this.getParentTreeWalk(node.parentNode, options, true);
+                }
+            }else{
+                return this.formatEmpty();
+            }
+        },
 
 
 
-		/**
-		 * configures what are the base DOM objects for parsing
-		 *
-		 * @param  {Object} options
-		 */
-		getDOMContext: function( options ){
-			var nodes = modules.domUtils.getDOMContext( options );
-			this.rootNode = nodes.rootNode;
-			this.document = nodes.document;
-		},
+        /**
+         * configures what are the base DOM objects for parsing
+         *
+         * @param  {Object} options
+         */
+        getDOMContext: function( options ){
+            var nodes = modules.domUtils.getDOMContext( options );
+            this.rootNode = nodes.rootNode;
+            this.document = nodes.document;
+        },
 
 
-		/**
-		 * prepares DOM before the parse begins
-		 *
-		 * @param  {Object} options
-		 * @return {Boolean}
-		 */
-		prepareDOM: function( options ){
-			var baseTag,
-				href;
+        /**
+         * prepares DOM before the parse begins
+         *
+         * @param  {Object} options
+         * @return {Boolean}
+         */
+        prepareDOM: function( options ){
+            var baseTag,
+                href;
 
             // use current document to define baseUrl, try/catch needed for IE10+ error
             try {
@@ -338,2077 +338,2077 @@ var Microformats; // jshint ignore:line
             }
 
 
-			// find base tag to set baseUrl
-			baseTag = modules.domUtils.querySelector(this.document,'base');
-			if(baseTag) {
-				href = modules.domUtils.getAttribute(baseTag, 'href');
-				if(href){
-					this.options.baseUrl = href;
-				}
-			}
-
-			// get path to rootNode
-			// then clone document
-			// then reset the rootNode to its cloned version in a new document
-			var path,
-				newDocument,
-				newRootNode;
-
-			path = modules.domUtils.getNodePath(this.rootNode);
-			newDocument = modules.domUtils.cloneDocument(this.document);
-			newRootNode = modules.domUtils.getNodeByPath(newDocument, path);
-
-			// check results as early IE fails
-			if(newDocument && newRootNode){
-				this.document = newDocument;
-				this.rootNode = newRootNode;
-			}
-
-			// add includes
-			if(this.addIncludes){
-				this.addIncludes( this.document );
-			}
-
-			return (this.rootNode && this.document);
-		},
-
-
-		/**
-		 * returns an empty structure with errors
-		 *
-		 *   @return {Object}
-		 */
-		formatError: function(){
-			var out = this.formatEmpty();
-			out.errors = this.errors;
-			return out;
-		},
-
-
-		/**
-		 * returns an empty structure
-		 *
-		 *   @return {Object}
-		 */
-		formatEmpty: function(){
-			return {
-			    'items': [],
-			    'rels': {},
-			    'rel-urls': {}
-			};
-		},
-
-
-		// find microformats of a given type and return node structures
-		findFilterNodes: function(rootNode, filters) {
-			var newRootNode = modules.domUtils.createNode('div'),
-				items = this.findRootNodes(rootNode, true),
-				i = 0,
-				x = 0,
-				y = 0;
-
-			if(items){
-				i = items.length;
-				while(x < i) {
-					// add v1 names
-					y = filters.length;
-					while (y--) {
-						if(this.getMapping(filters[y])){
-							var v1Name = this.getMapping(filters[y]).root;
-							filters.push(v1Name);
-						}
-					}
-					// append matching nodes into newRootNode
-					y = filters.length;
-					while (y--) {
-						if(modules.domUtils.hasAttributeValue(items[x], 'class', filters[y])){
-							var clone = modules.domUtils.clone(items[x]);
-							modules.domUtils.appendChild(newRootNode, clone);
-							break;
-						}
-					}
-					x++;
-				}
-			}
-
-			return newRootNode;
-		},
-
-
-		/**
-		 * appends data to output object for count
-		 *
-		 * @param  {string} name
-		 * @param  {Int} count
-		 * @param  {Object}
-		 */
-		appendCount: function(name, count, out){
-			if(out[name]){
-				out[name] = out[name] + count;
-			}else{
-				out[name] = count;
-			}
-		},
-
-
-		/**
-		 * is the microformats type in the filter list
-		 *
-		 * @param  {Object} uf
-		 * @param  {Array} filters
-		 * @return {Boolean}
-		 */
-		shouldInclude: function(uf, filters) {
-			var i;
-
-			if(modules.utils.isArray(filters) && filters.length > 0) {
-				i = filters.length;
-				while(i--) {
-					if(uf.type[0] === filters[i]) {
-						return true;
-					}
-				}
-				return false;
-			} else {
-				return true;
-			}
-		},
-
-
-		/**
-		 * finds all microformat roots in a rootNode
-		 *
-		 * @param  {DOM Node} rootNode
-		 * @param  {Boolean} includeRoot
-		 * @return {Array}
-		 */
-		findRootNodes: function(rootNode, includeRoot) {
-			var arr = null,
-				out = [],
-				classList = [],
-				items,
-				x,
-				i,
-				y,
-				key;
-
-
-			// build an array of v1 root names
-			for(key in modules.maps) {
-				if (modules.maps.hasOwnProperty(key)) {
-					classList.push(modules.maps[key].root);
-				}
-			}
-
-			// get all elements that have a class attribute
-			includeRoot = (includeRoot) ? includeRoot : false;
-			if(includeRoot && rootNode.parentNode) {
-				arr = modules.domUtils.getNodesByAttribute(rootNode.parentNode, 'class');
-			} else {
-				arr = modules.domUtils.getNodesByAttribute(rootNode, 'class');
-			}
-
-			// loop elements that have a class attribute
-			x = 0;
-			i = arr.length;
-			while(x < i) {
-
-				items = modules.domUtils.getAttributeList(arr[x], 'class');
-
-				// loop classes on an element
-				y = items.length;
-				while(y--) {
-					// match v1 root names
-					if(classList.indexOf(items[y]) > -1) {
-						out.push(arr[x]);
-						break;
-					}
-
-					// match v2 root name prefix
-					if(modules.utils.startWith(items[y], 'h-')) {
-						out.push(arr[x]);
-						break;
-					}
-				}
-
-				x++;
-			}
-			return out;
-		},
-
-
-		/**
-		 * starts the tree walk to find microformats
-		 *
-		 * @param  {DOM Node} node
-		 * @return {Array}
-		 */
-		walkRoot: function(node){
-			var context = this,
-				children = [],
-				child,
-				classes,
-				items = [],
-				out = [];
-
-			classes = this.getUfClassNames(node);
-			// if it is a root microformat node
-			if(classes && classes.root.length > 0){
-				items = this.walkTree(node);
-
-				if(items.length > 0){
-					out = out.concat(items);
-				}
-			}else{
-				// check if there are children and one of the children has a root microformat
-				children = modules.domUtils.getChildren( node );
-				if(children && children.length > 0 && this.findRootNodes(node, true).length > -1){
-					for (var i = 0; i < children.length; i++) {
-						child = children[i];
-						items = context.walkRoot(child);
-						if(items.length > 0){
-							out = out.concat(items);
-						}
-					}
-				}
-			}
-			return out;
-		},
-
-
-		/**
-		 * starts the tree walking for a single microformat
-		 *
-		 * @param  {DOM Node} node
-		 * @return {Array}
-		 */
-		walkTree: function(node) {
-			var classes,
-				out = [],
-				obj,
-				itemRootID;
-
-			// loop roots found on one element
-			classes = this.getUfClassNames(node);
-			if(classes && classes.root.length && classes.root.length > 0){
-
-				this.rootID++;
-				itemRootID = this.rootID;
-				obj = this.createUfObject(classes.root, classes.typeVersion);
-
-				this.walkChildren(node, obj, classes.root, itemRootID, classes);
-				if(this.impliedRules){
-					this.impliedRules(node, obj, classes);
-				}
-				out.push( this.cleanUfObject(obj) );
-
-
-			}
-			return out;
-		},
-
-
-		/**
-		 * finds child properties of microformat
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Object} out
-		 * @param  {String} ufName
-		 * @param  {Int} rootID
-		 * @param  {Object} parentClasses
-		 */
-		walkChildren: function(node, out, ufName, rootID, parentClasses) {
-			var context = this,
-				children = [],
-				rootItem,
-				itemRootID,
-				value,
-				propertyName,
-				propertyVersion,
-				i,
-				x,
-				y,
-				z,
-				child;
-
-			children = modules.domUtils.getChildren( node );
-
-			y = 0;
-			z = children.length;
-			while(y < z) {
-				child = children[y];
-
-				// get microformat classes for this single element
-				var classes = context.getUfClassNames(child, ufName);
-
-				// a property which is a microformat
-				if(classes.root.length > 0 && classes.properties.length > 0 && !child.addedAsRoot) {
-					// create object with type, property and value
-					rootItem = context.createUfObject(
-						classes.root,
-						classes.typeVersion,
-						modules.text.parse(this.document, child, context.options.textFormat)
-					);
-
-					// add the microformat as an array of properties
-					propertyName = context.removePropPrefix(classes.properties[0][0]);
-
-					// modifies value with "implied value rule"
-					if(parentClasses && parentClasses.root.length === 1 && parentClasses.properties.length === 1){
-						if(context.impliedValueRule){
-							out = context.impliedValueRule(out, parentClasses.properties[0][0], classes.properties[0][0], value);
-						}
-					}
-
-					if(out.properties[propertyName]) {
-						out.properties[propertyName].push(rootItem);
-					} else {
-						out.properties[propertyName] = [rootItem];
-					}
-
-					context.rootID++;
-					// used to stop duplication in heavily nested structures
-					child.addedAsRoot = true;
-
-
-					x = 0;
-					i = rootItem.type.length;
-					itemRootID = context.rootID;
-					while(x < i) {
-						context.walkChildren(child, rootItem, rootItem.type, itemRootID, classes);
-						x++;
-					}
-					if(this.impliedRules){
-						context.impliedRules(child, rootItem, classes);
-					}
-					this.cleanUfObject(rootItem);
-
-				}
-
-				// a property which is NOT a microformat and has not been used for a given root element
-				if(classes.root.length === 0 && classes.properties.length > 0) {
-
-					x = 0;
-					i = classes.properties.length;
-					while(x < i) {
-
-						value = context.getValue(child, classes.properties[x][0], out);
-						propertyName = context.removePropPrefix(classes.properties[x][0]);
-						propertyVersion = classes.properties[x][1];
-
-						// modifies value with "implied value rule"
-						if(parentClasses && parentClasses.root.length === 1 && parentClasses.properties.length === 1){
-							if(context.impliedValueRule){
-								out = context.impliedValueRule(out, parentClasses.properties[0][0], classes.properties[x][0], value);
-							}
-						}
-
-						// if we have not added this value into a property with the same name already
-						if(!context.hasRootID(child, rootID, propertyName)) {
-							// check the root and property is the same version or if overlapping versions are allowed
-							if( context.isAllowedPropertyVersion( out.typeVersion, propertyVersion ) ){
-								// add the property as an array of properties
-								if(out.properties[propertyName]) {
-									out.properties[propertyName].push(value);
-								} else {
-									out.properties[propertyName] = [value];
-								}
-								// add rootid to node so we can track its use
-								context.appendRootID(child, rootID, propertyName);
-							}
-						}
-
-						x++;
-					}
-
-					context.walkChildren(child, out, ufName, rootID, classes);
-				}
-
-				// if the node has no microformat classes, see if its children have
-				if(classes.root.length === 0 && classes.properties.length === 0) {
-					context.walkChildren(child, out, ufName, rootID, classes);
-				}
-
-				// if the node is a child root add it to the children tree
-				if(classes.root.length > 0 && classes.properties.length === 0) {
-
-					// create object with type, property and value
-					rootItem = context.createUfObject(
-						classes.root,
-						classes.typeVersion,
-						modules.text.parse(this.document, child, context.options.textFormat)
-					);
-
-					// add the microformat as an array of properties
-					if(!out.children){
-						out.children =  [];
-					}
-
-					if(!context.hasRootID(child, rootID, 'child-root')) {
-						out.children.push( rootItem );
-						context.appendRootID(child, rootID, 'child-root');
-						context.rootID++;
-					}
-
-					x = 0;
-					i = rootItem.type.length;
-					itemRootID = context.rootID;
-					while(x < i) {
-						context.walkChildren(child, rootItem, rootItem.type, itemRootID, classes);
-						x++;
-					}
-					if(this.impliedRules){
-						context.impliedRules(child, rootItem, classes);
-					}
-					context.cleanUfObject( rootItem );
-
-				}
-
-
-
-				y++;
-			}
-
-		},
-
-
-
-
-		/**
-		 * gets the value of a property from a node
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {String} className
-		 * @param  {Object} uf
-		 * @return {String || Object}
-		 */
-		getValue: function(node, className, uf) {
-			var value = '';
-
-			if(modules.utils.startWith(className, 'p-')) {
-				value = this.getPValue(node, true);
-			}
-
-			if(modules.utils.startWith(className, 'e-')) {
-				value = this.getEValue(node);
-			}
-
-			if(modules.utils.startWith(className, 'u-')) {
-				value = this.getUValue(node, true);
-			}
-
-			if(modules.utils.startWith(className, 'dt-')) {
-				value = this.getDTValue(node, className, uf, true);
-			}
-			return value;
-		},
-
-
-		/**
-		 * gets the value of a node which contains a 'p-' property
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Boolean} valueParse
-		 * @return {String}
-		 */
-		getPValue: function(node, valueParse) {
-			var out = '';
-			if(valueParse) {
-				out = this.getValueClass(node, 'p');
-			}
-
-			if(!out && valueParse) {
-				out = this.getValueTitle(node);
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title');
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['data','input'], 'value');
-			}
-
-			if(node.name === 'br' || node.name === 'hr') {
-				out = '';
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['img', 'area'], 'alt');
-			}
-
-			if(!out) {
-				out = modules.text.parse(this.document, node, this.options.textFormat);
-			}
-
-			return(out) ? out : '';
-		},
-
-
-		/**
-		 * gets the value of a node which contains the 'e-' property
-		 *
-		 * @param  {DOM Node} node
-		 * @return {Object}
-		 */
-		getEValue: function(node) {
-
-			var out = {value: '', html: ''};
-
-			this.expandURLs(node, 'src', this.options.baseUrl);
-			this.expandURLs(node, 'href', this.options.baseUrl);
-
-			out.value = modules.text.parse(this.document, node, this.options.textFormat);
-			out.html = modules.html.parse(node);
-
-			return out;
-		},
-
-
-		/**
-		 * gets the value of a node which contains the 'u-' property
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Boolean} valueParse
-		 * @return {String}
-		 */
-		getUValue: function(node, valueParse) {
-			var out = '';
-			if(valueParse) {
-				out = this.getValueClass(node, 'u');
-			}
-
-			if(!out && valueParse) {
-				out = this.getValueTitle(node);
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['a', 'area'], 'href');
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['img','audio','video','source'], 'src');
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['object'], 'data');
-			}
-
-			// if we have no protocol separator, turn relative url to absolute url
-			if(out && out !== '' && out.indexOf('://') === -1) {
-				out = modules.url.resolve(out, this.options.baseUrl);
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title');
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['data','input'], 'value');
-			}
-
-			if(!out) {
-				out = modules.text.parse(this.document, node, this.options.textFormat);
-			}
-
-			return(out) ? out : '';
-		},
-
-
-		/**
-		 * gets the value of a node which contains the 'dt-' property
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {String} className
-		 * @param  {Object} uf
-		 * @param  {Boolean} valueParse
-		 * @return {String}
-		 */
-		getDTValue: function(node, className, uf, valueParse) {
-			var out = '';
-
-			if(valueParse) {
-				out = this.getValueClass(node, 'dt');
-			}
-
-			if(!out && valueParse) {
-				out = this.getValueTitle(node);
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['time', 'ins', 'del'], 'datetime');
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title');
-			}
-
-			if(!out) {
-				out = modules.domUtils.getAttrValFromTagList(node, ['data', 'input'], 'value');
-			}
-
-			if(!out) {
-				out = modules.text.parse(this.document, node, this.options.textFormat);
-			}
-
-			if(out) {
-				if(modules.dates.isDuration(out)) {
-					// just duration
-					return out;
-				} else if(modules.dates.isTime(out)) {
-					// just time or time+timezone
-					if(uf) {
-						uf.times.push([className, modules.dates.parseAmPmTime(out, this.options.dateFormat)]);
-					}
-					return modules.dates.parseAmPmTime(out, this.options.dateFormat);
-				} else {
-					// returns a date - microformat profile
-					if(uf) {
-						uf.dates.push([className, new modules.ISODate(out).toString( this.options.dateFormat )]);
-					}
-					return new modules.ISODate(out).toString( this.options.dateFormat );
-				}
-			} else {
-				return '';
-			}
-		},
-
-
-		/**
-		 * appends a new rootid to a given node
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {String} id
-		 * @param  {String} propertyName
-		 */
-		appendRootID: function(node, id, propertyName) {
-			if(this.hasRootID(node, id, propertyName) === false){
-				var rootids = [];
-				if(modules.domUtils.hasAttribute(node,'rootids')){
-					rootids = modules.domUtils.getAttributeList(node,'rootids');
-				}
-				rootids.push('id' + id + '-' + propertyName);
-				modules.domUtils.setAttribute(node, 'rootids', rootids.join(' '));
-			}
-		},
-
-
-		/**
-		 * does a given node already have a rootid
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {String} id
-		 * @param  {String} propertyName
-		 * @return {Boolean}
-		 */
-		hasRootID: function(node, id, propertyName) {
-			var rootids = [];
-			if(!modules.domUtils.hasAttribute(node,'rootids')){
-				return false;
-			} else {
-				rootids = modules.domUtils.getAttributeList(node, 'rootids');
-				return (rootids.indexOf('id' + id + '-' + propertyName) > -1);
-			}
-		},
-
-
-
-		/**
-		 * gets the text of any child nodes with a class value
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {String} propertyName
-		 * @return {String || null}
-		 */
-		getValueClass: function(node, propertyType) {
-			var context = this,
-				children = [],
-				out = [],
-				child,
-				x,
-				i;
-
-			children = modules.domUtils.getChildren( node );
-
-			x = 0;
-			i = children.length;
-			while(x < i) {
-				child = children[x];
-				var value = null;
-				if(modules.domUtils.hasAttributeValue(child, 'class', 'value')) {
-					switch(propertyType) {
-					case 'p':
-						value = context.getPValue(child, false);
-						break;
-					case 'u':
-						value = context.getUValue(child, false);
-						break;
-					case 'dt':
-						value = context.getDTValue(child, '', null, false);
-						break;
-					}
-					if(value) {
-						out.push(modules.utils.trim(value));
-					}
-				}
-				x++;
-			}
-			if(out.length > 0) {
-				if(propertyType === 'p') {
-					return modules.text.parseText( this.document, out.join(' '), this.options.textFormat);
-				}
-				if(propertyType === 'u') {
-					return out.join('');
-				}
-				if(propertyType === 'dt') {
-					return modules.dates.concatFragments(out,this.options.dateFormat).toString(this.options.dateFormat);
-				}
-			} else {
-				return null;
-			}
-		},
-
-
-		/**
-		 * returns a single string of the 'title' attr from all
-		 * the child nodes with the class 'value-title'
-		 *
-		 * @param  {DOM Node} node
-		 * @return {String}
-		 */
-		getValueTitle: function(node) {
-			var out = [],
-				items,
-				i,
-				x;
-
-			items = modules.domUtils.getNodesByAttributeValue(node, 'class', 'value-title');
-			x = 0;
-			i = items.length;
-			while(x < i) {
-				if(modules.domUtils.hasAttribute(items[x], 'title')) {
-					out.push(modules.domUtils.getAttribute(items[x], 'title'));
-				}
-				x++;
-			}
-			return out.join('');
-		},
-
-
-	   /**
-		 * finds out whether a node has h-* class v1 and v2
-		 *
-		 * @param  {DOM Node} node
-		 * @return {Boolean}
-		 */
-		hasHClass: function(node){
-			var classes = this.getUfClassNames(node);
-			if(classes.root && classes.root.length > 0){
-				return true;
-			}else{
-				return false;
-			}
-		},
-
-
-		/**
-		 * get both the root and property class names from a node
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Array} ufNameArr
-		 * @return {Object}
-		 */
-		getUfClassNames: function(node, ufNameArr) {
-			var context = this,
-				out = {
-					'root': [],
-					'properties': []
-				},
-				classNames,
-				key,
-				items,
-				item,
-				i,
-				x,
-				z,
-				y,
-				map,
-				prop,
-				propName,
-				v2Name,
-				impiedRel,
-				ufName;
-
-			// don't get classes from excluded list of tags
-			if(modules.domUtils.hasTagName(node, this.excludeTags) === false){
-
-				// find classes for node
-				classNames = modules.domUtils.getAttribute(node, 'class');
-				if(classNames) {
-					items = classNames.split(' ');
-					x = 0;
-					i = items.length;
-					while(x < i) {
-
-						item = modules.utils.trim(items[x]);
-
-						// test for root prefix - v2
-						if(modules.utils.startWith(item, context.rootPrefix)) {
-							if(out.root.indexOf(item) === -1){
-								out.root.push(item);
-							}
-							out.typeVersion = 'v2';
-						}
-
-						// test for property prefix - v2
-						z = context.propertyPrefixes.length;
-						while(z--) {
-							if(modules.utils.startWith(item, context.propertyPrefixes[z])) {
-								out.properties.push([item,'v2']);
-							}
-						}
-
-						// test for mapped root classnames v1
-						for(key in modules.maps) {
-							if(modules.maps.hasOwnProperty(key)) {
-								// only add a root once
-								if(modules.maps[key].root === item && out.root.indexOf(key) === -1) {
-									// if root map has subTree set to true
-									// test to see if we should create a property or root
-									if(modules.maps[key].subTree) {
-										out.properties.push(['p-' + modules.maps[key].root, 'v1']);
-									} else {
-										out.root.push(key);
-										if(!out.typeVersion){
-											out.typeVersion = 'v1';
-										}
-									}
-								}
-							}
-						}
-
-
-						// test for mapped property classnames v1
-						if(ufNameArr){
-							for (var a = 0; a < ufNameArr.length; a++) {
-								ufName = ufNameArr[a];
-								// get mapped property v1 microformat
-								map = context.getMapping(ufName);
-								if(map) {
-									for(key in map.properties) {
-										if (map.properties.hasOwnProperty(key)) {
-
-											prop = map.properties[key];
-											propName = (prop.map) ? prop.map : 'p-' + key;
-
-											if(key === item) {
-												if(prop.uf) {
-													// loop all the classList make sure
-													//   1. this property is a root
-													//   2. that there is not already an equivalent v2 property i.e. url and u-url on the same element
-													y = 0;
-													while(y < i) {
-														v2Name = context.getV2RootName(items[y]);
-														// add new root
-														if(prop.uf.indexOf(v2Name) > -1 && out.root.indexOf(v2Name) === -1) {
-															out.root.push(v2Name);
-															out.typeVersion = 'v1';
-														}
-														y++;
-													}
-													//only add property once
-													if(out.properties.indexOf(propName) === -1) {
-														out.properties.push([propName,'v1']);
-													}
-												} else {
-													if(out.properties.indexOf(propName) === -1) {
-														out.properties.push([propName,'v1']);
-													}
-												}
-											}
-										}
-
-									}
-								}
-							}
-
-						}
-
-						x++;
-
-					}
-				}
-			}
-
-
-			// finds any alt rel=* mappings for a given node/microformat
-			if(ufNameArr && this.findRelImpied){
-				for (var b = 0; b < ufNameArr.length; b++) {
-					ufName = ufNameArr[b];
-					impiedRel = this.findRelImpied(node, ufName);
-					if(impiedRel && out.properties.indexOf(impiedRel) === -1) {
-						out.properties.push([impiedRel, 'v1']);
-					}
-				}
-			}
-
-
-			//if(out.root.length === 1 && out.properties.length === 1) {
-			//	if(out.root[0].replace('h-','') === this.removePropPrefix(out.properties[0][0])) {
-			//		out.typeVersion = 'v2';
-			//	}
-			//}
-
-			return out;
-		},
-
-
-		/**
-		 * given a v1 or v2 root name, return mapping object
-		 *
-		 * @param  {String} name
-		 * @return {Object || null}
-		 */
-		getMapping: function(name) {
-			var key;
-			for(key in modules.maps) {
-				if(modules.maps[key].root === name || key === name) {
-					return modules.maps[key];
-				}
-			}
-			return null;
-		},
-
-
-		/**
-		 * given a v1 root name returns a v2 root name i.e. vcard >>> h-card
-		 *
-		 * @param  {String} name
-		 * @return {String || null}
-		 */
-		getV2RootName: function(name) {
-			var key;
-			for(key in modules.maps) {
-				if(modules.maps[key].root === name) {
-					return key;
-				}
-			}
-			return null;
-		},
-
-
-		/**
-		 * whether a property is the right microformats version for its root type
-		 *
-		 * @param  {String} typeVersion
-		 * @param  {String} propertyVersion
-		 * @return {Boolean}
-		 */
-		isAllowedPropertyVersion: function(typeVersion, propertyVersion){
-			if(this.options.overlappingVersions === true){
-				return true;
-			}else{
-				return (typeVersion === propertyVersion);
-			}
-		},
-
-
-		/**
-		 * creates a blank microformats object
-		 *
-		 * @param  {String} name
-		 * @param  {String} value
-		 * @return {Object}
-		 */
-		createUfObject: function(names, typeVersion, value) {
-			var out = {};
-
-			// is more than just whitespace
-			if(value && modules.utils.isOnlyWhiteSpace(value) === false) {
-				out.value = value;
-			}
-			// add type i.e. ["h-card", "h-org"]
-			if(modules.utils.isArray(names)) {
-				out.type = names;
-			} else {
-				out.type = [names];
-			}
-			out.properties = {};
-			// metadata properties for parsing
-			out.typeVersion = typeVersion;
-			out.times = [];
-			out.dates = [];
-			out.altValue = null;
-
-			return out;
-		},
-
-
-		/**
-		 * removes unwanted microformats property before output
-		 *
-		 * @param  {Object} microformat
-		 */
-		cleanUfObject: function( microformat ) {
-			delete microformat.times;
-			delete microformat.dates;
-			delete microformat.typeVersion;
-			delete microformat.altValue;
-			return microformat;
-		},
-
-
-
-		/**
-		 * removes microformat property prefixes from text
-		 *
-		 * @param  {String} text
-		 * @return {String}
-		 */
-		removePropPrefix: function(text) {
-			var i;
-
-			i = this.propertyPrefixes.length;
-			while(i--) {
-				var prefix = this.propertyPrefixes[i];
-				if(modules.utils.startWith(text, prefix)) {
-					text = text.substr(prefix.length);
-				}
-			}
-			return text;
-		},
-
-
-		/**
-		 * expands all relative URLs to absolute ones where it can
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {String} attrName
-		 * @param  {String} baseUrl
-		 */
-		expandURLs: function(node, attrName, baseUrl){
-			var i,
-				nodes,
-				attr;
-
-			nodes = modules.domUtils.getNodesByAttribute(node, attrName);
-			i = nodes.length;
-			while (i--) {
-				try{
-					// the url parser can blow up if the format is not right
-					attr = modules.domUtils.getAttribute(nodes[i], attrName);
-					if(attr && attr !== '' && baseUrl !== '' && attr.indexOf('://') === -1) {
-						//attr = urlParser.resolve(baseUrl, attr);
-						attr = modules.url.resolve(attr, baseUrl);
-						modules.domUtils.setAttribute(nodes[i], attrName, attr);
-					}
-				}catch(err){
-					// do nothing - convert only the urls we can, leave the rest as they are
-				}
-			}
-		},
-
-
-
-		/**
-		 * merges passed and default options -single level clone of properties
-		 *
-		 * @param  {Object} options
-		 */
-		mergeOptions: function(options) {
-			var key;
-			for(key in options) {
-				if(options.hasOwnProperty(key)) {
-					this.options[key] = options[key];
-				}
-			}
-		},
-
-
-		/**
-		 * removes all rootid attributes
-		 *
-		 * @param  {DOM Node} rootNode
-		 */
-		removeRootIds: function(rootNode){
-			var arr,
-				i;
-
-			arr = modules.domUtils.getNodesByAttribute(rootNode, 'rootids');
-			i = arr.length;
-			while(i--) {
-				modules.domUtils.removeAttribute(arr[i],'rootids');
-			}
-		},
-
-
-		/**
-		 * removes all changes made to the DOM
-		 *
-		 * @param  {DOM Node} rootNode
-		 */
-		clearUpDom: function(rootNode){
-			if(this.removeIncludes){
-				this.removeIncludes(rootNode);
-			}
-			this.removeRootIds(rootNode);
-		}
-
-
-	};
-
-
-	modules.Parser.prototype.constructor = modules.Parser;
-
-
-	// check parser module is loaded
-	if(modules.Parser){
-
-		/**
-		 * applies "implied rules" microformat output structure i.e. feed-title, name, photo, url and date
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Object} uf (microformat output structure)
-		 * @param  {Object} parentClasses (classes structure)
-		 * @param  {Boolean} impliedPropertiesByVersion
-		 * @return {Object}
-		 */
-		 modules.Parser.prototype.impliedRules = function(node, uf, parentClasses) {
-			var typeVersion = (uf.typeVersion)? uf.typeVersion: 'v2';
-
-			// TEMP: override to allow v1 implied properties while spec changes
-			if(this.options.impliedPropertiesByVersion === false){
-				typeVersion = 'v2';
-			}
-
-			if(node && uf && uf.properties) {
-				uf = this.impliedBackwardComp( node, uf, parentClasses );
-				if(typeVersion === 'v2'){
-					uf = this.impliedhFeedTitle( uf );
-					uf = this.impliedName( node, uf );
-					uf = this.impliedPhoto( node, uf );
-					uf = this.impliedUrl( node, uf );
-				}
-				uf = this.impliedValue( node, uf, parentClasses );
-				uf = this.impliedDate( uf );
-
-				// TEMP: flagged while spec changes are put forward
-				if(this.options.parseLatLonGeo === true){
-					uf = this.impliedGeo( uf );
-				}
-			}
-
-			return uf;
-		};
-
-
-		/**
-		 * apply implied name rule
-		 *
-		 * @param  {DOM Node} node
-		 * @param  {Object} uf
-		 * @return {Object}
-		 */
-		modules.Parser.prototype.impliedName = function(node, uf) {
-			// implied name rule
-			/*
-				img.h-x[alt]										Glenn Jones
-				area.h-x[alt] 										Glenn Jones
-				abbr.h-x[title]										
-
-				.h-x>img:only-child[alt]:not[.h-*]					
- .h-x>area:only-child[alt]:not[.h-*]
Glenn Jones
- .h-x>abbr:only-child[title]
GJ
- - .h-x>:only-child>img:only-child[alt]:not[.h-*]
Jane Doe
- .h-x>:only-child>area:only-child[alt]:not[.h-*]
Jane Doe
- .h-x>:only-child>abbr:only-child[title]
JD
- */ - var name, - value; - - if(!uf.properties.name) { - value = this.getImpliedProperty(node, ['img', 'area', 'abbr'], this.getNameAttr); - var textFormat = this.options.textFormat; - // if no value for tags/properties use text - if(!value) { - name = [modules.text.parse(this.document, node, textFormat)]; - }else{ - name = [modules.text.parseText(this.document, value, textFormat)]; - } - if(name && name[0] !== ''){ - uf.properties.name = name; - } - } - - return uf; - }; - - - /** - * apply implied photo rule - * - * @param {DOM Node} node - * @param {Object} uf - * @return {Object} - */ - modules.Parser.prototype.impliedPhoto = function(node, uf) { - // implied photo rule - /* - img.h-x[src] Jane Doe - object.h-x[data] Jane Doe - .h-x>img[src]:only-of-type:not[.h-*]
Jane Doe
- .h-x>object[data]:only-of-type:not[.h-*]
Jane Doe
- .h-x>:only-child>img[src]:only-of-type:not[.h-*]
Jane Doe
- .h-x>:only-child>object[data]:only-of-type:not[.h-*]
Jane Doe
- */ - var value; - if(!uf.properties.photo) { - value = this.getImpliedProperty(node, ['img', 'object'], this.getPhotoAttr); - if(value) { - // relative to absolute URL - if(value && value !== '' && this.options.baseUrl !== '' && value.indexOf('://') === -1) { - value = modules.url.resolve(value, this.options.baseUrl); - } - uf.properties.photo = [modules.utils.trim(value)]; - } - } - return uf; - }; - - - /** - * apply implied URL rule - * - * @param {DOM Node} node - * @param {Object} uf - * @return {Object} - */ - modules.Parser.prototype.impliedUrl = function(node, uf) { - // implied URL rule - /* - a.h-x[href] Glenn - area.h-x[href] Glenn - .h-x>a[href]:only-of-type:not[.h-*] - .h-x>area[href]:only-of-type:not[.h-*]
Glenn

...

- */ - var value; - if(!uf.properties.url) { - value = this.getImpliedProperty(node, ['a', 'area'], this.getURLAttr); - if(value) { - // relative to absolute URL - if(value && value !== '' && this.options.baseUrl !== '' && value.indexOf('://') === -1) { - value = modules.url.resolve(value, this.options.baseUrl); - } - uf.properties.url = [modules.utils.trim(value)]; - } - } - return uf; - }; - - - /** - * apply implied date rule - if there is a time only property try to concat it with any date property - * - * @param {DOM Node} node - * @param {Object} uf - * @return {Object} - */ - modules.Parser.prototype.impliedDate = function(uf) { - // implied date rule - // http://microformats.org/wiki/value-class-pattern#microformats2_parsers - // http://microformats.org/wiki/microformats2-parsing-issues#implied_date_for_dt_properties_both_mf2_and_backcompat - var newDate; - if(uf.times.length > 0 && uf.dates.length > 0) { - newDate = modules.dates.dateTimeUnion(uf.dates[0][1], uf.times[0][1], this.options.dateFormat); - uf.properties[this.removePropPrefix(uf.times[0][0])][0] = newDate.toString(this.options.dateFormat); - } - // clean-up object - delete uf.times; - delete uf.dates; - return uf; - }; - - - /** - * get an implied property value from pre-defined tag/attriubte combinations - * - * @param {DOM Node} node - * @param {String} tagList (Array of tags from which an implied value can be pulled) - * @param {String} getAttrFunction (Function which can extract implied value) - * @return {String || null} - */ - modules.Parser.prototype.getImpliedProperty = function(node, tagList, getAttrFunction) { - // i.e. img.h-card - var value = getAttrFunction(node), - descendant, - child; - - if(!value) { - // i.e. .h-card>img:only-of-type:not(.h-card) - descendant = modules.domUtils.getSingleDescendantOfType( node, tagList); - if(descendant && this.hasHClass(descendant) === false){ - value = getAttrFunction(descendant); - } - if(node.children.length > 0 ){ - // i.e. .h-card>:only-child>img:only-of-type:not(.h-card) - child = modules.domUtils.getSingleDescendant(node); - if(child && this.hasHClass(child) === false){ - descendant = modules.domUtils.getSingleDescendantOfType(child, tagList); - if(descendant && this.hasHClass(descendant) === false){ - value = getAttrFunction(descendant); - } - } - } - } - - return value; - }; - - - /** - * get an implied name value from a node - * - * @param {DOM Node} node - * @return {String || null} - */ - modules.Parser.prototype.getNameAttr = function(node) { - var value = modules.domUtils.getAttrValFromTagList(node, ['img','area'], 'alt'); - if(!value) { - value = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title'); - } - return value; - }; - - - /** - * get an implied photo value from a node - * - * @param {DOM Node} node - * @return {String || null} - */ - modules.Parser.prototype.getPhotoAttr = function(node) { - var value = modules.domUtils.getAttrValFromTagList(node, ['img'], 'src'); - if(!value && modules.domUtils.hasAttributeValue(node, 'class', 'include') === false) { - value = modules.domUtils.getAttrValFromTagList(node, ['object'], 'data'); - } - return value; - }; - - - /** - * get an implied photo value from a node - * - * @param {DOM Node} node - * @return {String || null} - */ - modules.Parser.prototype.getURLAttr = function(node) { - var value = null; - if(modules.domUtils.hasAttributeValue(node, 'class', 'include') === false){ - - value = modules.domUtils.getAttrValFromTagList(node, ['a'], 'href'); - if(!value) { - value = modules.domUtils.getAttrValFromTagList(node, ['area'], 'href'); - } - - } - return value; - }; - - - /** - * - * - * @param {DOM Node} node - * @param {Object} uf - * @return {Object} - */ - modules.Parser.prototype.impliedValue = function(node, uf, parentClasses){ - - // intersection of implied name and implied value rules - if(uf.properties.name) { - if(uf.value && parentClasses.root.length > 0 && parentClasses.properties.length === 1){ - uf = this.getAltValue(uf, parentClasses.properties[0][0], 'p-name', uf.properties.name[0]); - } - } - - // intersection of implied URL and implied value rules - if(uf.properties.url) { - if(parentClasses && parentClasses.root.length === 1 && parentClasses.properties.length === 1){ - uf = this.getAltValue(uf, parentClasses.properties[0][0], 'u-url', uf.properties.url[0]); - } - } - - // apply alt value - if(uf.altValue !== null){ - uf.value = uf.altValue.value; - } - delete uf.altValue; - - - return uf; - }; - - - /** - * get alt value based on rules about parent property prefix - * - * @param {Object} uf - * @param {String} parentPropertyName - * @param {String} propertyName - * @param {String} value - * @return {Object} - */ - modules.Parser.prototype.getAltValue = function(uf, parentPropertyName, propertyName, value){ - if(uf.value && !uf.altValue){ - // first p-name of the h-* child - if(modules.utils.startWith(parentPropertyName,'p-') && propertyName === 'p-name'){ - uf.altValue = {name: propertyName, value: value}; - } - // if it's an e-* property element - if(modules.utils.startWith(parentPropertyName,'e-') && modules.utils.startWith(propertyName,'e-')){ - uf.altValue = {name: propertyName, value: value}; - } - // if it's an u-* property element - if(modules.utils.startWith(parentPropertyName,'u-') && propertyName === 'u-url'){ - uf.altValue = {name: propertyName, value: value}; - } - } - return uf; - }; - - - /** - * if a h-feed does not have a title use the title tag of a page - * - * @param {Object} uf - * @return {Object} - */ - modules.Parser.prototype.impliedhFeedTitle = function( uf ){ - if(uf.type && uf.type.indexOf('h-feed') > -1){ - // has no name property - if(uf.properties.name === undefined || uf.properties.name[0] === '' ){ - // use the text from the title tag - var title = modules.domUtils.querySelector(this.document, 'title'); - if(title){ - uf.properties.name = [modules.domUtils.textContent(title)]; - } - } - } - return uf; - }; - - - - /** - * implied Geo from pattern - * - * @param {Object} uf - * @return {Object} - */ - modules.Parser.prototype.impliedGeo = function( uf ){ - var geoPair, - parts, - longitude, - latitude, - valid = true; - - if(uf.type && uf.type.indexOf('h-geo') > -1){ - - // has no latitude or longitude property - if(uf.properties.latitude === undefined || uf.properties.longitude === undefined ){ - - geoPair = (uf.properties.name)? uf.properties.name[0] : null; - geoPair = (!geoPair && uf.properties.value)? uf.properties.value : geoPair; - - if(geoPair){ - // allow for the use of a ';' as in microformats and also ',' as in Geo URL - geoPair = geoPair.replace(';',','); - - // has sep char - if(geoPair.indexOf(',') > -1 ){ - parts = geoPair.split(','); - - // only correct if we have two or more parts - if(parts.length > 1){ - - // latitude no value outside the range -90 or 90 - latitude = parseFloat( parts[0] ); - if(modules.utils.isNumber(latitude) && latitude > 90 || latitude < -90){ - valid = false; - } - - // longitude no value outside the range -180 to 180 - longitude = parseFloat( parts[1] ); - if(modules.utils.isNumber(longitude) && longitude > 180 || longitude < -180){ - valid = false; - } - - if(valid){ - uf.properties.latitude = [latitude]; - uf.properties.longitude = [longitude]; - } - } - - } - } - } - } - return uf; - }; - - - /** - * if a backwards compat built structure has no properties add name through this.impliedName - * - * @param {Object} uf - * @return {Object} - */ - modules.Parser.prototype.impliedBackwardComp = function(node, uf, parentClasses){ - - // look for pattern in parent classes like "p-geo h-geo" - // these are structures built from backwards compat parsing of geo - if(parentClasses.root.length === 1 && parentClasses.properties.length === 1) { - if(parentClasses.root[0].replace('h-','') === this.removePropPrefix(parentClasses.properties[0][0])) { - - // if microformat has no properties apply the impliedName rule to get value from containing node - // this will get value from html such as Brighton - if( modules.utils.hasProperties(uf.properties) === false ){ - uf = this.impliedName( node, uf ); - } - } - } - - return uf; - }; - - - - } - - - // check parser module is loaded - if(modules.Parser){ - - - /** - * appends clones of include Nodes into the DOM structure - * - * @param {DOM node} rootNode - */ - modules.Parser.prototype.addIncludes = function(rootNode) { - this.addAttributeIncludes(rootNode, 'itemref'); - this.addAttributeIncludes(rootNode, 'headers'); - this.addClassIncludes(rootNode); - }; - - - /** - * appends clones of include Nodes into the DOM structure for attribute based includes - * - * @param {DOM node} rootNode - * @param {String} attributeName - */ - modules.Parser.prototype.addAttributeIncludes = function(rootNode, attributeName) { - var arr, - idList, - i, - x, - z, - y; - - arr = modules.domUtils.getNodesByAttribute(rootNode, attributeName); - x = 0; - i = arr.length; - while(x < i) { - idList = modules.domUtils.getAttributeList(arr[x], attributeName); - if(idList) { - z = 0; - y = idList.length; - while(z < y) { - this.apppendInclude(arr[x], idList[z]); - z++; - } - } - x++; - } - }; - - - /** - * appends clones of include Nodes into the DOM structure for class based includes - * - * @param {DOM node} rootNode - */ - modules.Parser.prototype.addClassIncludes = function(rootNode) { - var id, - arr, - x = 0, - i; - - arr = modules.domUtils.getNodesByAttributeValue(rootNode, 'class', 'include'); - i = arr.length; - while(x < i) { - id = modules.domUtils.getAttrValFromTagList(arr[x], ['a'], 'href'); - if(!id) { - id = modules.domUtils.getAttrValFromTagList(arr[x], ['object'], 'data'); - } - this.apppendInclude(arr[x], id); - x++; - } - }; - - - /** - * appends a clone of an include into another Node using Id - * - * @param {DOM node} rootNode - * @param {Stringe} id - */ - modules.Parser.prototype.apppendInclude = function(node, id){ - var include, - clone; - - id = modules.utils.trim(id.replace('#', '')); - include = modules.domUtils.getElementById(this.document, id); - if(include) { - clone = modules.domUtils.clone(include); - this.markIncludeChildren(clone); - modules.domUtils.appendChild(node, clone); - } - }; - - - /** - * adds an attribute marker to all the child microformat roots - * - * @param {DOM node} rootNode - */ - modules.Parser.prototype.markIncludeChildren = function(rootNode) { - var arr, - x, - i; - - // loop the array and add the attribute - arr = this.findRootNodes(rootNode); - x = 0; - i = arr.length; - modules.domUtils.setAttribute(rootNode, 'data-include', 'true'); - modules.domUtils.setAttribute(rootNode, 'style', 'display:none'); - while(x < i) { - modules.domUtils.setAttribute(arr[x], 'data-include', 'true'); - x++; - } - }; - - - /** - * removes all appended include clones from DOM - * - * @param {DOM node} rootNode - */ - modules.Parser.prototype.removeIncludes = function(rootNode){ - var arr, - i; - - // remove all the items that were added as includes - arr = modules.domUtils.getNodesByAttribute(rootNode, 'data-include'); - i = arr.length; - while(i--) { - modules.domUtils.removeChild(rootNode,arr[i]); - } - }; - - - } - - - // check parser module is loaded - if(modules.Parser){ - - /** - * finds rel=* structures - * - * @param {DOM node} rootNode - * @return {Object} - */ - modules.Parser.prototype.findRels = function(rootNode) { - var out = { - 'items': [], - 'rels': {}, - 'rel-urls': {} - }, - x, - i, - y, - z, - relList, - items, - item, - value, - arr; - - arr = modules.domUtils.getNodesByAttribute(rootNode, 'rel'); - x = 0; - i = arr.length; - while(x < i) { - relList = modules.domUtils.getAttribute(arr[x], 'rel'); - - if(relList) { - items = relList.split(' '); - - - // add rels - z = 0; - y = items.length; - while(z < y) { - item = modules.utils.trim(items[z]); - - // get rel value - value = modules.domUtils.getAttrValFromTagList(arr[x], ['a', 'area'], 'href'); - if(!value) { - value = modules.domUtils.getAttrValFromTagList(arr[x], ['link'], 'href'); - } - - // create the key - if(!out.rels[item]) { - out.rels[item] = []; - } - - if(typeof this.options.baseUrl === 'string' && typeof value === 'string') { - - var resolved = modules.url.resolve(value, this.options.baseUrl); - // do not add duplicate rels - based on resolved URLs - if(out.rels[item].indexOf(resolved) === -1){ - out.rels[item].push( resolved ); - } - } - z++; - } - - - var url = null; - if(modules.domUtils.hasAttribute(arr[x], 'href')){ - url = modules.domUtils.getAttribute(arr[x], 'href'); - if(url){ - url = modules.url.resolve(url, this.options.baseUrl ); - } - } - - - // add to rel-urls - var relUrl = this.getRelProperties(arr[x]); - relUrl.rels = items; - // // do not add duplicate rel-urls - based on resolved URLs - if(url && out['rel-urls'][url] === undefined){ - out['rel-urls'][url] = relUrl; - } - - - } - x++; - } - return out; - }; - - - /** - * gets the properties of a rel=* - * - * @param {DOM node} node - * @return {Object} - */ - modules.Parser.prototype.getRelProperties = function(node){ - var obj = {}; - - if(modules.domUtils.hasAttribute(node, 'media')){ - obj.media = modules.domUtils.getAttribute(node, 'media'); - } - if(modules.domUtils.hasAttribute(node, 'type')){ - obj.type = modules.domUtils.getAttribute(node, 'type'); - } - if(modules.domUtils.hasAttribute(node, 'hreflang')){ - obj.hreflang = modules.domUtils.getAttribute(node, 'hreflang'); - } - if(modules.domUtils.hasAttribute(node, 'title')){ - obj.title = modules.domUtils.getAttribute(node, 'title'); - } - if(modules.utils.trim(this.getPValue(node, false)) !== ''){ - obj.text = this.getPValue(node, false); - } - - return obj; - }; - - - /** - * finds any alt rel=* mappings for a given node/microformat - * - * @param {DOM node} node - * @param {String} ufName - * @return {String || undefined} - */ - modules.Parser.prototype.findRelImpied = function(node, ufName) { - var out, - map, - i; - - map = this.getMapping(ufName); - if(map) { - for(var key in map.properties) { - if (map.properties.hasOwnProperty(key)) { - var prop = map.properties[key], - propName = (prop.map) ? prop.map : 'p-' + key, - relCount = 0; - - // is property an alt rel=* mapping - if(prop.relAlt && modules.domUtils.hasAttribute(node, 'rel')) { - i = prop.relAlt.length; - while(i--) { - if(modules.domUtils.hasAttributeValue(node, 'rel', prop.relAlt[i])) { - relCount++; - } - } - if(relCount === prop.relAlt.length) { - out = propName; - } - } - } - } - } - return out; - }; - - - /** - * returns whether a node or its children has rel=* microformat - * - * @param {DOM node} node - * @return {Boolean} - */ - modules.Parser.prototype.hasRel = function(node) { - return (this.countRels(node) > 0); - }; - - - /** - * returns the number of rel=* microformats - * - * @param {DOM node} node - * @return {Int} - */ - modules.Parser.prototype.countRels = function(node) { - if(node){ - return modules.domUtils.getNodesByAttribute(node, 'rel').length; - } - return 0; - }; - - - - } - - - modules.utils = { - - /** - * is the object a string - * - * @param {Object} obj - * @return {Boolean} - */ - isString: function( obj ) { - return typeof( obj ) === 'string'; - }, - - /** - * is the object a number - * - * @param {Object} obj - * @return {Boolean} - */ - isNumber: function( obj ) { - return !isNaN(parseFloat( obj )) && isFinite( obj ); - }, - - - /** - * is the object an array - * - * @param {Object} obj - * @return {Boolean} - */ - isArray: function( obj ) { - return obj && !( obj.propertyIsEnumerable( 'length' ) ) && typeof obj === 'object' && typeof obj.length === 'number'; - }, - - - /** - * is the object a function - * - * @param {Object} obj - * @return {Boolean} - */ - isFunction: function(obj) { - return !!(obj && obj.constructor && obj.call && obj.apply); - }, - - - /** - * does the text start with a test string - * - * @param {String} text - * @param {String} test - * @return {Boolean} - */ - startWith: function( text, test ) { - return(text.indexOf(test) === 0); - }, - - - /** - * removes spaces at front and back of text - * - * @param {String} text - * @return {String} - */ - trim: function( text ) { - if(text && this.isString(text)){ - return (text.trim())? text.trim() : text.replace(/^\s+|\s+$/g, ''); - }else{ - return ''; - } - }, - - - /** - * replaces a character in text - * - * @param {String} text - * @param {Int} index - * @param {String} character - * @return {String} - */ - replaceCharAt: function( text, index, character ) { - if(text && text.length > index){ - return text.substr(0, index) + character + text.substr(index+character.length); - }else{ - return text; - } - }, - - - /** - * removes whitespace, tabs and returns from start and end of text - * - * @param {String} text - * @return {String} - */ - trimWhitespace: function( text ){ - if(text && text.length){ - var i = text.length, - x = 0; - - // turn all whitespace chars at end into spaces - while (i--) { - if(this.isOnlyWhiteSpace(text[i])){ - text = this.replaceCharAt( text, i, ' ' ); - }else{ - break; - } - } - - // turn all whitespace chars at start into spaces - i = text.length; - while (x < i) { - if(this.isOnlyWhiteSpace(text[x])){ - text = this.replaceCharAt( text, i, ' ' ); - }else{ - break; - } - x++; - } - } - return this.trim(text); - }, - - - /** - * does text only contain whitespace characters - * - * @param {String} text - * @return {Boolean} - */ - isOnlyWhiteSpace: function( text ){ - return !(/[^\t\n\r ]/.test( text )); - }, - - - /** - * removes whitespace from text (leaves a single space) - * - * @param {String} text - * @return {Sring} - */ - collapseWhiteSpace: function( text ){ - return text.replace(/[\t\n\r ]+/g, ' '); - }, - - - /** - * does an object have any of its own properties - * - * @param {Object} obj - * @return {Boolean} - */ - hasProperties: function( obj ) { - var key; - for(key in obj) { - if( obj.hasOwnProperty( key ) ) { - return true; - } - } - return false; - }, - - - /** - * a sort function - to sort objects in an array by a given property - * - * @param {String} property - * @param {Boolean} reverse - * @return {Int} - */ - sortObjects: function(property, reverse) { - reverse = (reverse) ? -1 : 1; - return function (a, b) { - a = a[property]; - b = b[property]; - if (a < b) { - return reverse * -1; - } - if (a > b) { - return reverse * 1; - } - return 0; - }; - } - - }; - - - modules.domUtils = { - - // blank objects for DOM - document: null, - rootNode: null, - - - /** - * gets DOMParser object - * + // find base tag to set baseUrl + baseTag = modules.domUtils.querySelector(this.document,'base'); + if(baseTag) { + href = modules.domUtils.getAttribute(baseTag, 'href'); + if(href){ + this.options.baseUrl = href; + } + } + + // get path to rootNode + // then clone document + // then reset the rootNode to its cloned version in a new document + var path, + newDocument, + newRootNode; + + path = modules.domUtils.getNodePath(this.rootNode); + newDocument = modules.domUtils.cloneDocument(this.document); + newRootNode = modules.domUtils.getNodeByPath(newDocument, path); + + // check results as early IE fails + if(newDocument && newRootNode){ + this.document = newDocument; + this.rootNode = newRootNode; + } + + // add includes + if(this.addIncludes){ + this.addIncludes( this.document ); + } + + return (this.rootNode && this.document); + }, + + + /** + * returns an empty structure with errors + * + * @return {Object} + */ + formatError: function(){ + var out = this.formatEmpty(); + out.errors = this.errors; + return out; + }, + + + /** + * returns an empty structure + * + * @return {Object} + */ + formatEmpty: function(){ + return { + 'items': [], + 'rels': {}, + 'rel-urls': {} + }; + }, + + + // find microformats of a given type and return node structures + findFilterNodes: function(rootNode, filters) { + var newRootNode = modules.domUtils.createNode('div'), + items = this.findRootNodes(rootNode, true), + i = 0, + x = 0, + y = 0; + + if(items){ + i = items.length; + while(x < i) { + // add v1 names + y = filters.length; + while (y--) { + if(this.getMapping(filters[y])){ + var v1Name = this.getMapping(filters[y]).root; + filters.push(v1Name); + } + } + // append matching nodes into newRootNode + y = filters.length; + while (y--) { + if(modules.domUtils.hasAttributeValue(items[x], 'class', filters[y])){ + var clone = modules.domUtils.clone(items[x]); + modules.domUtils.appendChild(newRootNode, clone); + break; + } + } + x++; + } + } + + return newRootNode; + }, + + + /** + * appends data to output object for count + * + * @param {string} name + * @param {Int} count + * @param {Object} + */ + appendCount: function(name, count, out){ + if(out[name]){ + out[name] = out[name] + count; + }else{ + out[name] = count; + } + }, + + + /** + * is the microformats type in the filter list + * + * @param {Object} uf + * @param {Array} filters + * @return {Boolean} + */ + shouldInclude: function(uf, filters) { + var i; + + if(modules.utils.isArray(filters) && filters.length > 0) { + i = filters.length; + while(i--) { + if(uf.type[0] === filters[i]) { + return true; + } + } + return false; + } else { + return true; + } + }, + + + /** + * finds all microformat roots in a rootNode + * + * @param {DOM Node} rootNode + * @param {Boolean} includeRoot + * @return {Array} + */ + findRootNodes: function(rootNode, includeRoot) { + var arr = null, + out = [], + classList = [], + items, + x, + i, + y, + key; + + + // build an array of v1 root names + for(key in modules.maps) { + if (modules.maps.hasOwnProperty(key)) { + classList.push(modules.maps[key].root); + } + } + + // get all elements that have a class attribute + includeRoot = (includeRoot) ? includeRoot : false; + if(includeRoot && rootNode.parentNode) { + arr = modules.domUtils.getNodesByAttribute(rootNode.parentNode, 'class'); + } else { + arr = modules.domUtils.getNodesByAttribute(rootNode, 'class'); + } + + // loop elements that have a class attribute + x = 0; + i = arr.length; + while(x < i) { + + items = modules.domUtils.getAttributeList(arr[x], 'class'); + + // loop classes on an element + y = items.length; + while(y--) { + // match v1 root names + if(classList.indexOf(items[y]) > -1) { + out.push(arr[x]); + break; + } + + // match v2 root name prefix + if(modules.utils.startWith(items[y], 'h-')) { + out.push(arr[x]); + break; + } + } + + x++; + } + return out; + }, + + + /** + * starts the tree walk to find microformats + * + * @param {DOM Node} node + * @return {Array} + */ + walkRoot: function(node){ + var context = this, + children = [], + child, + classes, + items = [], + out = []; + + classes = this.getUfClassNames(node); + // if it is a root microformat node + if(classes && classes.root.length > 0){ + items = this.walkTree(node); + + if(items.length > 0){ + out = out.concat(items); + } + }else{ + // check if there are children and one of the children has a root microformat + children = modules.domUtils.getChildren( node ); + if(children && children.length > 0 && this.findRootNodes(node, true).length > -1){ + for (var i = 0; i < children.length; i++) { + child = children[i]; + items = context.walkRoot(child); + if(items.length > 0){ + out = out.concat(items); + } + } + } + } + return out; + }, + + + /** + * starts the tree walking for a single microformat + * + * @param {DOM Node} node + * @return {Array} + */ + walkTree: function(node) { + var classes, + out = [], + obj, + itemRootID; + + // loop roots found on one element + classes = this.getUfClassNames(node); + if(classes && classes.root.length && classes.root.length > 0){ + + this.rootID++; + itemRootID = this.rootID; + obj = this.createUfObject(classes.root, classes.typeVersion); + + this.walkChildren(node, obj, classes.root, itemRootID, classes); + if(this.impliedRules){ + this.impliedRules(node, obj, classes); + } + out.push( this.cleanUfObject(obj) ); + + + } + return out; + }, + + + /** + * finds child properties of microformat + * + * @param {DOM Node} node + * @param {Object} out + * @param {String} ufName + * @param {Int} rootID + * @param {Object} parentClasses + */ + walkChildren: function(node, out, ufName, rootID, parentClasses) { + var context = this, + children = [], + rootItem, + itemRootID, + value, + propertyName, + propertyVersion, + i, + x, + y, + z, + child; + + children = modules.domUtils.getChildren( node ); + + y = 0; + z = children.length; + while(y < z) { + child = children[y]; + + // get microformat classes for this single element + var classes = context.getUfClassNames(child, ufName); + + // a property which is a microformat + if(classes.root.length > 0 && classes.properties.length > 0 && !child.addedAsRoot) { + // create object with type, property and value + rootItem = context.createUfObject( + classes.root, + classes.typeVersion, + modules.text.parse(this.document, child, context.options.textFormat) + ); + + // add the microformat as an array of properties + propertyName = context.removePropPrefix(classes.properties[0][0]); + + // modifies value with "implied value rule" + if(parentClasses && parentClasses.root.length === 1 && parentClasses.properties.length === 1){ + if(context.impliedValueRule){ + out = context.impliedValueRule(out, parentClasses.properties[0][0], classes.properties[0][0], value); + } + } + + if(out.properties[propertyName]) { + out.properties[propertyName].push(rootItem); + } else { + out.properties[propertyName] = [rootItem]; + } + + context.rootID++; + // used to stop duplication in heavily nested structures + child.addedAsRoot = true; + + + x = 0; + i = rootItem.type.length; + itemRootID = context.rootID; + while(x < i) { + context.walkChildren(child, rootItem, rootItem.type, itemRootID, classes); + x++; + } + if(this.impliedRules){ + context.impliedRules(child, rootItem, classes); + } + this.cleanUfObject(rootItem); + + } + + // a property which is NOT a microformat and has not been used for a given root element + if(classes.root.length === 0 && classes.properties.length > 0) { + + x = 0; + i = classes.properties.length; + while(x < i) { + + value = context.getValue(child, classes.properties[x][0], out); + propertyName = context.removePropPrefix(classes.properties[x][0]); + propertyVersion = classes.properties[x][1]; + + // modifies value with "implied value rule" + if(parentClasses && parentClasses.root.length === 1 && parentClasses.properties.length === 1){ + if(context.impliedValueRule){ + out = context.impliedValueRule(out, parentClasses.properties[0][0], classes.properties[x][0], value); + } + } + + // if we have not added this value into a property with the same name already + if(!context.hasRootID(child, rootID, propertyName)) { + // check the root and property is the same version or if overlapping versions are allowed + if( context.isAllowedPropertyVersion( out.typeVersion, propertyVersion ) ){ + // add the property as an array of properties + if(out.properties[propertyName]) { + out.properties[propertyName].push(value); + } else { + out.properties[propertyName] = [value]; + } + // add rootid to node so we can track its use + context.appendRootID(child, rootID, propertyName); + } + } + + x++; + } + + context.walkChildren(child, out, ufName, rootID, classes); + } + + // if the node has no microformat classes, see if its children have + if(classes.root.length === 0 && classes.properties.length === 0) { + context.walkChildren(child, out, ufName, rootID, classes); + } + + // if the node is a child root add it to the children tree + if(classes.root.length > 0 && classes.properties.length === 0) { + + // create object with type, property and value + rootItem = context.createUfObject( + classes.root, + classes.typeVersion, + modules.text.parse(this.document, child, context.options.textFormat) + ); + + // add the microformat as an array of properties + if(!out.children){ + out.children = []; + } + + if(!context.hasRootID(child, rootID, 'child-root')) { + out.children.push( rootItem ); + context.appendRootID(child, rootID, 'child-root'); + context.rootID++; + } + + x = 0; + i = rootItem.type.length; + itemRootID = context.rootID; + while(x < i) { + context.walkChildren(child, rootItem, rootItem.type, itemRootID, classes); + x++; + } + if(this.impliedRules){ + context.impliedRules(child, rootItem, classes); + } + context.cleanUfObject( rootItem ); + + } + + + + y++; + } + + }, + + + + + /** + * gets the value of a property from a node + * + * @param {DOM Node} node + * @param {String} className + * @param {Object} uf + * @return {String || Object} + */ + getValue: function(node, className, uf) { + var value = ''; + + if(modules.utils.startWith(className, 'p-')) { + value = this.getPValue(node, true); + } + + if(modules.utils.startWith(className, 'e-')) { + value = this.getEValue(node); + } + + if(modules.utils.startWith(className, 'u-')) { + value = this.getUValue(node, true); + } + + if(modules.utils.startWith(className, 'dt-')) { + value = this.getDTValue(node, className, uf, true); + } + return value; + }, + + + /** + * gets the value of a node which contains a 'p-' property + * + * @param {DOM Node} node + * @param {Boolean} valueParse + * @return {String} + */ + getPValue: function(node, valueParse) { + var out = ''; + if(valueParse) { + out = this.getValueClass(node, 'p'); + } + + if(!out && valueParse) { + out = this.getValueTitle(node); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['data','input'], 'value'); + } + + if(node.name === 'br' || node.name === 'hr') { + out = ''; + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['img', 'area'], 'alt'); + } + + if(!out) { + out = modules.text.parse(this.document, node, this.options.textFormat); + } + + return(out) ? out : ''; + }, + + + /** + * gets the value of a node which contains the 'e-' property + * + * @param {DOM Node} node + * @return {Object} + */ + getEValue: function(node) { + + var out = {value: '', html: ''}; + + this.expandURLs(node, 'src', this.options.baseUrl); + this.expandURLs(node, 'href', this.options.baseUrl); + + out.value = modules.text.parse(this.document, node, this.options.textFormat); + out.html = modules.html.parse(node); + + return out; + }, + + + /** + * gets the value of a node which contains the 'u-' property + * + * @param {DOM Node} node + * @param {Boolean} valueParse + * @return {String} + */ + getUValue: function(node, valueParse) { + var out = ''; + if(valueParse) { + out = this.getValueClass(node, 'u'); + } + + if(!out && valueParse) { + out = this.getValueTitle(node); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['a', 'area'], 'href'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['img','audio','video','source'], 'src'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['object'], 'data'); + } + + // if we have no protocol separator, turn relative url to absolute url + if(out && out !== '' && out.indexOf('://') === -1) { + out = modules.url.resolve(out, this.options.baseUrl); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['data','input'], 'value'); + } + + if(!out) { + out = modules.text.parse(this.document, node, this.options.textFormat); + } + + return(out) ? out : ''; + }, + + + /** + * gets the value of a node which contains the 'dt-' property + * + * @param {DOM Node} node + * @param {String} className + * @param {Object} uf + * @param {Boolean} valueParse + * @return {String} + */ + getDTValue: function(node, className, uf, valueParse) { + var out = ''; + + if(valueParse) { + out = this.getValueClass(node, 'dt'); + } + + if(!out && valueParse) { + out = this.getValueTitle(node); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['time', 'ins', 'del'], 'datetime'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title'); + } + + if(!out) { + out = modules.domUtils.getAttrValFromTagList(node, ['data', 'input'], 'value'); + } + + if(!out) { + out = modules.text.parse(this.document, node, this.options.textFormat); + } + + if(out) { + if(modules.dates.isDuration(out)) { + // just duration + return out; + } else if(modules.dates.isTime(out)) { + // just time or time+timezone + if(uf) { + uf.times.push([className, modules.dates.parseAmPmTime(out, this.options.dateFormat)]); + } + return modules.dates.parseAmPmTime(out, this.options.dateFormat); + } else { + // returns a date - microformat profile + if(uf) { + uf.dates.push([className, new modules.ISODate(out).toString( this.options.dateFormat )]); + } + return new modules.ISODate(out).toString( this.options.dateFormat ); + } + } else { + return ''; + } + }, + + + /** + * appends a new rootid to a given node + * + * @param {DOM Node} node + * @param {String} id + * @param {String} propertyName + */ + appendRootID: function(node, id, propertyName) { + if(this.hasRootID(node, id, propertyName) === false){ + var rootids = []; + if(modules.domUtils.hasAttribute(node,'rootids')){ + rootids = modules.domUtils.getAttributeList(node,'rootids'); + } + rootids.push('id' + id + '-' + propertyName); + modules.domUtils.setAttribute(node, 'rootids', rootids.join(' ')); + } + }, + + + /** + * does a given node already have a rootid + * + * @param {DOM Node} node + * @param {String} id + * @param {String} propertyName + * @return {Boolean} + */ + hasRootID: function(node, id, propertyName) { + var rootids = []; + if(!modules.domUtils.hasAttribute(node,'rootids')){ + return false; + } else { + rootids = modules.domUtils.getAttributeList(node, 'rootids'); + return (rootids.indexOf('id' + id + '-' + propertyName) > -1); + } + }, + + + + /** + * gets the text of any child nodes with a class value + * + * @param {DOM Node} node + * @param {String} propertyName + * @return {String || null} + */ + getValueClass: function(node, propertyType) { + var context = this, + children = [], + out = [], + child, + x, + i; + + children = modules.domUtils.getChildren( node ); + + x = 0; + i = children.length; + while(x < i) { + child = children[x]; + var value = null; + if(modules.domUtils.hasAttributeValue(child, 'class', 'value')) { + switch(propertyType) { + case 'p': + value = context.getPValue(child, false); + break; + case 'u': + value = context.getUValue(child, false); + break; + case 'dt': + value = context.getDTValue(child, '', null, false); + break; + } + if(value) { + out.push(modules.utils.trim(value)); + } + } + x++; + } + if(out.length > 0) { + if(propertyType === 'p') { + return modules.text.parseText( this.document, out.join(' '), this.options.textFormat); + } + if(propertyType === 'u') { + return out.join(''); + } + if(propertyType === 'dt') { + return modules.dates.concatFragments(out,this.options.dateFormat).toString(this.options.dateFormat); + } + } else { + return null; + } + }, + + + /** + * returns a single string of the 'title' attr from all + * the child nodes with the class 'value-title' + * + * @param {DOM Node} node + * @return {String} + */ + getValueTitle: function(node) { + var out = [], + items, + i, + x; + + items = modules.domUtils.getNodesByAttributeValue(node, 'class', 'value-title'); + x = 0; + i = items.length; + while(x < i) { + if(modules.domUtils.hasAttribute(items[x], 'title')) { + out.push(modules.domUtils.getAttribute(items[x], 'title')); + } + x++; + } + return out.join(''); + }, + + + /** + * finds out whether a node has h-* class v1 and v2 + * + * @param {DOM Node} node + * @return {Boolean} + */ + hasHClass: function(node){ + var classes = this.getUfClassNames(node); + if(classes.root && classes.root.length > 0){ + return true; + }else{ + return false; + } + }, + + + /** + * get both the root and property class names from a node + * + * @param {DOM Node} node + * @param {Array} ufNameArr + * @return {Object} + */ + getUfClassNames: function(node, ufNameArr) { + var context = this, + out = { + 'root': [], + 'properties': [] + }, + classNames, + key, + items, + item, + i, + x, + z, + y, + map, + prop, + propName, + v2Name, + impiedRel, + ufName; + + // don't get classes from excluded list of tags + if(modules.domUtils.hasTagName(node, this.excludeTags) === false){ + + // find classes for node + classNames = modules.domUtils.getAttribute(node, 'class'); + if(classNames) { + items = classNames.split(' '); + x = 0; + i = items.length; + while(x < i) { + + item = modules.utils.trim(items[x]); + + // test for root prefix - v2 + if(modules.utils.startWith(item, context.rootPrefix)) { + if(out.root.indexOf(item) === -1){ + out.root.push(item); + } + out.typeVersion = 'v2'; + } + + // test for property prefix - v2 + z = context.propertyPrefixes.length; + while(z--) { + if(modules.utils.startWith(item, context.propertyPrefixes[z])) { + out.properties.push([item,'v2']); + } + } + + // test for mapped root classnames v1 + for(key in modules.maps) { + if(modules.maps.hasOwnProperty(key)) { + // only add a root once + if(modules.maps[key].root === item && out.root.indexOf(key) === -1) { + // if root map has subTree set to true + // test to see if we should create a property or root + if(modules.maps[key].subTree) { + out.properties.push(['p-' + modules.maps[key].root, 'v1']); + } else { + out.root.push(key); + if(!out.typeVersion){ + out.typeVersion = 'v1'; + } + } + } + } + } + + + // test for mapped property classnames v1 + if(ufNameArr){ + for (var a = 0; a < ufNameArr.length; a++) { + ufName = ufNameArr[a]; + // get mapped property v1 microformat + map = context.getMapping(ufName); + if(map) { + for(key in map.properties) { + if (map.properties.hasOwnProperty(key)) { + + prop = map.properties[key]; + propName = (prop.map) ? prop.map : 'p-' + key; + + if(key === item) { + if(prop.uf) { + // loop all the classList make sure + // 1. this property is a root + // 2. that there is not already an equivalent v2 property i.e. url and u-url on the same element + y = 0; + while(y < i) { + v2Name = context.getV2RootName(items[y]); + // add new root + if(prop.uf.indexOf(v2Name) > -1 && out.root.indexOf(v2Name) === -1) { + out.root.push(v2Name); + out.typeVersion = 'v1'; + } + y++; + } + //only add property once + if(out.properties.indexOf(propName) === -1) { + out.properties.push([propName,'v1']); + } + } else { + if(out.properties.indexOf(propName) === -1) { + out.properties.push([propName,'v1']); + } + } + } + } + + } + } + } + + } + + x++; + + } + } + } + + + // finds any alt rel=* mappings for a given node/microformat + if(ufNameArr && this.findRelImpied){ + for (var b = 0; b < ufNameArr.length; b++) { + ufName = ufNameArr[b]; + impiedRel = this.findRelImpied(node, ufName); + if(impiedRel && out.properties.indexOf(impiedRel) === -1) { + out.properties.push([impiedRel, 'v1']); + } + } + } + + + //if(out.root.length === 1 && out.properties.length === 1) { + // if(out.root[0].replace('h-','') === this.removePropPrefix(out.properties[0][0])) { + // out.typeVersion = 'v2'; + // } + //} + + return out; + }, + + + /** + * given a v1 or v2 root name, return mapping object + * + * @param {String} name + * @return {Object || null} + */ + getMapping: function(name) { + var key; + for(key in modules.maps) { + if(modules.maps[key].root === name || key === name) { + return modules.maps[key]; + } + } + return null; + }, + + + /** + * given a v1 root name returns a v2 root name i.e. vcard >>> h-card + * + * @param {String} name + * @return {String || null} + */ + getV2RootName: function(name) { + var key; + for(key in modules.maps) { + if(modules.maps[key].root === name) { + return key; + } + } + return null; + }, + + + /** + * whether a property is the right microformats version for its root type + * + * @param {String} typeVersion + * @param {String} propertyVersion + * @return {Boolean} + */ + isAllowedPropertyVersion: function(typeVersion, propertyVersion){ + if(this.options.overlappingVersions === true){ + return true; + }else{ + return (typeVersion === propertyVersion); + } + }, + + + /** + * creates a blank microformats object + * + * @param {String} name + * @param {String} value + * @return {Object} + */ + createUfObject: function(names, typeVersion, value) { + var out = {}; + + // is more than just whitespace + if(value && modules.utils.isOnlyWhiteSpace(value) === false) { + out.value = value; + } + // add type i.e. ["h-card", "h-org"] + if(modules.utils.isArray(names)) { + out.type = names; + } else { + out.type = [names]; + } + out.properties = {}; + // metadata properties for parsing + out.typeVersion = typeVersion; + out.times = []; + out.dates = []; + out.altValue = null; + + return out; + }, + + + /** + * removes unwanted microformats property before output + * + * @param {Object} microformat + */ + cleanUfObject: function( microformat ) { + delete microformat.times; + delete microformat.dates; + delete microformat.typeVersion; + delete microformat.altValue; + return microformat; + }, + + + + /** + * removes microformat property prefixes from text + * + * @param {String} text + * @return {String} + */ + removePropPrefix: function(text) { + var i; + + i = this.propertyPrefixes.length; + while(i--) { + var prefix = this.propertyPrefixes[i]; + if(modules.utils.startWith(text, prefix)) { + text = text.substr(prefix.length); + } + } + return text; + }, + + + /** + * expands all relative URLs to absolute ones where it can + * + * @param {DOM Node} node + * @param {String} attrName + * @param {String} baseUrl + */ + expandURLs: function(node, attrName, baseUrl){ + var i, + nodes, + attr; + + nodes = modules.domUtils.getNodesByAttribute(node, attrName); + i = nodes.length; + while (i--) { + try{ + // the url parser can blow up if the format is not right + attr = modules.domUtils.getAttribute(nodes[i], attrName); + if(attr && attr !== '' && baseUrl !== '' && attr.indexOf('://') === -1) { + //attr = urlParser.resolve(baseUrl, attr); + attr = modules.url.resolve(attr, baseUrl); + modules.domUtils.setAttribute(nodes[i], attrName, attr); + } + }catch(err){ + // do nothing - convert only the urls we can, leave the rest as they are + } + } + }, + + + + /** + * merges passed and default options -single level clone of properties + * + * @param {Object} options + */ + mergeOptions: function(options) { + var key; + for(key in options) { + if(options.hasOwnProperty(key)) { + this.options[key] = options[key]; + } + } + }, + + + /** + * removes all rootid attributes + * + * @param {DOM Node} rootNode + */ + removeRootIds: function(rootNode){ + var arr, + i; + + arr = modules.domUtils.getNodesByAttribute(rootNode, 'rootids'); + i = arr.length; + while(i--) { + modules.domUtils.removeAttribute(arr[i],'rootids'); + } + }, + + + /** + * removes all changes made to the DOM + * + * @param {DOM Node} rootNode + */ + clearUpDom: function(rootNode){ + if(this.removeIncludes){ + this.removeIncludes(rootNode); + } + this.removeRootIds(rootNode); + } + + + }; + + + modules.Parser.prototype.constructor = modules.Parser; + + + // check parser module is loaded + if(modules.Parser){ + + /** + * applies "implied rules" microformat output structure i.e. feed-title, name, photo, url and date + * + * @param {DOM Node} node + * @param {Object} uf (microformat output structure) + * @param {Object} parentClasses (classes structure) + * @param {Boolean} impliedPropertiesByVersion + * @return {Object} + */ + modules.Parser.prototype.impliedRules = function(node, uf, parentClasses) { + var typeVersion = (uf.typeVersion)? uf.typeVersion: 'v2'; + + // TEMP: override to allow v1 implied properties while spec changes + if(this.options.impliedPropertiesByVersion === false){ + typeVersion = 'v2'; + } + + if(node && uf && uf.properties) { + uf = this.impliedBackwardComp( node, uf, parentClasses ); + if(typeVersion === 'v2'){ + uf = this.impliedhFeedTitle( uf ); + uf = this.impliedName( node, uf ); + uf = this.impliedPhoto( node, uf ); + uf = this.impliedUrl( node, uf ); + } + uf = this.impliedValue( node, uf, parentClasses ); + uf = this.impliedDate( uf ); + + // TEMP: flagged while spec changes are put forward + if(this.options.parseLatLonGeo === true){ + uf = this.impliedGeo( uf ); + } + } + + return uf; + }; + + + /** + * apply implied name rule + * + * @param {DOM Node} node + * @param {Object} uf + * @return {Object} + */ + modules.Parser.prototype.impliedName = function(node, uf) { + // implied name rule + /* + img.h-x[alt] Glenn Jones + area.h-x[alt] Glenn Jones + abbr.h-x[title] + + .h-x>img:only-child[alt]:not[.h-*]
+ .h-x>area:only-child[alt]:not[.h-*]
Glenn Jones
+ .h-x>abbr:only-child[title]
GJ
+ + .h-x>:only-child>img:only-child[alt]:not[.h-*]
Jane Doe
+ .h-x>:only-child>area:only-child[alt]:not[.h-*]
Jane Doe
+ .h-x>:only-child>abbr:only-child[title]
JD
+ */ + var name, + value; + + if(!uf.properties.name) { + value = this.getImpliedProperty(node, ['img', 'area', 'abbr'], this.getNameAttr); + var textFormat = this.options.textFormat; + // if no value for tags/properties use text + if(!value) { + name = [modules.text.parse(this.document, node, textFormat)]; + }else{ + name = [modules.text.parseText(this.document, value, textFormat)]; + } + if(name && name[0] !== ''){ + uf.properties.name = name; + } + } + + return uf; + }; + + + /** + * apply implied photo rule + * + * @param {DOM Node} node + * @param {Object} uf + * @return {Object} + */ + modules.Parser.prototype.impliedPhoto = function(node, uf) { + // implied photo rule + /* + img.h-x[src] Jane Doe + object.h-x[data] Jane Doe + .h-x>img[src]:only-of-type:not[.h-*]
Jane Doe
+ .h-x>object[data]:only-of-type:not[.h-*]
Jane Doe
+ .h-x>:only-child>img[src]:only-of-type:not[.h-*]
Jane Doe
+ .h-x>:only-child>object[data]:only-of-type:not[.h-*]
Jane Doe
+ */ + var value; + if(!uf.properties.photo) { + value = this.getImpliedProperty(node, ['img', 'object'], this.getPhotoAttr); + if(value) { + // relative to absolute URL + if(value && value !== '' && this.options.baseUrl !== '' && value.indexOf('://') === -1) { + value = modules.url.resolve(value, this.options.baseUrl); + } + uf.properties.photo = [modules.utils.trim(value)]; + } + } + return uf; + }; + + + /** + * apply implied URL rule + * + * @param {DOM Node} node + * @param {Object} uf + * @return {Object} + */ + modules.Parser.prototype.impliedUrl = function(node, uf) { + // implied URL rule + /* + a.h-x[href] Glenn + area.h-x[href] Glenn + .h-x>a[href]:only-of-type:not[.h-*] + .h-x>area[href]:only-of-type:not[.h-*]
Glenn

...

+ */ + var value; + if(!uf.properties.url) { + value = this.getImpliedProperty(node, ['a', 'area'], this.getURLAttr); + if(value) { + // relative to absolute URL + if(value && value !== '' && this.options.baseUrl !== '' && value.indexOf('://') === -1) { + value = modules.url.resolve(value, this.options.baseUrl); + } + uf.properties.url = [modules.utils.trim(value)]; + } + } + return uf; + }; + + + /** + * apply implied date rule - if there is a time only property try to concat it with any date property + * + * @param {DOM Node} node + * @param {Object} uf + * @return {Object} + */ + modules.Parser.prototype.impliedDate = function(uf) { + // implied date rule + // http://microformats.org/wiki/value-class-pattern#microformats2_parsers + // http://microformats.org/wiki/microformats2-parsing-issues#implied_date_for_dt_properties_both_mf2_and_backcompat + var newDate; + if(uf.times.length > 0 && uf.dates.length > 0) { + newDate = modules.dates.dateTimeUnion(uf.dates[0][1], uf.times[0][1], this.options.dateFormat); + uf.properties[this.removePropPrefix(uf.times[0][0])][0] = newDate.toString(this.options.dateFormat); + } + // clean-up object + delete uf.times; + delete uf.dates; + return uf; + }; + + + /** + * get an implied property value from pre-defined tag/attriubte combinations + * + * @param {DOM Node} node + * @param {String} tagList (Array of tags from which an implied value can be pulled) + * @param {String} getAttrFunction (Function which can extract implied value) + * @return {String || null} + */ + modules.Parser.prototype.getImpliedProperty = function(node, tagList, getAttrFunction) { + // i.e. img.h-card + var value = getAttrFunction(node), + descendant, + child; + + if(!value) { + // i.e. .h-card>img:only-of-type:not(.h-card) + descendant = modules.domUtils.getSingleDescendantOfType( node, tagList); + if(descendant && this.hasHClass(descendant) === false){ + value = getAttrFunction(descendant); + } + if(node.children.length > 0 ){ + // i.e. .h-card>:only-child>img:only-of-type:not(.h-card) + child = modules.domUtils.getSingleDescendant(node); + if(child && this.hasHClass(child) === false){ + descendant = modules.domUtils.getSingleDescendantOfType(child, tagList); + if(descendant && this.hasHClass(descendant) === false){ + value = getAttrFunction(descendant); + } + } + } + } + + return value; + }; + + + /** + * get an implied name value from a node + * + * @param {DOM Node} node + * @return {String || null} + */ + modules.Parser.prototype.getNameAttr = function(node) { + var value = modules.domUtils.getAttrValFromTagList(node, ['img','area'], 'alt'); + if(!value) { + value = modules.domUtils.getAttrValFromTagList(node, ['abbr'], 'title'); + } + return value; + }; + + + /** + * get an implied photo value from a node + * + * @param {DOM Node} node + * @return {String || null} + */ + modules.Parser.prototype.getPhotoAttr = function(node) { + var value = modules.domUtils.getAttrValFromTagList(node, ['img'], 'src'); + if(!value && modules.domUtils.hasAttributeValue(node, 'class', 'include') === false) { + value = modules.domUtils.getAttrValFromTagList(node, ['object'], 'data'); + } + return value; + }; + + + /** + * get an implied photo value from a node + * + * @param {DOM Node} node + * @return {String || null} + */ + modules.Parser.prototype.getURLAttr = function(node) { + var value = null; + if(modules.domUtils.hasAttributeValue(node, 'class', 'include') === false){ + + value = modules.domUtils.getAttrValFromTagList(node, ['a'], 'href'); + if(!value) { + value = modules.domUtils.getAttrValFromTagList(node, ['area'], 'href'); + } + + } + return value; + }; + + + /** + * + * + * @param {DOM Node} node + * @param {Object} uf + * @return {Object} + */ + modules.Parser.prototype.impliedValue = function(node, uf, parentClasses){ + + // intersection of implied name and implied value rules + if(uf.properties.name) { + if(uf.value && parentClasses.root.length > 0 && parentClasses.properties.length === 1){ + uf = this.getAltValue(uf, parentClasses.properties[0][0], 'p-name', uf.properties.name[0]); + } + } + + // intersection of implied URL and implied value rules + if(uf.properties.url) { + if(parentClasses && parentClasses.root.length === 1 && parentClasses.properties.length === 1){ + uf = this.getAltValue(uf, parentClasses.properties[0][0], 'u-url', uf.properties.url[0]); + } + } + + // apply alt value + if(uf.altValue !== null){ + uf.value = uf.altValue.value; + } + delete uf.altValue; + + + return uf; + }; + + + /** + * get alt value based on rules about parent property prefix + * + * @param {Object} uf + * @param {String} parentPropertyName + * @param {String} propertyName + * @param {String} value + * @return {Object} + */ + modules.Parser.prototype.getAltValue = function(uf, parentPropertyName, propertyName, value){ + if(uf.value && !uf.altValue){ + // first p-name of the h-* child + if(modules.utils.startWith(parentPropertyName,'p-') && propertyName === 'p-name'){ + uf.altValue = {name: propertyName, value: value}; + } + // if it's an e-* property element + if(modules.utils.startWith(parentPropertyName,'e-') && modules.utils.startWith(propertyName,'e-')){ + uf.altValue = {name: propertyName, value: value}; + } + // if it's an u-* property element + if(modules.utils.startWith(parentPropertyName,'u-') && propertyName === 'u-url'){ + uf.altValue = {name: propertyName, value: value}; + } + } + return uf; + }; + + + /** + * if a h-feed does not have a title use the title tag of a page + * + * @param {Object} uf + * @return {Object} + */ + modules.Parser.prototype.impliedhFeedTitle = function( uf ){ + if(uf.type && uf.type.indexOf('h-feed') > -1){ + // has no name property + if(uf.properties.name === undefined || uf.properties.name[0] === '' ){ + // use the text from the title tag + var title = modules.domUtils.querySelector(this.document, 'title'); + if(title){ + uf.properties.name = [modules.domUtils.textContent(title)]; + } + } + } + return uf; + }; + + + + /** + * implied Geo from pattern + * + * @param {Object} uf + * @return {Object} + */ + modules.Parser.prototype.impliedGeo = function( uf ){ + var geoPair, + parts, + longitude, + latitude, + valid = true; + + if(uf.type && uf.type.indexOf('h-geo') > -1){ + + // has no latitude or longitude property + if(uf.properties.latitude === undefined || uf.properties.longitude === undefined ){ + + geoPair = (uf.properties.name)? uf.properties.name[0] : null; + geoPair = (!geoPair && uf.properties.value)? uf.properties.value : geoPair; + + if(geoPair){ + // allow for the use of a ';' as in microformats and also ',' as in Geo URL + geoPair = geoPair.replace(';',','); + + // has sep char + if(geoPair.indexOf(',') > -1 ){ + parts = geoPair.split(','); + + // only correct if we have two or more parts + if(parts.length > 1){ + + // latitude no value outside the range -90 or 90 + latitude = parseFloat( parts[0] ); + if(modules.utils.isNumber(latitude) && latitude > 90 || latitude < -90){ + valid = false; + } + + // longitude no value outside the range -180 to 180 + longitude = parseFloat( parts[1] ); + if(modules.utils.isNumber(longitude) && longitude > 180 || longitude < -180){ + valid = false; + } + + if(valid){ + uf.properties.latitude = [latitude]; + uf.properties.longitude = [longitude]; + } + } + + } + } + } + } + return uf; + }; + + + /** + * if a backwards compat built structure has no properties add name through this.impliedName + * + * @param {Object} uf + * @return {Object} + */ + modules.Parser.prototype.impliedBackwardComp = function(node, uf, parentClasses){ + + // look for pattern in parent classes like "p-geo h-geo" + // these are structures built from backwards compat parsing of geo + if(parentClasses.root.length === 1 && parentClasses.properties.length === 1) { + if(parentClasses.root[0].replace('h-','') === this.removePropPrefix(parentClasses.properties[0][0])) { + + // if microformat has no properties apply the impliedName rule to get value from containing node + // this will get value from html such as Brighton + if( modules.utils.hasProperties(uf.properties) === false ){ + uf = this.impliedName( node, uf ); + } + } + } + + return uf; + }; + + + + } + + + // check parser module is loaded + if(modules.Parser){ + + + /** + * appends clones of include Nodes into the DOM structure + * + * @param {DOM node} rootNode + */ + modules.Parser.prototype.addIncludes = function(rootNode) { + this.addAttributeIncludes(rootNode, 'itemref'); + this.addAttributeIncludes(rootNode, 'headers'); + this.addClassIncludes(rootNode); + }; + + + /** + * appends clones of include Nodes into the DOM structure for attribute based includes + * + * @param {DOM node} rootNode + * @param {String} attributeName + */ + modules.Parser.prototype.addAttributeIncludes = function(rootNode, attributeName) { + var arr, + idList, + i, + x, + z, + y; + + arr = modules.domUtils.getNodesByAttribute(rootNode, attributeName); + x = 0; + i = arr.length; + while(x < i) { + idList = modules.domUtils.getAttributeList(arr[x], attributeName); + if(idList) { + z = 0; + y = idList.length; + while(z < y) { + this.apppendInclude(arr[x], idList[z]); + z++; + } + } + x++; + } + }; + + + /** + * appends clones of include Nodes into the DOM structure for class based includes + * + * @param {DOM node} rootNode + */ + modules.Parser.prototype.addClassIncludes = function(rootNode) { + var id, + arr, + x = 0, + i; + + arr = modules.domUtils.getNodesByAttributeValue(rootNode, 'class', 'include'); + i = arr.length; + while(x < i) { + id = modules.domUtils.getAttrValFromTagList(arr[x], ['a'], 'href'); + if(!id) { + id = modules.domUtils.getAttrValFromTagList(arr[x], ['object'], 'data'); + } + this.apppendInclude(arr[x], id); + x++; + } + }; + + + /** + * appends a clone of an include into another Node using Id + * + * @param {DOM node} rootNode + * @param {Stringe} id + */ + modules.Parser.prototype.apppendInclude = function(node, id){ + var include, + clone; + + id = modules.utils.trim(id.replace('#', '')); + include = modules.domUtils.getElementById(this.document, id); + if(include) { + clone = modules.domUtils.clone(include); + this.markIncludeChildren(clone); + modules.domUtils.appendChild(node, clone); + } + }; + + + /** + * adds an attribute marker to all the child microformat roots + * + * @param {DOM node} rootNode + */ + modules.Parser.prototype.markIncludeChildren = function(rootNode) { + var arr, + x, + i; + + // loop the array and add the attribute + arr = this.findRootNodes(rootNode); + x = 0; + i = arr.length; + modules.domUtils.setAttribute(rootNode, 'data-include', 'true'); + modules.domUtils.setAttribute(rootNode, 'style', 'display:none'); + while(x < i) { + modules.domUtils.setAttribute(arr[x], 'data-include', 'true'); + x++; + } + }; + + + /** + * removes all appended include clones from DOM + * + * @param {DOM node} rootNode + */ + modules.Parser.prototype.removeIncludes = function(rootNode){ + var arr, + i; + + // remove all the items that were added as includes + arr = modules.domUtils.getNodesByAttribute(rootNode, 'data-include'); + i = arr.length; + while(i--) { + modules.domUtils.removeChild(rootNode,arr[i]); + } + }; + + + } + + + // check parser module is loaded + if(modules.Parser){ + + /** + * finds rel=* structures + * + * @param {DOM node} rootNode + * @return {Object} + */ + modules.Parser.prototype.findRels = function(rootNode) { + var out = { + 'items': [], + 'rels': {}, + 'rel-urls': {} + }, + x, + i, + y, + z, + relList, + items, + item, + value, + arr; + + arr = modules.domUtils.getNodesByAttribute(rootNode, 'rel'); + x = 0; + i = arr.length; + while(x < i) { + relList = modules.domUtils.getAttribute(arr[x], 'rel'); + + if(relList) { + items = relList.split(' '); + + + // add rels + z = 0; + y = items.length; + while(z < y) { + item = modules.utils.trim(items[z]); + + // get rel value + value = modules.domUtils.getAttrValFromTagList(arr[x], ['a', 'area'], 'href'); + if(!value) { + value = modules.domUtils.getAttrValFromTagList(arr[x], ['link'], 'href'); + } + + // create the key + if(!out.rels[item]) { + out.rels[item] = []; + } + + if(typeof this.options.baseUrl === 'string' && typeof value === 'string') { + + var resolved = modules.url.resolve(value, this.options.baseUrl); + // do not add duplicate rels - based on resolved URLs + if(out.rels[item].indexOf(resolved) === -1){ + out.rels[item].push( resolved ); + } + } + z++; + } + + + var url = null; + if(modules.domUtils.hasAttribute(arr[x], 'href')){ + url = modules.domUtils.getAttribute(arr[x], 'href'); + if(url){ + url = modules.url.resolve(url, this.options.baseUrl ); + } + } + + + // add to rel-urls + var relUrl = this.getRelProperties(arr[x]); + relUrl.rels = items; + // // do not add duplicate rel-urls - based on resolved URLs + if(url && out['rel-urls'][url] === undefined){ + out['rel-urls'][url] = relUrl; + } + + + } + x++; + } + return out; + }; + + + /** + * gets the properties of a rel=* + * + * @param {DOM node} node + * @return {Object} + */ + modules.Parser.prototype.getRelProperties = function(node){ + var obj = {}; + + if(modules.domUtils.hasAttribute(node, 'media')){ + obj.media = modules.domUtils.getAttribute(node, 'media'); + } + if(modules.domUtils.hasAttribute(node, 'type')){ + obj.type = modules.domUtils.getAttribute(node, 'type'); + } + if(modules.domUtils.hasAttribute(node, 'hreflang')){ + obj.hreflang = modules.domUtils.getAttribute(node, 'hreflang'); + } + if(modules.domUtils.hasAttribute(node, 'title')){ + obj.title = modules.domUtils.getAttribute(node, 'title'); + } + if(modules.utils.trim(this.getPValue(node, false)) !== ''){ + obj.text = this.getPValue(node, false); + } + + return obj; + }; + + + /** + * finds any alt rel=* mappings for a given node/microformat + * + * @param {DOM node} node + * @param {String} ufName + * @return {String || undefined} + */ + modules.Parser.prototype.findRelImpied = function(node, ufName) { + var out, + map, + i; + + map = this.getMapping(ufName); + if(map) { + for(var key in map.properties) { + if (map.properties.hasOwnProperty(key)) { + var prop = map.properties[key], + propName = (prop.map) ? prop.map : 'p-' + key, + relCount = 0; + + // is property an alt rel=* mapping + if(prop.relAlt && modules.domUtils.hasAttribute(node, 'rel')) { + i = prop.relAlt.length; + while(i--) { + if(modules.domUtils.hasAttributeValue(node, 'rel', prop.relAlt[i])) { + relCount++; + } + } + if(relCount === prop.relAlt.length) { + out = propName; + } + } + } + } + } + return out; + }; + + + /** + * returns whether a node or its children has rel=* microformat + * + * @param {DOM node} node + * @return {Boolean} + */ + modules.Parser.prototype.hasRel = function(node) { + return (this.countRels(node) > 0); + }; + + + /** + * returns the number of rel=* microformats + * + * @param {DOM node} node + * @return {Int} + */ + modules.Parser.prototype.countRels = function(node) { + if(node){ + return modules.domUtils.getNodesByAttribute(node, 'rel').length; + } + return 0; + }; + + + + } + + + modules.utils = { + + /** + * is the object a string + * + * @param {Object} obj + * @return {Boolean} + */ + isString: function( obj ) { + return typeof( obj ) === 'string'; + }, + + /** + * is the object a number + * + * @param {Object} obj + * @return {Boolean} + */ + isNumber: function( obj ) { + return !isNaN(parseFloat( obj )) && isFinite( obj ); + }, + + + /** + * is the object an array + * + * @param {Object} obj + * @return {Boolean} + */ + isArray: function( obj ) { + return obj && !( obj.propertyIsEnumerable( 'length' ) ) && typeof obj === 'object' && typeof obj.length === 'number'; + }, + + + /** + * is the object a function + * + * @param {Object} obj + * @return {Boolean} + */ + isFunction: function(obj) { + return !!(obj && obj.constructor && obj.call && obj.apply); + }, + + + /** + * does the text start with a test string + * + * @param {String} text + * @param {String} test + * @return {Boolean} + */ + startWith: function( text, test ) { + return(text.indexOf(test) === 0); + }, + + + /** + * removes spaces at front and back of text + * + * @param {String} text + * @return {String} + */ + trim: function( text ) { + if(text && this.isString(text)){ + return (text.trim())? text.trim() : text.replace(/^\s+|\s+$/g, ''); + }else{ + return ''; + } + }, + + + /** + * replaces a character in text + * + * @param {String} text + * @param {Int} index + * @param {String} character + * @return {String} + */ + replaceCharAt: function( text, index, character ) { + if(text && text.length > index){ + return text.substr(0, index) + character + text.substr(index+character.length); + }else{ + return text; + } + }, + + + /** + * removes whitespace, tabs and returns from start and end of text + * + * @param {String} text + * @return {String} + */ + trimWhitespace: function( text ){ + if(text && text.length){ + var i = text.length, + x = 0; + + // turn all whitespace chars at end into spaces + while (i--) { + if(this.isOnlyWhiteSpace(text[i])){ + text = this.replaceCharAt( text, i, ' ' ); + }else{ + break; + } + } + + // turn all whitespace chars at start into spaces + i = text.length; + while (x < i) { + if(this.isOnlyWhiteSpace(text[x])){ + text = this.replaceCharAt( text, i, ' ' ); + }else{ + break; + } + x++; + } + } + return this.trim(text); + }, + + + /** + * does text only contain whitespace characters + * + * @param {String} text + * @return {Boolean} + */ + isOnlyWhiteSpace: function( text ){ + return !(/[^\t\n\r ]/.test( text )); + }, + + + /** + * removes whitespace from text (leaves a single space) + * + * @param {String} text + * @return {Sring} + */ + collapseWhiteSpace: function( text ){ + return text.replace(/[\t\n\r ]+/g, ' '); + }, + + + /** + * does an object have any of its own properties + * + * @param {Object} obj + * @return {Boolean} + */ + hasProperties: function( obj ) { + var key; + for(key in obj) { + if( obj.hasOwnProperty( key ) ) { + return true; + } + } + return false; + }, + + + /** + * a sort function - to sort objects in an array by a given property + * + * @param {String} property + * @param {Boolean} reverse + * @return {Int} + */ + sortObjects: function(property, reverse) { + reverse = (reverse) ? -1 : 1; + return function (a, b) { + a = a[property]; + b = b[property]; + if (a < b) { + return reverse * -1; + } + if (a > b) { + return reverse * 1; + } + return 0; + }; + } + + }; + + + modules.domUtils = { + + // blank objects for DOM + document: null, + rootNode: null, + + + /** + * gets DOMParser object + * * @return {Object || undefined} - */ + */ getDOMParser: function () { if (typeof DOMParser === "undefined") { try { @@ -2423,580 +2423,580 @@ var Microformats; // jshint ignore:line }, - /** - * configures what are the base DOM objects for parsing - * - * @param {Object} options - * @return {DOM Node} node - */ - getDOMContext: function( options ){ + /** + * configures what are the base DOM objects for parsing + * + * @param {Object} options + * @return {DOM Node} node + */ + getDOMContext: function( options ){ - // if a node is passed - if(options.node){ - this.rootNode = options.node; - } + // if a node is passed + if(options.node){ + this.rootNode = options.node; + } - // if a html string is passed - if(options.html){ - //var domParser = new DOMParser(); + // if a html string is passed + if(options.html){ + //var domParser = new DOMParser(); var domParser = this.getDOMParser(); - this.rootNode = domParser.parseFromString( options.html, 'text/html' ); - } + this.rootNode = domParser.parseFromString( options.html, 'text/html' ); + } - // find top level document from rootnode - if(this.rootNode !== null){ - if(this.rootNode.nodeType === 9){ - this.document = this.rootNode; - this.rootNode = modules.domUtils.querySelector(this.rootNode, 'html'); - }else{ - // if it's DOM node get parent DOM Document - this.document = modules.domUtils.ownerDocument(this.rootNode); - } - } + // find top level document from rootnode + if(this.rootNode !== null){ + if(this.rootNode.nodeType === 9){ + this.document = this.rootNode; + this.rootNode = modules.domUtils.querySelector(this.rootNode, 'html'); + }else{ + // if it's DOM node get parent DOM Document + this.document = modules.domUtils.ownerDocument(this.rootNode); + } + } - // use global document object - if(!this.rootNode && document){ - this.rootNode = modules.domUtils.querySelector(document, 'html'); - this.document = document; - } + // use global document object + if(!this.rootNode && document){ + this.rootNode = modules.domUtils.querySelector(document, 'html'); + this.document = document; + } - if(this.rootNode && this.document){ - return {document: this.document, rootNode: this.rootNode}; - } + if(this.rootNode && this.document){ + return {document: this.document, rootNode: this.rootNode}; + } - return {document: null, rootNode: null}; - }, + return {document: null, rootNode: null}; + }, - /** - * gets the first DOM node - * - * @param {Dom Document} - * @return {DOM Node} node - */ - getTopMostNode: function( node ){ - //var doc = this.ownerDocument(node); - //if(doc && doc.nodeType && doc.nodeType === 9 && doc.documentElement){ - // return doc.documentElement; - //} - return node; - }, - - - - /** - * abstracts DOM ownerDocument - * - * @param {DOM Node} node - * @return {Dom Document} - */ - ownerDocument: function(node){ - return node.ownerDocument; - }, - - - /** - * abstracts DOM textContent - * - * @param {DOM Node} node - * @return {String} - */ - textContent: function(node){ - if(node.textContent){ - return node.textContent; - }else if(node.innerText){ - return node.innerText; - } - return ''; - }, - - - /** - * abstracts DOM innerHTML - * - * @param {DOM Node} node - * @return {String} - */ - innerHTML: function(node){ - return node.innerHTML; - }, - - - /** - * abstracts DOM hasAttribute - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {Boolean} - */ - hasAttribute: function(node, attributeName) { - return node.hasAttribute(attributeName); - }, - - - /** - * does an attribute contain a value - * - * @param {DOM Node} node - * @param {String} attributeName - * @param {String} value - * @return {Boolean} - */ - hasAttributeValue: function(node, attributeName, value) { - return (this.getAttributeList(node, attributeName).indexOf(value) > -1); - }, - - - /** - * abstracts DOM getAttribute - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {String || null} - */ - getAttribute: function(node, attributeName) { - return node.getAttribute(attributeName); - }, - - - /** - * abstracts DOM setAttribute - * - * @param {DOM Node} node - * @param {String} attributeName - * @param {String} attributeValue - */ - setAttribute: function(node, attributeName, attributeValue){ - node.setAttribute(attributeName, attributeValue); - }, - - - /** - * abstracts DOM removeAttribute - * - * @param {DOM Node} node - * @param {String} attributeName - */ - removeAttribute: function(node, attributeName) { - node.removeAttribute(attributeName); - }, - - - /** - * abstracts DOM getElementById - * - * @param {DOM Node || DOM Document} node - * @param {String} id - * @return {DOM Node} - */ - getElementById: function(docNode, id) { - return docNode.querySelector( '#' + id ); - }, - - - /** - * abstracts DOM querySelector - * - * @param {DOM Node || DOM Document} node - * @param {String} selector - * @return {DOM Node} - */ - querySelector: function(docNode, selector) { - return docNode.querySelector( selector ); - }, - - - /** - * get value of a Node attribute as an array - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {Array} - */ - getAttributeList: function(node, attributeName) { - var out = [], - attList; - - attList = node.getAttribute(attributeName); - if(attList && attList !== '') { - if(attList.indexOf(' ') > -1) { - out = attList.split(' '); - } else { - out.push(attList); - } - } - return out; - }, - - - /** - * gets all child nodes with a given attribute - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {NodeList} - */ - getNodesByAttribute: function(node, attributeName) { - var selector = '[' + attributeName + ']'; - return node.querySelectorAll(selector); - }, - - - /** - * gets all child nodes with a given attribute containing a given value - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {DOM NodeList} - */ - getNodesByAttributeValue: function(rootNode, name, value) { - var arr = [], - x = 0, - i, - out = []; - - arr = this.getNodesByAttribute(rootNode, name); - if(arr) { - i = arr.length; - while(x < i) { - if(this.hasAttributeValue(arr[x], name, value)) { - out.push(arr[x]); - } - x++; - } - } - return out; - }, - - - /** - * gets attribute value from controlled list of tags - * - * @param {Array} tagNames - * @param {String} attributeName - * @return {String || null} - */ - getAttrValFromTagList: function(node, tagNames, attributeName) { - var i = tagNames.length; - - while(i--) { - if(node.tagName.toLowerCase() === tagNames[i]) { - var attrValue = this.getAttribute(node, attributeName); - if(attrValue && attrValue !== '') { - return attrValue; - } - } - } - return null; - }, - - - /** - * get node if it has no siblings. CSS equivalent is :only-child - * - * @param {DOM Node} rootNode - * @param {Array} tagNames - * @return {DOM Node || null} - */ - getSingleDescendant: function(node){ - return this.getDescendant( node, null, false ); - }, - - /** - * get node if it has no siblings of the same type. CSS equivalent is :only-of-type - * - * @param {DOM Node} rootNode - * @param {Array} tagNames - * @return {DOM Node || null} - */ - getSingleDescendantOfType: function(node, tagNames){ - return this.getDescendant( node, tagNames, true ); - }, - - - /** - * get child node limited by presence of siblings - either CSS :only-of-type or :only-child - * - * @param {DOM Node} rootNode - * @param {Array} tagNames - * @return {DOM Node || null} - */ - getDescendant: function( node, tagNames, onlyOfType ){ - var i = node.children.length, - countAll = 0, - countOfType = 0, - child, - out = null; - - while(i--) { - child = node.children[i]; - if(child.nodeType === 1) { - if(tagNames){ - // count just only-of-type - if(this.hasTagName(child, tagNames)){ - out = child; - countOfType++; - } - }else{ - // count all elements - out = child; - countAll++; - } - } - } - if(onlyOfType === true){ - return (countOfType === 1)? out : null; - }else{ - return (countAll === 1)? out : null; - } - }, - - - /** - * is a node one of a list of tags - * - * @param {DOM Node} rootNode - * @param {Array} tagNames - * @return {Boolean} - */ - hasTagName: function(node, tagNames){ - var i = tagNames.length; - while(i--) { - if(node.tagName.toLowerCase() === tagNames[i]) { - return true; - } - } - return false; - }, - - - /** - * abstracts DOM appendChild - * - * @param {DOM Node} node - * @param {DOM Node} childNode - * @return {DOM Node} - */ - appendChild: function(node, childNode){ - return node.appendChild(childNode); - }, - - - /** - * abstracts DOM removeChild - * - * @param {DOM Node} childNode - * @return {DOM Node || null} - */ - removeChild: function(childNode){ - if (childNode.parentNode) { - return childNode.parentNode.removeChild(childNode); - }else{ - return null; - } - }, - - - /** - * abstracts DOM cloneNode - * - * @param {DOM Node} node - * @return {DOM Node} - */ - clone: function(node) { - var newNode = node.cloneNode(true); - newNode.removeAttribute('id'); - return newNode; - }, - - - /** - * gets the text of a node - * - * @param {DOM Node} node - * @return {String} - */ - getElementText: function( node ){ - if(node && node.data){ - return node.data; - }else{ - return ''; - } - }, - - - /** - * gets the attributes of a node - ordered by sequence in html - * - * @param {DOM Node} node - * @return {Array} - */ - getOrderedAttributes: function( node ){ - var nodeStr = node.outerHTML, - attrs = []; - - for (var i = 0; i < node.attributes.length; i++) { - var attr = node.attributes[i]; - attr.indexNum = nodeStr.indexOf(attr.name); - - attrs.push( attr ); - } - return attrs.sort( modules.utils.sortObjects( 'indexNum' ) ); - }, - - - /** - * decodes html entities in given text - * - * @param {DOM Document} doc - * @param String} text - * @return {String} - */ - decodeEntities: function( doc, text ){ - //return text; - return doc.createTextNode( text ).nodeValue; - }, - - - /** - * clones a DOM document - * - * @param {DOM Document} document - * @return {DOM Document} - */ - cloneDocument: function( document ){ - var newNode, - newDocument = null; - - if( this.canCloneDocument( document )){ - newDocument = document.implementation.createHTMLDocument(''); - newNode = newDocument.importNode( document.documentElement, true ); - newDocument.replaceChild(newNode, newDocument.querySelector('html')); - } - return (newNode && newNode.nodeType && newNode.nodeType === 1)? newDocument : document; - }, - - - /** - * can environment clone a DOM document - * - * @param {DOM Document} document - * @return {Boolean} - */ - canCloneDocument: function( document ){ - return (document && document.importNode && document.implementation && document.implementation.createHTMLDocument); - }, - - - /** - * get the child index of a node. Used to create a node path - * - * @param {DOM Node} node - * @return {Int} - */ - getChildIndex: function (node) { - var parent = node.parentNode, - i = -1, - child; - while (parent && (child = parent.childNodes[++i])){ - if (child === node){ - return i; - } - } - return -1; - }, - - - /** - * get a node's path - * - * @param {DOM Node} node - * @return {Array} - */ - getNodePath: function (node) { - var parent = node.parentNode, - path = [], - index = this.getChildIndex(node); - - if(parent && (path = this.getNodePath(parent))){ - if(index > -1){ - path.push(index); - } - } - return path; - }, - - - /** - * get a node from a path. - * - * @param {DOM document} document - * @param {Array} path - * @return {DOM Node} - */ - getNodeByPath: function (document, path) { - var node = document.documentElement, - i = 0, - index; - while ((index = path[++i]) > -1){ - node = node.childNodes[index]; - } - return node; - }, - - - /** - * get an array/nodeList of child nodes - * - * @param {DOM node} node - * @return {Array} - */ - getChildren: function( node ){ - return node.children; - }, - - - /** - * create a node - * - * @param {String} tagName - * @return {DOM node} - */ - createNode: function( tagName ){ - return this.document.createElement(tagName); - }, - - - /** - * create a node with text content - * - * @param {String} tagName - * @param {String} text - * @return {DOM node} - */ - createNodeWithText: function( tagName, text ){ - var node = this.document.createElement(tagName); - node.innerHTML = text; - return node; - } + * gets the first DOM node + * + * @param {Dom Document} + * @return {DOM Node} node + */ + getTopMostNode: function( node ){ + //var doc = this.ownerDocument(node); + //if(doc && doc.nodeType && doc.nodeType === 9 && doc.documentElement){ + // return doc.documentElement; + //} + return node; + }, - }; + /** + * abstracts DOM ownerDocument + * + * @param {DOM Node} node + * @return {Dom Document} + */ + ownerDocument: function(node){ + return node.ownerDocument; + }, - modules.url = { + /** + * abstracts DOM textContent + * + * @param {DOM Node} node + * @return {String} + */ + textContent: function(node){ + if(node.textContent){ + return node.textContent; + }else if(node.innerText){ + return node.innerText; + } + return ''; + }, - /** - * creates DOM objects needed to resolve URLs - */ + /** + * abstracts DOM innerHTML + * + * @param {DOM Node} node + * @return {String} + */ + innerHTML: function(node){ + return node.innerHTML; + }, + + + /** + * abstracts DOM hasAttribute + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {Boolean} + */ + hasAttribute: function(node, attributeName) { + return node.hasAttribute(attributeName); + }, + + + /** + * does an attribute contain a value + * + * @param {DOM Node} node + * @param {String} attributeName + * @param {String} value + * @return {Boolean} + */ + hasAttributeValue: function(node, attributeName, value) { + return (this.getAttributeList(node, attributeName).indexOf(value) > -1); + }, + + + /** + * abstracts DOM getAttribute + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {String || null} + */ + getAttribute: function(node, attributeName) { + return node.getAttribute(attributeName); + }, + + + /** + * abstracts DOM setAttribute + * + * @param {DOM Node} node + * @param {String} attributeName + * @param {String} attributeValue + */ + setAttribute: function(node, attributeName, attributeValue){ + node.setAttribute(attributeName, attributeValue); + }, + + + /** + * abstracts DOM removeAttribute + * + * @param {DOM Node} node + * @param {String} attributeName + */ + removeAttribute: function(node, attributeName) { + node.removeAttribute(attributeName); + }, + + + /** + * abstracts DOM getElementById + * + * @param {DOM Node || DOM Document} node + * @param {String} id + * @return {DOM Node} + */ + getElementById: function(docNode, id) { + return docNode.querySelector( '#' + id ); + }, + + + /** + * abstracts DOM querySelector + * + * @param {DOM Node || DOM Document} node + * @param {String} selector + * @return {DOM Node} + */ + querySelector: function(docNode, selector) { + return docNode.querySelector( selector ); + }, + + + /** + * get value of a Node attribute as an array + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {Array} + */ + getAttributeList: function(node, attributeName) { + var out = [], + attList; + + attList = node.getAttribute(attributeName); + if(attList && attList !== '') { + if(attList.indexOf(' ') > -1) { + out = attList.split(' '); + } else { + out.push(attList); + } + } + return out; + }, + + + /** + * gets all child nodes with a given attribute + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {NodeList} + */ + getNodesByAttribute: function(node, attributeName) { + var selector = '[' + attributeName + ']'; + return node.querySelectorAll(selector); + }, + + + /** + * gets all child nodes with a given attribute containing a given value + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {DOM NodeList} + */ + getNodesByAttributeValue: function(rootNode, name, value) { + var arr = [], + x = 0, + i, + out = []; + + arr = this.getNodesByAttribute(rootNode, name); + if(arr) { + i = arr.length; + while(x < i) { + if(this.hasAttributeValue(arr[x], name, value)) { + out.push(arr[x]); + } + x++; + } + } + return out; + }, + + + /** + * gets attribute value from controlled list of tags + * + * @param {Array} tagNames + * @param {String} attributeName + * @return {String || null} + */ + getAttrValFromTagList: function(node, tagNames, attributeName) { + var i = tagNames.length; + + while(i--) { + if(node.tagName.toLowerCase() === tagNames[i]) { + var attrValue = this.getAttribute(node, attributeName); + if(attrValue && attrValue !== '') { + return attrValue; + } + } + } + return null; + }, + + + /** + * get node if it has no siblings. CSS equivalent is :only-child + * + * @param {DOM Node} rootNode + * @param {Array} tagNames + * @return {DOM Node || null} + */ + getSingleDescendant: function(node){ + return this.getDescendant( node, null, false ); + }, + + + /** + * get node if it has no siblings of the same type. CSS equivalent is :only-of-type + * + * @param {DOM Node} rootNode + * @param {Array} tagNames + * @return {DOM Node || null} + */ + getSingleDescendantOfType: function(node, tagNames){ + return this.getDescendant( node, tagNames, true ); + }, + + + /** + * get child node limited by presence of siblings - either CSS :only-of-type or :only-child + * + * @param {DOM Node} rootNode + * @param {Array} tagNames + * @return {DOM Node || null} + */ + getDescendant: function( node, tagNames, onlyOfType ){ + var i = node.children.length, + countAll = 0, + countOfType = 0, + child, + out = null; + + while(i--) { + child = node.children[i]; + if(child.nodeType === 1) { + if(tagNames){ + // count just only-of-type + if(this.hasTagName(child, tagNames)){ + out = child; + countOfType++; + } + }else{ + // count all elements + out = child; + countAll++; + } + } + } + if(onlyOfType === true){ + return (countOfType === 1)? out : null; + }else{ + return (countAll === 1)? out : null; + } + }, + + + /** + * is a node one of a list of tags + * + * @param {DOM Node} rootNode + * @param {Array} tagNames + * @return {Boolean} + */ + hasTagName: function(node, tagNames){ + var i = tagNames.length; + while(i--) { + if(node.tagName.toLowerCase() === tagNames[i]) { + return true; + } + } + return false; + }, + + + /** + * abstracts DOM appendChild + * + * @param {DOM Node} node + * @param {DOM Node} childNode + * @return {DOM Node} + */ + appendChild: function(node, childNode){ + return node.appendChild(childNode); + }, + + + /** + * abstracts DOM removeChild + * + * @param {DOM Node} childNode + * @return {DOM Node || null} + */ + removeChild: function(childNode){ + if (childNode.parentNode) { + return childNode.parentNode.removeChild(childNode); + }else{ + return null; + } + }, + + + /** + * abstracts DOM cloneNode + * + * @param {DOM Node} node + * @return {DOM Node} + */ + clone: function(node) { + var newNode = node.cloneNode(true); + newNode.removeAttribute('id'); + return newNode; + }, + + + /** + * gets the text of a node + * + * @param {DOM Node} node + * @return {String} + */ + getElementText: function( node ){ + if(node && node.data){ + return node.data; + }else{ + return ''; + } + }, + + + /** + * gets the attributes of a node - ordered by sequence in html + * + * @param {DOM Node} node + * @return {Array} + */ + getOrderedAttributes: function( node ){ + var nodeStr = node.outerHTML, + attrs = []; + + for (var i = 0; i < node.attributes.length; i++) { + var attr = node.attributes[i]; + attr.indexNum = nodeStr.indexOf(attr.name); + + attrs.push( attr ); + } + return attrs.sort( modules.utils.sortObjects( 'indexNum' ) ); + }, + + + /** + * decodes html entities in given text + * + * @param {DOM Document} doc + * @param String} text + * @return {String} + */ + decodeEntities: function( doc, text ){ + //return text; + return doc.createTextNode( text ).nodeValue; + }, + + + /** + * clones a DOM document + * + * @param {DOM Document} document + * @return {DOM Document} + */ + cloneDocument: function( document ){ + var newNode, + newDocument = null; + + if( this.canCloneDocument( document )){ + newDocument = document.implementation.createHTMLDocument(''); + newNode = newDocument.importNode( document.documentElement, true ); + newDocument.replaceChild(newNode, newDocument.querySelector('html')); + } + return (newNode && newNode.nodeType && newNode.nodeType === 1)? newDocument : document; + }, + + + /** + * can environment clone a DOM document + * + * @param {DOM Document} document + * @return {Boolean} + */ + canCloneDocument: function( document ){ + return (document && document.importNode && document.implementation && document.implementation.createHTMLDocument); + }, + + + /** + * get the child index of a node. Used to create a node path + * + * @param {DOM Node} node + * @return {Int} + */ + getChildIndex: function (node) { + var parent = node.parentNode, + i = -1, + child; + while (parent && (child = parent.childNodes[++i])){ + if (child === node){ + return i; + } + } + return -1; + }, + + + /** + * get a node's path + * + * @param {DOM Node} node + * @return {Array} + */ + getNodePath: function (node) { + var parent = node.parentNode, + path = [], + index = this.getChildIndex(node); + + if(parent && (path = this.getNodePath(parent))){ + if(index > -1){ + path.push(index); + } + } + return path; + }, + + + /** + * get a node from a path. + * + * @param {DOM document} document + * @param {Array} path + * @return {DOM Node} + */ + getNodeByPath: function (document, path) { + var node = document.documentElement, + i = 0, + index; + while ((index = path[++i]) > -1){ + node = node.childNodes[index]; + } + return node; + }, + + + /** + * get an array/nodeList of child nodes + * + * @param {DOM node} node + * @return {Array} + */ + getChildren: function( node ){ + return node.children; + }, + + + /** + * create a node + * + * @param {String} tagName + * @return {DOM node} + */ + createNode: function( tagName ){ + return this.document.createElement(tagName); + }, + + + /** + * create a node with text content + * + * @param {String} tagName + * @param {String} text + * @return {DOM node} + */ + createNodeWithText: function( tagName, text ){ + var node = this.document.createElement(tagName); + node.innerHTML = text; + return node; + } + + + + }; + + + modules.url = { + + + /** + * creates DOM objects needed to resolve URLs + */ init: function(){ //this._domParser = new DOMParser(); this._domParser = modules.domUtils.getDOMParser(); @@ -3008,26 +3008,26 @@ var Microformats; // jshint ignore:line }, - /** - * resolves url to absolute version using baseUrl - * - * @param {String} url - * @param {String} baseUrl - * @return {String} - */ - resolve: function(url, baseUrl) { - // use modern URL web API where we can - if(modules.utils.isString(url) && modules.utils.isString(baseUrl) && url.indexOf('://') === -1){ - // this try catch is required as IE has an URL object but no constuctor support - // http://glennjones.net/articles/the-problem-with-window-url - try { - var resolved = new URL(url, baseUrl).toString(); - // deal with early Webkit not throwing an error - for Safari - if(resolved === '[object URL]'){ - resolved = URI.resolve(baseUrl, url); - } - return resolved; - }catch(e){ + /** + * resolves url to absolute version using baseUrl + * + * @param {String} url + * @param {String} baseUrl + * @return {String} + */ + resolve: function(url, baseUrl) { + // use modern URL web API where we can + if(modules.utils.isString(url) && modules.utils.isString(baseUrl) && url.indexOf('://') === -1){ + // this try catch is required as IE has an URL object but no constuctor support + // http://glennjones.net/articles/the-problem-with-window-url + try { + var resolved = new URL(url, baseUrl).toString(); + // deal with early Webkit not throwing an error - for Safari + if(resolved === '[object URL]'){ + resolved = URI.resolve(baseUrl, url); + } + return resolved; + }catch(e){ // otherwise fallback to DOM if(this._domParser === undefined){ this.init(); @@ -3039,1449 +3039,1449 @@ var Microformats; // jshint ignore:line // dont use getAttribute as it returns orginal value not resolved return this._linkNode.href; - } - }else{ - if(modules.utils.isString(url)){ - return url; - } - return ''; - } - }, - - }; - - - /** - * constructor - * parses text to find just the date element of an ISO date/time string i.e. 2008-05-01 - * - * @param {String} dateString - * @param {String} format - * @return {String} - */ - modules.ISODate = function ( dateString, format ) { - this.clear(); - - this.format = (format)? format : 'auto'; // auto or W3C or RFC3339 or HTML5 - this.setFormatSep(); - - // optional should be full iso date/time string - if(arguments[0]) { - this.parse(dateString, format); - } - }; - - - modules.ISODate.prototype = { - - - /** - * clear all states - * - */ - clear: function(){ - this.clearDate(); - this.clearTime(); - this.clearTimeZone(); - this.setAutoProfileState(); - }, - - - /** - * clear date states - * - */ - clearDate: function(){ - this.dY = -1; - this.dM = -1; - this.dD = -1; - this.dDDD = -1; - }, - - - /** - * clear time states - * - */ - clearTime: function(){ - this.tH = -1; - this.tM = -1; - this.tS = -1; - this.tD = -1; - }, - - - /** - * clear timezone states - * - */ - clearTimeZone: function(){ - this.tzH = -1; - this.tzM = -1; - this.tzPN = '+'; - this.z = false; - }, - - - /** - * resets the auto profile state - * - */ - setAutoProfileState: function(){ - this.autoProfile = { - sep: 'T', - dsep: '-', - tsep: ':', - tzsep: ':', - tzZulu: 'Z' - }; - }, - - - /** - * parses text to find ISO date/time string i.e. 2008-05-01T15:45:19Z - * - * @param {String} dateString - * @param {String} format - * @return {String} - */ - parse: function( dateString, format ) { - this.clear(); - - var parts = [], - tzArray = [], - position = 0, - datePart = '', - timePart = '', - timeZonePart = ''; - - if(format){ - this.format = format; - } - - - - // discover date time separtor for auto profile - // Set to 'T' by default - if(dateString.indexOf('t') > -1) { - this.autoProfile.sep = 't'; - } - if(dateString.indexOf('z') > -1) { - this.autoProfile.tzZulu = 'z'; - } - if(dateString.indexOf('Z') > -1) { - this.autoProfile.tzZulu = 'Z'; - } - if(dateString.toUpperCase().indexOf('T') === -1) { - this.autoProfile.sep = ' '; - } - - - dateString = dateString.toUpperCase().replace(' ','T'); - - // break on 'T' divider or space - if(dateString.indexOf('T') > -1) { - parts = dateString.split('T'); - datePart = parts[0]; - timePart = parts[1]; - - // zulu UTC - if(timePart.indexOf( 'Z' ) > -1) { - this.z = true; - } - - // timezone - if(timePart.indexOf( '+' ) > -1 || timePart.indexOf( '-' ) > -1) { - tzArray = timePart.split( 'Z' ); // incase of incorrect use of Z - timePart = tzArray[0]; - timeZonePart = tzArray[1]; - - // timezone - if(timePart.indexOf( '+' ) > -1 || timePart.indexOf( '-' ) > -1) { - position = 0; - - if(timePart.indexOf( '+' ) > -1) { - position = timePart.indexOf( '+' ); - } else { - position = timePart.indexOf( '-' ); - } - - timeZonePart = timePart.substring( position, timePart.length ); - timePart = timePart.substring( 0, position ); - } - } - - } else { - datePart = dateString; - } - - if(datePart !== '') { - this.parseDate( datePart ); - if(timePart !== '') { - this.parseTime( timePart ); - if(timeZonePart !== '') { - this.parseTimeZone( timeZonePart ); - } - } - } - return this.toString( format ); - }, - - - /** - * parses text to find just the date element of an ISO date/time string i.e. 2008-05-01 - * - * @param {String} dateString - * @param {String} format - * @return {String} - */ - parseDate: function( dateString, format ) { - this.clearDate(); - - var parts = []; - - // discover timezone separtor for auto profile // default is ':' - if(dateString.indexOf('-') === -1) { - this.autoProfile.tsep = ''; - } - - // YYYY-DDD - parts = dateString.match( /(\d\d\d\d)-(\d\d\d)/ ); - if(parts) { - if(parts[1]) { - this.dY = parts[1]; - } - if(parts[2]) { - this.dDDD = parts[2]; - } - } - - if(this.dDDD === -1) { - // YYYY-MM-DD ie 2008-05-01 and YYYYMMDD ie 20080501 - parts = dateString.match( /(\d\d\d\d)?-?(\d\d)?-?(\d\d)?/ ); - if(parts[1]) { - this.dY = parts[1]; - } - if(parts[2]) { - this.dM = parts[2]; - } - if(parts[3]) { - this.dD = parts[3]; - } - } - return this.toString(format); - }, - - - /** - * parses text to find just the time element of an ISO date/time string i.e. 13:30:45 - * - * @param {String} timeString - * @param {String} format - * @return {String} - */ - parseTime: function( timeString, format ) { - this.clearTime(); - var parts = []; - - // discover date separtor for auto profile // default is ':' - if(timeString.indexOf(':') === -1) { - this.autoProfile.tsep = ''; - } - - // finds timezone HH:MM:SS and HHMMSS ie 13:30:45, 133045 and 13:30:45.0135 - parts = timeString.match( /(\d\d)?:?(\d\d)?:?(\d\d)?.?([0-9]+)?/ ); - if(parts[1]) { - this.tH = parts[1]; - } - if(parts[2]) { - this.tM = parts[2]; - } - if(parts[3]) { - this.tS = parts[3]; - } - if(parts[4]) { - this.tD = parts[4]; - } - return this.toTimeString(format); - }, - - - /** - * parses text to find just the time element of an ISO date/time string i.e. +08:00 - * - * @param {String} timeString - * @param {String} format - * @return {String} - */ - parseTimeZone: function( timeString, format ) { - this.clearTimeZone(); - var parts = []; - - if(timeString.toLowerCase() === 'z'){ - this.z = true; - // set case for z - this.autoProfile.tzZulu = (timeString === 'z')? 'z' : 'Z'; - }else{ - - // discover timezone separtor for auto profile // default is ':' - if(timeString.indexOf(':') === -1) { - this.autoProfile.tzsep = ''; - } - - // finds timezone +HH:MM and +HHMM ie +13:30 and +1330 - parts = timeString.match( /([\-\+]{1})?(\d\d)?:?(\d\d)?/ ); - if(parts[1]) { - this.tzPN = parts[1]; - } - if(parts[2]) { - this.tzH = parts[2]; - } - if(parts[3]) { - this.tzM = parts[3]; - } - - - } - this.tzZulu = 'z'; - return this.toTimeString( format ); - }, - - - /** - * returns ISO date/time string in W3C Note, RFC 3339, HTML5, or auto profile - * - * @param {String} format - * @return {String} - */ - toString: function( format ) { - var output = ''; - - if(format){ - this.format = format; - } - this.setFormatSep(); - - if(this.dY > -1) { - output = this.dY; - if(this.dM > 0 && this.dM < 13) { - output += this.dsep + this.dM; - if(this.dD > 0 && this.dD < 32) { - output += this.dsep + this.dD; - if(this.tH > -1 && this.tH < 25) { - output += this.sep + this.toTimeString( format ); - } - } - } - if(this.dDDD > -1) { - output += this.dsep + this.dDDD; - } - } else if(this.tH > -1) { - output += this.toTimeString( format ); - } - - return output; - }, - - - /** - * returns just the time string element of an ISO date/time - * in W3C Note, RFC 3339, HTML5, or auto profile - * - * @param {String} format - * @return {String} - */ - toTimeString: function( format ) { - var out = ''; - - if(format){ - this.format = format; - } - this.setFormatSep(); - - // time can only be created with a full date - if(this.tH) { - if(this.tH > -1 && this.tH < 25) { - out += this.tH; - if(this.tM > -1 && this.tM < 61){ - out += this.tsep + this.tM; - if(this.tS > -1 && this.tS < 61){ - out += this.tsep + this.tS; - if(this.tD > -1){ - out += '.' + this.tD; - } - } - } - - - - // time zone offset - if(this.z) { - out += this.tzZulu; - } else { - if(this.tzH && this.tzH > -1 && this.tzH < 25) { - out += this.tzPN + this.tzH; - if(this.tzM > -1 && this.tzM < 61){ - out += this.tzsep + this.tzM; - } - } - } - } - } - return out; - }, - - - /** - * set the current profile to W3C Note, RFC 3339, HTML5, or auto profile - * - */ - setFormatSep: function() { - switch( this.format.toLowerCase() ) { - case 'rfc3339': - this.sep = 'T'; - this.dsep = ''; - this.tsep = ''; - this.tzsep = ''; - this.tzZulu = 'Z'; - break; - case 'w3c': - this.sep = 'T'; - this.dsep = '-'; - this.tsep = ':'; - this.tzsep = ':'; - this.tzZulu = 'Z'; - break; - case 'html5': - this.sep = ' '; - this.dsep = '-'; - this.tsep = ':'; - this.tzsep = ':'; - this.tzZulu = 'Z'; - break; - default: - // auto - defined by format of input string - this.sep = this.autoProfile.sep; - this.dsep = this.autoProfile.dsep; - this.tsep = this.autoProfile.tsep; - this.tzsep = this.autoProfile.tzsep; - this.tzZulu = this.autoProfile.tzZulu; - } - }, - - - /** - * does current data contain a full date i.e. 2015-03-23 - * - * @return {Boolean} - */ - hasFullDate: function() { - return(this.dY !== -1 && this.dM !== -1 && this.dD !== -1); - }, - - - /** - * does current data contain a minimum date which is just a year number i.e. 2015 - * - * @return {Boolean} - */ - hasDate: function() { - return(this.dY !== -1); - }, - - - /** - * does current data contain a minimum time which is just a hour number i.e. 13 - * - * @return {Boolean} - */ - hasTime: function() { - return(this.tH !== -1); - }, - - /** - * does current data contain a minimum timezone i.e. -1 || +1 || z - * - * @return {Boolean} - */ - hasTimeZone: function() { - return(this.tzH !== -1); - } - - }; - - modules.ISODate.prototype.constructor = modules.ISODate; - - - modules.dates = { - - - /** - * does text contain am - * - * @param {String} text - * @return {Boolean} - */ - hasAM: function( text ) { - text = text.toLowerCase(); - return(text.indexOf('am') > -1 || text.indexOf('a.m.') > -1); - }, - - - /** - * does text contain pm - * - * @param {String} text - * @return {Boolean} - */ - hasPM: function( text ) { - text = text.toLowerCase(); - return(text.indexOf('pm') > -1 || text.indexOf('p.m.') > -1); - }, - - - /** - * remove am and pm from text and return it - * - * @param {String} text - * @return {String} - */ - removeAMPM: function( text ) { - return text.replace('pm', '').replace('p.m.', '').replace('am', '').replace('a.m.', ''); - }, - - - /** - * simple test of whether ISO date string is a duration i.e. PY17M or PW12 - * - * @param {String} text - * @return {Boolean} - */ - isDuration: function( text ) { - if(modules.utils.isString( text )){ - text = text.toLowerCase(); - if(modules.utils.startWith(text, 'p') ){ - return true; - } - } - return false; - }, - - - /** - * is text a time or timezone - * i.e. HH-MM-SS or z+-HH-MM-SS 08:43 | 15:23:00:0567 | 10:34pm | 10:34 p.m. | +01:00:00 | -02:00 | z15:00 | 0843 - * - * @param {String} text - * @return {Boolean} - */ - isTime: function( text ) { - if(modules.utils.isString(text)){ - text = text.toLowerCase(); - text = modules.utils.trim( text ); - // start with timezone char - if( text.match(':') && ( modules.utils.startWith(text, 'z') || modules.utils.startWith(text, '-') || modules.utils.startWith(text, '+') )) { - return true; - } - // has ante meridiem or post meridiem - if( text.match(/^[0-9]/) && - ( this.hasAM(text) || this.hasPM(text) )) { - return true; - } - // contains time delimiter but not datetime delimiter - if( text.match(':') && !text.match(/t|\s/) ) { - return true; - } - - // if it's a number of 2, 4 or 6 chars - if(modules.utils.isNumber(text)){ - if(text.length === 2 || text.length === 4 || text.length === 6){ - return true; - } - } - } - return false; - }, - - - /** - * parses a time from text and returns 24hr time string - * i.e. 5:34am = 05:34:00 and 1:52:04p.m. = 13:52:04 - * - * @param {String} text - * @return {String} - */ - parseAmPmTime: function( text ) { - var out = text, - times = []; - - // if the string has a text : or am or pm - if(modules.utils.isString(out)) { - //text = text.toLowerCase(); - text = text.replace(/[ ]+/g, ''); - - if(text.match(':') || this.hasAM(text) || this.hasPM(text)) { - - if(text.match(':')) { - times = text.split(':'); - } else { - // single number text i.e. 5pm - times[0] = text; - times[0] = this.removeAMPM(times[0]); - } - - // change pm hours to 24hr number - if(this.hasPM(text)) { - if(times[0] < 12) { - times[0] = parseInt(times[0], 10) + 12; - } - } - - // add leading zero's where needed - if(times[0] && times[0].length === 1) { - times[0] = '0' + times[0]; - } - - // rejoin text elements together - if(times[0]) { - text = times.join(':'); - } - } - } - - // remove am/pm strings - return this.removeAMPM(text); - }, - - - /** - * overlays a time on a date to return the union of the two - * - * @param {String} date - * @param {String} time - * @param {String} format ( Modules.ISODate profile format ) - * @return {Object} Modules.ISODate - */ - dateTimeUnion: function(date, time, format) { - var isodate = new modules.ISODate(date, format), - isotime = new modules.ISODate(); - - isotime.parseTime(this.parseAmPmTime(time), format); - if(isodate.hasFullDate() && isotime.hasTime()) { - isodate.tH = isotime.tH; - isodate.tM = isotime.tM; - isodate.tS = isotime.tS; - isodate.tD = isotime.tD; - return isodate; - } else { - if(isodate.hasFullDate()){ - return isodate; - } - return new modules.ISODate(); - } - }, - - - /** - * concatenate an array of date and time text fragments to create an ISODate object - * used for microformat value and value-title rules - * - * @param {Array} arr ( Array of Strings ) - * @param {String} format ( Modules.ISODate profile format ) - * @return {Object} Modules.ISODate - */ - concatFragments: function (arr, format) { - var out = new modules.ISODate(), - i = 0, - value = ''; - - // if the fragment already contains a full date just return it once - if(arr[0].toUpperCase().match('T')) { - return new modules.ISODate(arr[0], format); - }else{ - for(i = 0; i < arr.length; i++) { - value = arr[i]; - - // date pattern - if( value.charAt(4) === '-' && out.hasFullDate() === false ){ - out.parseDate(value); - } - - // time pattern - if( (value.indexOf(':') > -1 || modules.utils.isNumber( this.parseAmPmTime(value) )) && out.hasTime() === false ) { - // split time and timezone - var items = this.splitTimeAndZone(value); - value = items[0]; - - // parse any use of am/pm - value = this.parseAmPmTime(value); - out.parseTime(value); - - // parse any timezone - if(items.length > 1){ - out.parseTimeZone(items[1], format); - } - } - - // timezone pattern - if(value.charAt(0) === '-' || value.charAt(0) === '+' || value.toUpperCase() === 'Z') { - if( out.hasTimeZone() === false ){ - out.parseTimeZone(value); - } - } - - } - return out; - - } - }, - - - /** - * parses text by splitting it into an array of time and timezone strings - * - * @param {String} text - * @return {Array} Modules.ISODate - */ - splitTimeAndZone: function ( text ){ - var out = [text], - chars = ['-','+','z','Z'], - i = chars.length; - - while (i--) { - if(text.indexOf(chars[i]) > -1){ - out[0] = text.slice( 0, text.indexOf(chars[i]) ); - out.push( text.slice( text.indexOf(chars[i]) ) ); - break; - } - } - return out; - } - - }; - - - modules.text = { - - // normalised or whitespace or whitespacetrimmed - textFormat: 'whitespacetrimmed', - - // block level tags, used to add line returns - blockLevelTags: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'hr', 'pre', 'table', - 'address', 'article', 'aside', 'blockquote', 'caption', 'col', 'colgroup', 'dd', 'div', - 'dt', 'dir', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'header', 'hgroup', 'hr', - 'li', 'map', 'menu', 'nav', 'optgroup', 'option', 'section', 'tbody', 'testarea', - 'tfoot', 'th', 'thead', 'tr', 'td', 'ul', 'ol', 'dl', 'details'], - - // tags to exclude - excludeTags: ['noframe', 'noscript', 'template', 'script', 'style', 'frames', 'frameset'], - - - /** - * parses the text from the DOM Node - * - * @param {DOM Node} node - * @param {String} textFormat - * @return {String} - */ - parse: function(doc, node, textFormat){ - var out; - this.textFormat = (textFormat)? textFormat : this.textFormat; - if(this.textFormat === 'normalised'){ - out = this.walkTreeForText( node ); - if(out !== undefined){ - return this.normalise( doc, out ); - }else{ - return ''; - } - }else{ - return this.formatText( doc, modules.domUtils.textContent(node), this.textFormat ); - } - }, - - - /** - * parses the text from a html string - * - * @param {DOM Document} doc - * @param {String} text - * @param {String} textFormat - * @return {String} - */ - parseText: function( doc, text, textFormat ){ - var node = modules.domUtils.createNodeWithText( 'div', text ); - return this.parse( doc, node, textFormat ); - }, - - - /** - * parses the text from a html string - only for whitespace or whitespacetrimmed formats - * - * @param {String} text - * @param {String} textFormat - * @return {String} - */ - formatText: function( doc, text, textFormat ){ - this.textFormat = (textFormat)? textFormat : this.textFormat; - if(text){ - var out = '', - regex = /(<([^>]+)>)/ig; - - out = text.replace(regex, ''); - if(this.textFormat === 'whitespacetrimmed') { - out = modules.utils.trimWhitespace( out ); - } - - //return entities.decode( out, 2 ); - return modules.domUtils.decodeEntities( doc, out ); - }else{ - return ''; - } - }, - - - /** - * normalises whitespace in given text - * - * @param {String} text - * @return {String} - */ - normalise: function( doc, text ){ - text = text.replace( / /g, ' ') ; // exchanges html entity for space into space char - text = modules.utils.collapseWhiteSpace( text ); // removes linefeeds, tabs and addtional spaces - text = modules.domUtils.decodeEntities( doc, text ); // decode HTML entities - text = text.replace( '–', '-' ); // correct dash decoding - return modules.utils.trim( text ); - }, - - - /** - * walks DOM tree parsing the text from DOM Nodes - * - * @param {DOM Node} node - * @return {String} - */ - walkTreeForText: function( node ) { - var out = '', - j = 0; - - if(node.tagName && this.excludeTags.indexOf( node.tagName.toLowerCase() ) > -1){ - return out; - } - - // if node is a text node get its text - if(node.nodeType && node.nodeType === 3){ - out += modules.domUtils.getElementText( node ); - } - - // get the text of the child nodes - if(node.childNodes && node.childNodes.length > 0){ - for (j = 0; j < node.childNodes.length; j++) { - var text = this.walkTreeForText( node.childNodes[j] ); - if(text !== undefined){ - out += text; - } - } - } - - // if it's a block level tag add an additional space at the end - if(node.tagName && this.blockLevelTags.indexOf( node.tagName.toLowerCase() ) !== -1){ - out += ' '; - } - - return (out === '')? undefined : out ; - } - - }; - - - modules.html = { - - // elements which are self-closing - selfClosingElt: ['area', 'base', 'br', 'col', 'hr', 'img', 'input', 'link', 'meta', 'param', 'command', 'keygen', 'source'], - - - /** - * parse the html string from DOM Node - * - * @param {DOM Node} node - * @return {String} - */ - parse: function( node ){ - var out = '', - j = 0; - - // we do not want the outer container - if(node.childNodes && node.childNodes.length > 0){ - for (j = 0; j < node.childNodes.length; j++) { - var text = this.walkTreeForHtml( node.childNodes[j] ); - if(text !== undefined){ - out += text; - } - } - } - - return out; - }, - - - /** - * walks the DOM tree parsing the html string from the nodes - * - * @param {DOM Document} doc - * @param {DOM Node} node - * @return {String} - */ - walkTreeForHtml: function( node ) { - var out = '', - j = 0; - - // if node is a text node get its text - if(node.nodeType && node.nodeType === 3){ - out += modules.domUtils.getElementText( node ); - } - - - // exclude text which has been added with include pattern - - if(node.nodeType && node.nodeType === 1 && modules.domUtils.hasAttribute(node, 'data-include') === false){ - - // begin tag - out += '<' + node.tagName.toLowerCase(); - - // add attributes - var attrs = modules.domUtils.getOrderedAttributes(node); - for (j = 0; j < attrs.length; j++) { - out += ' ' + attrs[j].name + '=' + '"' + attrs[j].value + '"'; - } - - if(this.selfClosingElt.indexOf(node.tagName.toLowerCase()) === -1){ - out += '>'; - } - - // get the text of the child nodes - if(node.childNodes && node.childNodes.length > 0){ - - for (j = 0; j < node.childNodes.length; j++) { - var text = this.walkTreeForHtml( node.childNodes[j] ); - if(text !== undefined){ - out += text; - } - } - } - - // end tag - if(this.selfClosingElt.indexOf(node.tagName.toLowerCase()) > -1){ - out += ' />'; - }else{ - out += ''; - } - } - - return (out === '')? undefined : out; - } - - - }; - - - modules.maps['h-adr'] = { - root: 'adr', - name: 'h-adr', - properties: { - 'post-office-box': {}, - 'street-address': {}, - 'extended-address': {}, - 'locality': {}, - 'region': {}, - 'postal-code': {}, - 'country-name': {} - } - }; - - - modules.maps['h-card'] = { - root: 'vcard', - name: 'h-card', - properties: { - 'fn': { - 'map': 'p-name' - }, - 'adr': { - 'map': 'p-adr', - 'uf': ['h-adr'] - }, - 'agent': { - 'uf': ['h-card'] - }, - 'bday': { - 'map': 'dt-bday' - }, - 'class': {}, - 'category': { - 'map': 'p-category', - 'relAlt': ['tag'] - }, - 'email': { - 'map': 'u-email' - }, - 'geo': { - 'map': 'p-geo', - 'uf': ['h-geo'] - }, - 'key': { - 'map': 'u-key' - }, - 'label': {}, - 'logo': { - 'map': 'u-logo' - }, - 'mailer': {}, - 'honorific-prefix': {}, - 'given-name': {}, - 'additional-name': {}, - 'family-name': {}, - 'honorific-suffix': {}, - 'nickname': {}, - 'note': {}, // could be html i.e. e-note - 'org': {}, - 'p-organization-name': {}, - 'p-organization-unit': {}, - 'photo': { - 'map': 'u-photo' - }, - 'rev': { - 'map': 'dt-rev' - }, - 'role': {}, - 'sequence': {}, - 'sort-string': {}, - 'sound': { - 'map': 'u-sound' - }, - 'title': { - 'map': 'p-job-title' - }, - 'tel': {}, - 'tz': {}, - 'uid': { - 'map': 'u-uid' - }, - 'url': { - 'map': 'u-url' - } - } - }; - - - modules.maps['h-entry'] = { - root: 'hentry', - name: 'h-entry', - properties: { - 'entry-title': { - 'map': 'p-name' - }, - 'entry-summary': { - 'map': 'p-summary' - }, - 'entry-content': { - 'map': 'e-content' - }, - 'published': { - 'map': 'dt-published' - }, - 'updated': { - 'map': 'dt-updated' - }, - 'author': { - 'uf': ['h-card'] - }, - 'category': { - 'map': 'p-category', - 'relAlt': ['tag'] - }, - 'geo': { - 'map': 'p-geo', - 'uf': ['h-geo'] - }, - 'latitude': {}, - 'longitude': {}, - 'url': { - 'map': 'u-url', - 'relAlt': ['bookmark'] - } - } - }; - - - modules.maps['h-event'] = { - root: 'vevent', - name: 'h-event', - properties: { - 'summary': { - 'map': 'p-name' - }, - 'dtstart': { - 'map': 'dt-start' - }, - 'dtend': { - 'map': 'dt-end' - }, - 'description': {}, - 'url': { - 'map': 'u-url' - }, - 'category': { - 'map': 'p-category', - 'relAlt': ['tag'] - }, - 'location': { - 'uf': ['h-card'] - }, - 'geo': { - 'uf': ['h-geo'] - }, - 'latitude': {}, - 'longitude': {}, - 'duration': { - 'map': 'dt-duration' - }, - 'contact': { - 'uf': ['h-card'] - }, - 'organizer': { - 'uf': ['h-card']}, - 'attendee': { - 'uf': ['h-card']}, - 'uid': { - 'map': 'u-uid' - }, - 'attach': { - 'map': 'u-attach' - }, - 'status': {}, - 'rdate': {}, - 'rrule': {} - } - }; - - - modules.maps['h-feed'] = { - root: 'hfeed', - name: 'h-feed', - properties: { - 'category': { - 'map': 'p-category', - 'relAlt': ['tag'] - }, - 'summary': { - 'map': 'p-summary' - }, - 'author': { - 'uf': ['h-card'] - }, - 'url': { - 'map': 'u-url' - }, - 'photo': { - 'map': 'u-photo' - }, - } - }; - - - modules.maps['h-geo'] = { - root: 'geo', - name: 'h-geo', - properties: { - 'latitude': {}, - 'longitude': {} - } - }; - - - modules.maps['h-item'] = { - root: 'item', - name: 'h-item', - subTree: false, - properties: { - 'fn': { - 'map': 'p-name' - }, - 'url': { - 'map': 'u-url' - }, - 'photo': { - 'map': 'u-photo' - } - } - }; - - - modules.maps['h-listing'] = { - root: 'hlisting', - name: 'h-listing', - properties: { - 'version': {}, - 'lister': { - 'uf': ['h-card'] - }, - 'dtlisted': { - 'map': 'dt-listed' - }, - 'dtexpired': { - 'map': 'dt-expired' - }, - 'location': {}, - 'price': {}, - 'item': { - 'uf': ['h-card','a-adr','h-geo'] - }, - 'summary': { - 'map': 'p-name' - }, - 'description': { - 'map': 'e-description' - }, - 'listing': {} - } - }; - - - modules.maps['h-news'] = { - root: 'hnews', - name: 'h-news', - properties: { - 'entry': { - 'uf': ['h-entry'] - }, - 'geo': { - 'uf': ['h-geo'] - }, - 'latitude': {}, - 'longitude': {}, - 'source-org': { - 'uf': ['h-card'] - }, - 'dateline': { - 'uf': ['h-card'] - }, - 'item-license': { - 'map': 'u-item-license' - }, - 'principles': { - 'map': 'u-principles', - 'relAlt': ['principles'] - } - } - }; - - - modules.maps['h-org'] = { - root: 'h-x-org', // drop this from v1 as it causes issue with fn org hcard pattern - name: 'h-org', - childStructure: true, - properties: { - 'organization-name': {}, - 'organization-unit': {} - } - }; - - - modules.maps['h-product'] = { - root: 'hproduct', - name: 'h-product', - properties: { - 'brand': { - 'uf': ['h-card'] - }, - 'category': { - 'map': 'p-category', - 'relAlt': ['tag'] - }, - 'price': {}, - 'description': { - 'map': 'e-description' - }, - 'fn': { - 'map': 'p-name' - }, - 'photo': { - 'map': 'u-photo' - }, - 'url': { - 'map': 'u-url' - }, - 'review': { - 'uf': ['h-review', 'h-review-aggregate'] - }, - 'listing': { - 'uf': ['h-listing'] - }, - 'identifier': { - 'map': 'u-identifier' - } - } - }; - - - modules.maps['h-recipe'] = { - root: 'hrecipe', - name: 'h-recipe', - properties: { - 'fn': { - 'map': 'p-name' - }, - 'ingredient': { - 'map': 'e-ingredient' - }, - 'yield': {}, - 'instructions': { - 'map': 'e-instructions' - }, - 'duration': { - 'map': 'dt-duration' - }, - 'photo': { - 'map': 'u-photo' - }, - 'summary': {}, - 'author': { - 'uf': ['h-card'] - }, - 'published': { - 'map': 'dt-published' - }, - 'nutrition': {}, - 'category': { - 'map': 'p-category', - 'relAlt': ['tag'] - }, - } - }; - - - modules.maps['h-resume'] = { - root: 'hresume', - name: 'h-resume', - properties: { - 'summary': {}, - 'contact': { - 'uf': ['h-card'] - }, - 'education': { - 'uf': ['h-card', 'h-event'] - }, - 'experience': { - 'uf': ['h-card', 'h-event'] - }, - 'skill': {}, - 'affiliation': { - 'uf': ['h-card'] - } - } - }; - - - modules.maps['h-review-aggregate'] = { - root: 'hreview-aggregate', - name: 'h-review-aggregate', - properties: { - 'summary': { - 'map': 'p-name' - }, - 'item': { - 'map': 'p-item', - 'uf': ['h-item', 'h-geo', 'h-adr', 'h-card', 'h-event', 'h-product'] - }, - 'rating': {}, - 'average': {}, - 'best': {}, - 'worst': {}, - 'count': {}, - 'votes': {}, - 'category': { - 'map': 'p-category', - 'relAlt': ['tag'] - }, - 'url': { - 'map': 'u-url', - 'relAlt': ['self', 'bookmark'] - } - } - }; - - - modules.maps['h-review'] = { - root: 'hreview', - name: 'h-review', - properties: { - 'summary': { - 'map': 'p-name' - }, - 'description': { - 'map': 'e-description' - }, - 'item': { - 'map': 'p-item', - 'uf': ['h-item', 'h-geo', 'h-adr', 'h-card', 'h-event', 'h-product'] - }, - 'reviewer': { - 'uf': ['h-card'] - }, - 'dtreviewer': { - 'map': 'dt-reviewer' - }, - 'rating': {}, - 'best': {}, - 'worst': {}, - 'category': { - 'map': 'p-category', - 'relAlt': ['tag'] - }, - 'url': { - 'map': 'u-url', - 'relAlt': ['self', 'bookmark'] - } - } - }; - - - modules.rels = { - // xfn - 'friend': [ 'yes','external'], - 'acquaintance': [ 'yes','external'], - 'contact': [ 'yes','external'], - 'met': [ 'yes','external'], - 'co-worker': [ 'yes','external'], - 'colleague': [ 'yes','external'], - 'co-resident': [ 'yes','external'], - 'neighbor': [ 'yes','external'], - 'child': [ 'yes','external'], - 'parent': [ 'yes','external'], - 'sibling': [ 'yes','external'], - 'spouse': [ 'yes','external'], - 'kin': [ 'yes','external'], - 'muse': [ 'yes','external'], - 'crush': [ 'yes','external'], - 'date': [ 'yes','external'], - 'sweetheart': [ 'yes','external'], - 'me': [ 'yes','external'], - - // other rel=* - 'license': [ 'yes','yes'], - 'nofollow': [ 'no','external'], - 'tag': [ 'no','yes'], - 'self': [ 'no','external'], - 'bookmark': [ 'no','external'], - 'author': [ 'no','external'], - 'home': [ 'no','external'], - 'directory': [ 'no','external'], - 'enclosure': [ 'no','external'], - 'pronunciation': [ 'no','external'], - 'payment': [ 'no','external'], - 'principles': [ 'no','external'] - - }; + } + }else{ + if(modules.utils.isString(url)){ + return url; + } + return ''; + } + }, + + }; + + + /** + * constructor + * parses text to find just the date element of an ISO date/time string i.e. 2008-05-01 + * + * @param {String} dateString + * @param {String} format + * @return {String} + */ + modules.ISODate = function ( dateString, format ) { + this.clear(); + + this.format = (format)? format : 'auto'; // auto or W3C or RFC3339 or HTML5 + this.setFormatSep(); + + // optional should be full iso date/time string + if(arguments[0]) { + this.parse(dateString, format); + } + }; + + + modules.ISODate.prototype = { + + + /** + * clear all states + * + */ + clear: function(){ + this.clearDate(); + this.clearTime(); + this.clearTimeZone(); + this.setAutoProfileState(); + }, + + + /** + * clear date states + * + */ + clearDate: function(){ + this.dY = -1; + this.dM = -1; + this.dD = -1; + this.dDDD = -1; + }, + + + /** + * clear time states + * + */ + clearTime: function(){ + this.tH = -1; + this.tM = -1; + this.tS = -1; + this.tD = -1; + }, + + + /** + * clear timezone states + * + */ + clearTimeZone: function(){ + this.tzH = -1; + this.tzM = -1; + this.tzPN = '+'; + this.z = false; + }, + + + /** + * resets the auto profile state + * + */ + setAutoProfileState: function(){ + this.autoProfile = { + sep: 'T', + dsep: '-', + tsep: ':', + tzsep: ':', + tzZulu: 'Z' + }; + }, + + + /** + * parses text to find ISO date/time string i.e. 2008-05-01T15:45:19Z + * + * @param {String} dateString + * @param {String} format + * @return {String} + */ + parse: function( dateString, format ) { + this.clear(); + + var parts = [], + tzArray = [], + position = 0, + datePart = '', + timePart = '', + timeZonePart = ''; + + if(format){ + this.format = format; + } + + + + // discover date time separtor for auto profile + // Set to 'T' by default + if(dateString.indexOf('t') > -1) { + this.autoProfile.sep = 't'; + } + if(dateString.indexOf('z') > -1) { + this.autoProfile.tzZulu = 'z'; + } + if(dateString.indexOf('Z') > -1) { + this.autoProfile.tzZulu = 'Z'; + } + if(dateString.toUpperCase().indexOf('T') === -1) { + this.autoProfile.sep = ' '; + } + + + dateString = dateString.toUpperCase().replace(' ','T'); + + // break on 'T' divider or space + if(dateString.indexOf('T') > -1) { + parts = dateString.split('T'); + datePart = parts[0]; + timePart = parts[1]; + + // zulu UTC + if(timePart.indexOf( 'Z' ) > -1) { + this.z = true; + } + + // timezone + if(timePart.indexOf( '+' ) > -1 || timePart.indexOf( '-' ) > -1) { + tzArray = timePart.split( 'Z' ); // incase of incorrect use of Z + timePart = tzArray[0]; + timeZonePart = tzArray[1]; + + // timezone + if(timePart.indexOf( '+' ) > -1 || timePart.indexOf( '-' ) > -1) { + position = 0; + + if(timePart.indexOf( '+' ) > -1) { + position = timePart.indexOf( '+' ); + } else { + position = timePart.indexOf( '-' ); + } + + timeZonePart = timePart.substring( position, timePart.length ); + timePart = timePart.substring( 0, position ); + } + } + + } else { + datePart = dateString; + } + + if(datePart !== '') { + this.parseDate( datePart ); + if(timePart !== '') { + this.parseTime( timePart ); + if(timeZonePart !== '') { + this.parseTimeZone( timeZonePart ); + } + } + } + return this.toString( format ); + }, + + + /** + * parses text to find just the date element of an ISO date/time string i.e. 2008-05-01 + * + * @param {String} dateString + * @param {String} format + * @return {String} + */ + parseDate: function( dateString, format ) { + this.clearDate(); + + var parts = []; + + // discover timezone separtor for auto profile // default is ':' + if(dateString.indexOf('-') === -1) { + this.autoProfile.tsep = ''; + } + + // YYYY-DDD + parts = dateString.match( /(\d\d\d\d)-(\d\d\d)/ ); + if(parts) { + if(parts[1]) { + this.dY = parts[1]; + } + if(parts[2]) { + this.dDDD = parts[2]; + } + } + + if(this.dDDD === -1) { + // YYYY-MM-DD ie 2008-05-01 and YYYYMMDD ie 20080501 + parts = dateString.match( /(\d\d\d\d)?-?(\d\d)?-?(\d\d)?/ ); + if(parts[1]) { + this.dY = parts[1]; + } + if(parts[2]) { + this.dM = parts[2]; + } + if(parts[3]) { + this.dD = parts[3]; + } + } + return this.toString(format); + }, + + + /** + * parses text to find just the time element of an ISO date/time string i.e. 13:30:45 + * + * @param {String} timeString + * @param {String} format + * @return {String} + */ + parseTime: function( timeString, format ) { + this.clearTime(); + var parts = []; + + // discover date separtor for auto profile // default is ':' + if(timeString.indexOf(':') === -1) { + this.autoProfile.tsep = ''; + } + + // finds timezone HH:MM:SS and HHMMSS ie 13:30:45, 133045 and 13:30:45.0135 + parts = timeString.match( /(\d\d)?:?(\d\d)?:?(\d\d)?.?([0-9]+)?/ ); + if(parts[1]) { + this.tH = parts[1]; + } + if(parts[2]) { + this.tM = parts[2]; + } + if(parts[3]) { + this.tS = parts[3]; + } + if(parts[4]) { + this.tD = parts[4]; + } + return this.toTimeString(format); + }, + + + /** + * parses text to find just the time element of an ISO date/time string i.e. +08:00 + * + * @param {String} timeString + * @param {String} format + * @return {String} + */ + parseTimeZone: function( timeString, format ) { + this.clearTimeZone(); + var parts = []; + + if(timeString.toLowerCase() === 'z'){ + this.z = true; + // set case for z + this.autoProfile.tzZulu = (timeString === 'z')? 'z' : 'Z'; + }else{ + + // discover timezone separtor for auto profile // default is ':' + if(timeString.indexOf(':') === -1) { + this.autoProfile.tzsep = ''; + } + + // finds timezone +HH:MM and +HHMM ie +13:30 and +1330 + parts = timeString.match( /([\-\+]{1})?(\d\d)?:?(\d\d)?/ ); + if(parts[1]) { + this.tzPN = parts[1]; + } + if(parts[2]) { + this.tzH = parts[2]; + } + if(parts[3]) { + this.tzM = parts[3]; + } + + + } + this.tzZulu = 'z'; + return this.toTimeString( format ); + }, + + + /** + * returns ISO date/time string in W3C Note, RFC 3339, HTML5, or auto profile + * + * @param {String} format + * @return {String} + */ + toString: function( format ) { + var output = ''; + + if(format){ + this.format = format; + } + this.setFormatSep(); + + if(this.dY > -1) { + output = this.dY; + if(this.dM > 0 && this.dM < 13) { + output += this.dsep + this.dM; + if(this.dD > 0 && this.dD < 32) { + output += this.dsep + this.dD; + if(this.tH > -1 && this.tH < 25) { + output += this.sep + this.toTimeString( format ); + } + } + } + if(this.dDDD > -1) { + output += this.dsep + this.dDDD; + } + } else if(this.tH > -1) { + output += this.toTimeString( format ); + } + + return output; + }, + + + /** + * returns just the time string element of an ISO date/time + * in W3C Note, RFC 3339, HTML5, or auto profile + * + * @param {String} format + * @return {String} + */ + toTimeString: function( format ) { + var out = ''; + + if(format){ + this.format = format; + } + this.setFormatSep(); + + // time can only be created with a full date + if(this.tH) { + if(this.tH > -1 && this.tH < 25) { + out += this.tH; + if(this.tM > -1 && this.tM < 61){ + out += this.tsep + this.tM; + if(this.tS > -1 && this.tS < 61){ + out += this.tsep + this.tS; + if(this.tD > -1){ + out += '.' + this.tD; + } + } + } + + + + // time zone offset + if(this.z) { + out += this.tzZulu; + } else { + if(this.tzH && this.tzH > -1 && this.tzH < 25) { + out += this.tzPN + this.tzH; + if(this.tzM > -1 && this.tzM < 61){ + out += this.tzsep + this.tzM; + } + } + } + } + } + return out; + }, + + + /** + * set the current profile to W3C Note, RFC 3339, HTML5, or auto profile + * + */ + setFormatSep: function() { + switch( this.format.toLowerCase() ) { + case 'rfc3339': + this.sep = 'T'; + this.dsep = ''; + this.tsep = ''; + this.tzsep = ''; + this.tzZulu = 'Z'; + break; + case 'w3c': + this.sep = 'T'; + this.dsep = '-'; + this.tsep = ':'; + this.tzsep = ':'; + this.tzZulu = 'Z'; + break; + case 'html5': + this.sep = ' '; + this.dsep = '-'; + this.tsep = ':'; + this.tzsep = ':'; + this.tzZulu = 'Z'; + break; + default: + // auto - defined by format of input string + this.sep = this.autoProfile.sep; + this.dsep = this.autoProfile.dsep; + this.tsep = this.autoProfile.tsep; + this.tzsep = this.autoProfile.tzsep; + this.tzZulu = this.autoProfile.tzZulu; + } + }, + + + /** + * does current data contain a full date i.e. 2015-03-23 + * + * @return {Boolean} + */ + hasFullDate: function() { + return(this.dY !== -1 && this.dM !== -1 && this.dD !== -1); + }, + + + /** + * does current data contain a minimum date which is just a year number i.e. 2015 + * + * @return {Boolean} + */ + hasDate: function() { + return(this.dY !== -1); + }, + + + /** + * does current data contain a minimum time which is just a hour number i.e. 13 + * + * @return {Boolean} + */ + hasTime: function() { + return(this.tH !== -1); + }, + + /** + * does current data contain a minimum timezone i.e. -1 || +1 || z + * + * @return {Boolean} + */ + hasTimeZone: function() { + return(this.tzH !== -1); + } + + }; + + modules.ISODate.prototype.constructor = modules.ISODate; + + + modules.dates = { + + + /** + * does text contain am + * + * @param {String} text + * @return {Boolean} + */ + hasAM: function( text ) { + text = text.toLowerCase(); + return(text.indexOf('am') > -1 || text.indexOf('a.m.') > -1); + }, + + + /** + * does text contain pm + * + * @param {String} text + * @return {Boolean} + */ + hasPM: function( text ) { + text = text.toLowerCase(); + return(text.indexOf('pm') > -1 || text.indexOf('p.m.') > -1); + }, + + + /** + * remove am and pm from text and return it + * + * @param {String} text + * @return {String} + */ + removeAMPM: function( text ) { + return text.replace('pm', '').replace('p.m.', '').replace('am', '').replace('a.m.', ''); + }, + + + /** + * simple test of whether ISO date string is a duration i.e. PY17M or PW12 + * + * @param {String} text + * @return {Boolean} + */ + isDuration: function( text ) { + if(modules.utils.isString( text )){ + text = text.toLowerCase(); + if(modules.utils.startWith(text, 'p') ){ + return true; + } + } + return false; + }, + + + /** + * is text a time or timezone + * i.e. HH-MM-SS or z+-HH-MM-SS 08:43 | 15:23:00:0567 | 10:34pm | 10:34 p.m. | +01:00:00 | -02:00 | z15:00 | 0843 + * + * @param {String} text + * @return {Boolean} + */ + isTime: function( text ) { + if(modules.utils.isString(text)){ + text = text.toLowerCase(); + text = modules.utils.trim( text ); + // start with timezone char + if( text.match(':') && ( modules.utils.startWith(text, 'z') || modules.utils.startWith(text, '-') || modules.utils.startWith(text, '+') )) { + return true; + } + // has ante meridiem or post meridiem + if( text.match(/^[0-9]/) && + ( this.hasAM(text) || this.hasPM(text) )) { + return true; + } + // contains time delimiter but not datetime delimiter + if( text.match(':') && !text.match(/t|\s/) ) { + return true; + } + + // if it's a number of 2, 4 or 6 chars + if(modules.utils.isNumber(text)){ + if(text.length === 2 || text.length === 4 || text.length === 6){ + return true; + } + } + } + return false; + }, + + + /** + * parses a time from text and returns 24hr time string + * i.e. 5:34am = 05:34:00 and 1:52:04p.m. = 13:52:04 + * + * @param {String} text + * @return {String} + */ + parseAmPmTime: function( text ) { + var out = text, + times = []; + + // if the string has a text : or am or pm + if(modules.utils.isString(out)) { + //text = text.toLowerCase(); + text = text.replace(/[ ]+/g, ''); + + if(text.match(':') || this.hasAM(text) || this.hasPM(text)) { + + if(text.match(':')) { + times = text.split(':'); + } else { + // single number text i.e. 5pm + times[0] = text; + times[0] = this.removeAMPM(times[0]); + } + + // change pm hours to 24hr number + if(this.hasPM(text)) { + if(times[0] < 12) { + times[0] = parseInt(times[0], 10) + 12; + } + } + + // add leading zero's where needed + if(times[0] && times[0].length === 1) { + times[0] = '0' + times[0]; + } + + // rejoin text elements together + if(times[0]) { + text = times.join(':'); + } + } + } + + // remove am/pm strings + return this.removeAMPM(text); + }, + + + /** + * overlays a time on a date to return the union of the two + * + * @param {String} date + * @param {String} time + * @param {String} format ( Modules.ISODate profile format ) + * @return {Object} Modules.ISODate + */ + dateTimeUnion: function(date, time, format) { + var isodate = new modules.ISODate(date, format), + isotime = new modules.ISODate(); + + isotime.parseTime(this.parseAmPmTime(time), format); + if(isodate.hasFullDate() && isotime.hasTime()) { + isodate.tH = isotime.tH; + isodate.tM = isotime.tM; + isodate.tS = isotime.tS; + isodate.tD = isotime.tD; + return isodate; + } else { + if(isodate.hasFullDate()){ + return isodate; + } + return new modules.ISODate(); + } + }, + + + /** + * concatenate an array of date and time text fragments to create an ISODate object + * used for microformat value and value-title rules + * + * @param {Array} arr ( Array of Strings ) + * @param {String} format ( Modules.ISODate profile format ) + * @return {Object} Modules.ISODate + */ + concatFragments: function (arr, format) { + var out = new modules.ISODate(), + i = 0, + value = ''; + + // if the fragment already contains a full date just return it once + if(arr[0].toUpperCase().match('T')) { + return new modules.ISODate(arr[0], format); + }else{ + for(i = 0; i < arr.length; i++) { + value = arr[i]; + + // date pattern + if( value.charAt(4) === '-' && out.hasFullDate() === false ){ + out.parseDate(value); + } + + // time pattern + if( (value.indexOf(':') > -1 || modules.utils.isNumber( this.parseAmPmTime(value) )) && out.hasTime() === false ) { + // split time and timezone + var items = this.splitTimeAndZone(value); + value = items[0]; + + // parse any use of am/pm + value = this.parseAmPmTime(value); + out.parseTime(value); + + // parse any timezone + if(items.length > 1){ + out.parseTimeZone(items[1], format); + } + } + + // timezone pattern + if(value.charAt(0) === '-' || value.charAt(0) === '+' || value.toUpperCase() === 'Z') { + if( out.hasTimeZone() === false ){ + out.parseTimeZone(value); + } + } + + } + return out; + + } + }, + + + /** + * parses text by splitting it into an array of time and timezone strings + * + * @param {String} text + * @return {Array} Modules.ISODate + */ + splitTimeAndZone: function ( text ){ + var out = [text], + chars = ['-','+','z','Z'], + i = chars.length; + + while (i--) { + if(text.indexOf(chars[i]) > -1){ + out[0] = text.slice( 0, text.indexOf(chars[i]) ); + out.push( text.slice( text.indexOf(chars[i]) ) ); + break; + } + } + return out; + } + + }; + + + modules.text = { + + // normalised or whitespace or whitespacetrimmed + textFormat: 'whitespacetrimmed', + + // block level tags, used to add line returns + blockLevelTags: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'hr', 'pre', 'table', + 'address', 'article', 'aside', 'blockquote', 'caption', 'col', 'colgroup', 'dd', 'div', + 'dt', 'dir', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'header', 'hgroup', 'hr', + 'li', 'map', 'menu', 'nav', 'optgroup', 'option', 'section', 'tbody', 'testarea', + 'tfoot', 'th', 'thead', 'tr', 'td', 'ul', 'ol', 'dl', 'details'], + + // tags to exclude + excludeTags: ['noframe', 'noscript', 'template', 'script', 'style', 'frames', 'frameset'], + + + /** + * parses the text from the DOM Node + * + * @param {DOM Node} node + * @param {String} textFormat + * @return {String} + */ + parse: function(doc, node, textFormat){ + var out; + this.textFormat = (textFormat)? textFormat : this.textFormat; + if(this.textFormat === 'normalised'){ + out = this.walkTreeForText( node ); + if(out !== undefined){ + return this.normalise( doc, out ); + }else{ + return ''; + } + }else{ + return this.formatText( doc, modules.domUtils.textContent(node), this.textFormat ); + } + }, + + + /** + * parses the text from a html string + * + * @param {DOM Document} doc + * @param {String} text + * @param {String} textFormat + * @return {String} + */ + parseText: function( doc, text, textFormat ){ + var node = modules.domUtils.createNodeWithText( 'div', text ); + return this.parse( doc, node, textFormat ); + }, + + + /** + * parses the text from a html string - only for whitespace or whitespacetrimmed formats + * + * @param {String} text + * @param {String} textFormat + * @return {String} + */ + formatText: function( doc, text, textFormat ){ + this.textFormat = (textFormat)? textFormat : this.textFormat; + if(text){ + var out = '', + regex = /(<([^>]+)>)/ig; + + out = text.replace(regex, ''); + if(this.textFormat === 'whitespacetrimmed') { + out = modules.utils.trimWhitespace( out ); + } + + //return entities.decode( out, 2 ); + return modules.domUtils.decodeEntities( doc, out ); + }else{ + return ''; + } + }, + + + /** + * normalises whitespace in given text + * + * @param {String} text + * @return {String} + */ + normalise: function( doc, text ){ + text = text.replace( / /g, ' ') ; // exchanges html entity for space into space char + text = modules.utils.collapseWhiteSpace( text ); // removes linefeeds, tabs and addtional spaces + text = modules.domUtils.decodeEntities( doc, text ); // decode HTML entities + text = text.replace( '–', '-' ); // correct dash decoding + return modules.utils.trim( text ); + }, + + + /** + * walks DOM tree parsing the text from DOM Nodes + * + * @param {DOM Node} node + * @return {String} + */ + walkTreeForText: function( node ) { + var out = '', + j = 0; + + if(node.tagName && this.excludeTags.indexOf( node.tagName.toLowerCase() ) > -1){ + return out; + } + + // if node is a text node get its text + if(node.nodeType && node.nodeType === 3){ + out += modules.domUtils.getElementText( node ); + } + + // get the text of the child nodes + if(node.childNodes && node.childNodes.length > 0){ + for (j = 0; j < node.childNodes.length; j++) { + var text = this.walkTreeForText( node.childNodes[j] ); + if(text !== undefined){ + out += text; + } + } + } + + // if it's a block level tag add an additional space at the end + if(node.tagName && this.blockLevelTags.indexOf( node.tagName.toLowerCase() ) !== -1){ + out += ' '; + } + + return (out === '')? undefined : out ; + } + + }; + + + modules.html = { + + // elements which are self-closing + selfClosingElt: ['area', 'base', 'br', 'col', 'hr', 'img', 'input', 'link', 'meta', 'param', 'command', 'keygen', 'source'], + + + /** + * parse the html string from DOM Node + * + * @param {DOM Node} node + * @return {String} + */ + parse: function( node ){ + var out = '', + j = 0; + + // we do not want the outer container + if(node.childNodes && node.childNodes.length > 0){ + for (j = 0; j < node.childNodes.length; j++) { + var text = this.walkTreeForHtml( node.childNodes[j] ); + if(text !== undefined){ + out += text; + } + } + } + + return out; + }, + + + /** + * walks the DOM tree parsing the html string from the nodes + * + * @param {DOM Document} doc + * @param {DOM Node} node + * @return {String} + */ + walkTreeForHtml: function( node ) { + var out = '', + j = 0; + + // if node is a text node get its text + if(node.nodeType && node.nodeType === 3){ + out += modules.domUtils.getElementText( node ); + } + + + // exclude text which has been added with include pattern - + if(node.nodeType && node.nodeType === 1 && modules.domUtils.hasAttribute(node, 'data-include') === false){ + + // begin tag + out += '<' + node.tagName.toLowerCase(); + + // add attributes + var attrs = modules.domUtils.getOrderedAttributes(node); + for (j = 0; j < attrs.length; j++) { + out += ' ' + attrs[j].name + '=' + '"' + attrs[j].value + '"'; + } + + if(this.selfClosingElt.indexOf(node.tagName.toLowerCase()) === -1){ + out += '>'; + } + + // get the text of the child nodes + if(node.childNodes && node.childNodes.length > 0){ + + for (j = 0; j < node.childNodes.length; j++) { + var text = this.walkTreeForHtml( node.childNodes[j] ); + if(text !== undefined){ + out += text; + } + } + } + + // end tag + if(this.selfClosingElt.indexOf(node.tagName.toLowerCase()) > -1){ + out += ' />'; + }else{ + out += ''; + } + } + + return (out === '')? undefined : out; + } + + + }; + + + modules.maps['h-adr'] = { + root: 'adr', + name: 'h-adr', + properties: { + 'post-office-box': {}, + 'street-address': {}, + 'extended-address': {}, + 'locality': {}, + 'region': {}, + 'postal-code': {}, + 'country-name': {} + } + }; + + + modules.maps['h-card'] = { + root: 'vcard', + name: 'h-card', + properties: { + 'fn': { + 'map': 'p-name' + }, + 'adr': { + 'map': 'p-adr', + 'uf': ['h-adr'] + }, + 'agent': { + 'uf': ['h-card'] + }, + 'bday': { + 'map': 'dt-bday' + }, + 'class': {}, + 'category': { + 'map': 'p-category', + 'relAlt': ['tag'] + }, + 'email': { + 'map': 'u-email' + }, + 'geo': { + 'map': 'p-geo', + 'uf': ['h-geo'] + }, + 'key': { + 'map': 'u-key' + }, + 'label': {}, + 'logo': { + 'map': 'u-logo' + }, + 'mailer': {}, + 'honorific-prefix': {}, + 'given-name': {}, + 'additional-name': {}, + 'family-name': {}, + 'honorific-suffix': {}, + 'nickname': {}, + 'note': {}, // could be html i.e. e-note + 'org': {}, + 'p-organization-name': {}, + 'p-organization-unit': {}, + 'photo': { + 'map': 'u-photo' + }, + 'rev': { + 'map': 'dt-rev' + }, + 'role': {}, + 'sequence': {}, + 'sort-string': {}, + 'sound': { + 'map': 'u-sound' + }, + 'title': { + 'map': 'p-job-title' + }, + 'tel': {}, + 'tz': {}, + 'uid': { + 'map': 'u-uid' + }, + 'url': { + 'map': 'u-url' + } + } + }; + + + modules.maps['h-entry'] = { + root: 'hentry', + name: 'h-entry', + properties: { + 'entry-title': { + 'map': 'p-name' + }, + 'entry-summary': { + 'map': 'p-summary' + }, + 'entry-content': { + 'map': 'e-content' + }, + 'published': { + 'map': 'dt-published' + }, + 'updated': { + 'map': 'dt-updated' + }, + 'author': { + 'uf': ['h-card'] + }, + 'category': { + 'map': 'p-category', + 'relAlt': ['tag'] + }, + 'geo': { + 'map': 'p-geo', + 'uf': ['h-geo'] + }, + 'latitude': {}, + 'longitude': {}, + 'url': { + 'map': 'u-url', + 'relAlt': ['bookmark'] + } + } + }; + + + modules.maps['h-event'] = { + root: 'vevent', + name: 'h-event', + properties: { + 'summary': { + 'map': 'p-name' + }, + 'dtstart': { + 'map': 'dt-start' + }, + 'dtend': { + 'map': 'dt-end' + }, + 'description': {}, + 'url': { + 'map': 'u-url' + }, + 'category': { + 'map': 'p-category', + 'relAlt': ['tag'] + }, + 'location': { + 'uf': ['h-card'] + }, + 'geo': { + 'uf': ['h-geo'] + }, + 'latitude': {}, + 'longitude': {}, + 'duration': { + 'map': 'dt-duration' + }, + 'contact': { + 'uf': ['h-card'] + }, + 'organizer': { + 'uf': ['h-card']}, + 'attendee': { + 'uf': ['h-card']}, + 'uid': { + 'map': 'u-uid' + }, + 'attach': { + 'map': 'u-attach' + }, + 'status': {}, + 'rdate': {}, + 'rrule': {} + } + }; + + + modules.maps['h-feed'] = { + root: 'hfeed', + name: 'h-feed', + properties: { + 'category': { + 'map': 'p-category', + 'relAlt': ['tag'] + }, + 'summary': { + 'map': 'p-summary' + }, + 'author': { + 'uf': ['h-card'] + }, + 'url': { + 'map': 'u-url' + }, + 'photo': { + 'map': 'u-photo' + }, + } + }; + + + modules.maps['h-geo'] = { + root: 'geo', + name: 'h-geo', + properties: { + 'latitude': {}, + 'longitude': {} + } + }; + + + modules.maps['h-item'] = { + root: 'item', + name: 'h-item', + subTree: false, + properties: { + 'fn': { + 'map': 'p-name' + }, + 'url': { + 'map': 'u-url' + }, + 'photo': { + 'map': 'u-photo' + } + } + }; + + + modules.maps['h-listing'] = { + root: 'hlisting', + name: 'h-listing', + properties: { + 'version': {}, + 'lister': { + 'uf': ['h-card'] + }, + 'dtlisted': { + 'map': 'dt-listed' + }, + 'dtexpired': { + 'map': 'dt-expired' + }, + 'location': {}, + 'price': {}, + 'item': { + 'uf': ['h-card','a-adr','h-geo'] + }, + 'summary': { + 'map': 'p-name' + }, + 'description': { + 'map': 'e-description' + }, + 'listing': {} + } + }; + + + modules.maps['h-news'] = { + root: 'hnews', + name: 'h-news', + properties: { + 'entry': { + 'uf': ['h-entry'] + }, + 'geo': { + 'uf': ['h-geo'] + }, + 'latitude': {}, + 'longitude': {}, + 'source-org': { + 'uf': ['h-card'] + }, + 'dateline': { + 'uf': ['h-card'] + }, + 'item-license': { + 'map': 'u-item-license' + }, + 'principles': { + 'map': 'u-principles', + 'relAlt': ['principles'] + } + } + }; + + + modules.maps['h-org'] = { + root: 'h-x-org', // drop this from v1 as it causes issue with fn org hcard pattern + name: 'h-org', + childStructure: true, + properties: { + 'organization-name': {}, + 'organization-unit': {} + } + }; + + + modules.maps['h-product'] = { + root: 'hproduct', + name: 'h-product', + properties: { + 'brand': { + 'uf': ['h-card'] + }, + 'category': { + 'map': 'p-category', + 'relAlt': ['tag'] + }, + 'price': {}, + 'description': { + 'map': 'e-description' + }, + 'fn': { + 'map': 'p-name' + }, + 'photo': { + 'map': 'u-photo' + }, + 'url': { + 'map': 'u-url' + }, + 'review': { + 'uf': ['h-review', 'h-review-aggregate'] + }, + 'listing': { + 'uf': ['h-listing'] + }, + 'identifier': { + 'map': 'u-identifier' + } + } + }; + + + modules.maps['h-recipe'] = { + root: 'hrecipe', + name: 'h-recipe', + properties: { + 'fn': { + 'map': 'p-name' + }, + 'ingredient': { + 'map': 'e-ingredient' + }, + 'yield': {}, + 'instructions': { + 'map': 'e-instructions' + }, + 'duration': { + 'map': 'dt-duration' + }, + 'photo': { + 'map': 'u-photo' + }, + 'summary': {}, + 'author': { + 'uf': ['h-card'] + }, + 'published': { + 'map': 'dt-published' + }, + 'nutrition': {}, + 'category': { + 'map': 'p-category', + 'relAlt': ['tag'] + }, + } + }; + + + modules.maps['h-resume'] = { + root: 'hresume', + name: 'h-resume', + properties: { + 'summary': {}, + 'contact': { + 'uf': ['h-card'] + }, + 'education': { + 'uf': ['h-card', 'h-event'] + }, + 'experience': { + 'uf': ['h-card', 'h-event'] + }, + 'skill': {}, + 'affiliation': { + 'uf': ['h-card'] + } + } + }; + + + modules.maps['h-review-aggregate'] = { + root: 'hreview-aggregate', + name: 'h-review-aggregate', + properties: { + 'summary': { + 'map': 'p-name' + }, + 'item': { + 'map': 'p-item', + 'uf': ['h-item', 'h-geo', 'h-adr', 'h-card', 'h-event', 'h-product'] + }, + 'rating': {}, + 'average': {}, + 'best': {}, + 'worst': {}, + 'count': {}, + 'votes': {}, + 'category': { + 'map': 'p-category', + 'relAlt': ['tag'] + }, + 'url': { + 'map': 'u-url', + 'relAlt': ['self', 'bookmark'] + } + } + }; + + + modules.maps['h-review'] = { + root: 'hreview', + name: 'h-review', + properties: { + 'summary': { + 'map': 'p-name' + }, + 'description': { + 'map': 'e-description' + }, + 'item': { + 'map': 'p-item', + 'uf': ['h-item', 'h-geo', 'h-adr', 'h-card', 'h-event', 'h-product'] + }, + 'reviewer': { + 'uf': ['h-card'] + }, + 'dtreviewer': { + 'map': 'dt-reviewer' + }, + 'rating': {}, + 'best': {}, + 'worst': {}, + 'category': { + 'map': 'p-category', + 'relAlt': ['tag'] + }, + 'url': { + 'map': 'u-url', + 'relAlt': ['self', 'bookmark'] + } + } + }; + + + modules.rels = { + // xfn + 'friend': [ 'yes','external'], + 'acquaintance': [ 'yes','external'], + 'contact': [ 'yes','external'], + 'met': [ 'yes','external'], + 'co-worker': [ 'yes','external'], + 'colleague': [ 'yes','external'], + 'co-resident': [ 'yes','external'], + 'neighbor': [ 'yes','external'], + 'child': [ 'yes','external'], + 'parent': [ 'yes','external'], + 'sibling': [ 'yes','external'], + 'spouse': [ 'yes','external'], + 'kin': [ 'yes','external'], + 'muse': [ 'yes','external'], + 'crush': [ 'yes','external'], + 'date': [ 'yes','external'], + 'sweetheart': [ 'yes','external'], + 'me': [ 'yes','external'], + + // other rel=* + 'license': [ 'yes','yes'], + 'nofollow': [ 'no','external'], + 'tag': [ 'no','yes'], + 'self': [ 'no','external'], + 'bookmark': [ 'no','external'], + 'author': [ 'no','external'], + 'home': [ 'no','external'], + 'directory': [ 'no','external'], + 'enclosure': [ 'no','external'], + 'pronunciation': [ 'no','external'], + 'payment': [ 'no','external'], + 'principles': [ 'no','external'] + + }; @@ -4492,48 +4492,48 @@ var Microformats; // jshint ignore:line External.get = function(options){ - var parser = new modules.Parser(); + var parser = new modules.Parser(); addV1(parser, options); - return parser.get( options ); + return parser.get( options ); }; External.getParent = function(node, options){ - var parser = new modules.Parser(); + var parser = new modules.Parser(); addV1(parser, options); - return parser.getParent( node, options ); + return parser.getParent( node, options ); }; External.count = function(options){ - var parser = new modules.Parser(); + var parser = new modules.Parser(); addV1(parser, options); - return parser.count( options ); + return parser.count( options ); }; External.isMicroformat = function( node, options ){ - var parser = new modules.Parser(); + var parser = new modules.Parser(); addV1(parser, options); - return parser.isMicroformat( node, options ); + return parser.isMicroformat( node, options ); }; External.hasMicroformats = function( node, options ){ - var parser = new modules.Parser(); + var parser = new modules.Parser(); addV1(parser, options); - return parser.hasMicroformats( node, options ); + return parser.hasMicroformats( node, options ); }; function addV1(parser, options){ - if(options && options.maps){ - if(Array.isArray(options.maps)){ - parser.add(options.maps); - }else{ - parser.add([options.maps]); - } - } + if(options && options.maps){ + if(Array.isArray(options.maps)){ + parser.add(options.maps); + }else{ + parser.add([options.maps]); + } + } } diff --git a/toolkit/components/microformats/test/interface-tests/get-test.js b/toolkit/components/microformats/test/interface-tests/get-test.js index f6a96dcdb3..0e8c3ca3dc 100644 --- a/toolkit/components/microformats/test/interface-tests/get-test.js +++ b/toolkit/components/microformats/test/interface-tests/get-test.js @@ -547,12 +547,12 @@ describe('Microformat.get', function() { 'rel-urls': {} }; var v1Definition = { - root: 'hpayment', - name: 'h-payment', - properties: { - 'amount': {} - } - }; + root: 'hpayment', + name: 'h-payment', + properties: { + 'amount': {} + } + }; doc = document.implementation.createHTMLDocument('New Document'); diff --git a/toolkit/components/microformats/test/interface-tests/index.html b/toolkit/components/microformats/test/interface-tests/index.html index 19b499ae1e..0622d5bec9 100644 --- a/toolkit/components/microformats/test/interface-tests/index.html +++ b/toolkit/components/microformats/test/interface-tests/index.html @@ -1,68 +1,68 @@ -Mocha - - - - - - - - - - - - - - - - - - - - - - - - -

Microformats-shiv: interface tests

-
- - - +Mocha + + + + + + + + + + + + + + + + + + + + + + + + +

Microformats-shiv: interface tests

+
+ + + diff --git a/toolkit/components/microformats/test/lib/domutils.js b/toolkit/components/microformats/test/lib/domutils.js index c236c01bd5..57269de978 100644 --- a/toolkit/components/microformats/test/lib/domutils.js +++ b/toolkit/components/microformats/test/lib/domutils.js @@ -13,18 +13,18 @@ var Modules = (function (modules) { - modules.domUtils = { + modules.domUtils = { - // blank objects for DOM - document: null, - rootNode: null, + // blank objects for DOM + document: null, + rootNode: null, - /** - * gets DOMParser object - * + /** + * gets DOMParser object + * * @return {Object || undefined} - */ + */ getDOMParser: function () { if (typeof DOMParser === undefined) { try { @@ -39,573 +39,573 @@ var Modules = (function (modules) { }, - /** - * configures what are the base DOM objects for parsing - * - * @param {Object} options - * @return {DOM Node} node - */ - getDOMContext: function( options ){ + /** + * configures what are the base DOM objects for parsing + * + * @param {Object} options + * @return {DOM Node} node + */ + getDOMContext: function( options ){ - // if a node is passed - if(options.node){ - this.rootNode = options.node; - } + // if a node is passed + if(options.node){ + this.rootNode = options.node; + } - // if a html string is passed - if(options.html){ - //var domParser = new DOMParser(); + // if a html string is passed + if(options.html){ + //var domParser = new DOMParser(); var domParser = this.getDOMParser(); - this.rootNode = domParser.parseFromString( options.html, 'text/html' ); - } + this.rootNode = domParser.parseFromString( options.html, 'text/html' ); + } - // find top level document from rootnode - if(this.rootNode !== null){ - if(this.rootNode.nodeType === 9){ - this.document = this.rootNode; - this.rootNode = modules.domUtils.querySelector(this.rootNode, 'html'); - }else{ - // if it's DOM node get parent DOM Document - this.document = modules.domUtils.ownerDocument(this.rootNode); - } - } + // find top level document from rootnode + if(this.rootNode !== null){ + if(this.rootNode.nodeType === 9){ + this.document = this.rootNode; + this.rootNode = modules.domUtils.querySelector(this.rootNode, 'html'); + }else{ + // if it's DOM node get parent DOM Document + this.document = modules.domUtils.ownerDocument(this.rootNode); + } + } - // use global document object - if(!this.rootNode && document){ - this.rootNode = modules.domUtils.querySelector(document, 'html'); - this.document = document; - } + // use global document object + if(!this.rootNode && document){ + this.rootNode = modules.domUtils.querySelector(document, 'html'); + this.document = document; + } - if(this.rootNode && this.document){ - return {document: this.document, rootNode: this.rootNode}; - } + if(this.rootNode && this.document){ + return {document: this.document, rootNode: this.rootNode}; + } - return {document: null, rootNode: null}; - }, + return {document: null, rootNode: null}; + }, - /** - * gets the first DOM node - * - * @param {Dom Document} - * @return {DOM Node} node - */ - getTopMostNode: function( node ){ - //var doc = this.ownerDocument(node); - //if(doc && doc.nodeType && doc.nodeType === 9 && doc.documentElement){ - // return doc.documentElement; - //} - return node; - }, + /** + * gets the first DOM node + * + * @param {Dom Document} + * @return {DOM Node} node + */ + getTopMostNode: function( node ){ + //var doc = this.ownerDocument(node); + //if(doc && doc.nodeType && doc.nodeType === 9 && doc.documentElement){ + // return doc.documentElement; + //} + return node; + }, - /** - * abstracts DOM ownerDocument - * - * @param {DOM Node} node - * @return {Dom Document} - */ - ownerDocument: function(node){ - return node.ownerDocument; - }, - - - /** - * abstracts DOM textContent - * - * @param {DOM Node} node - * @return {String} - */ - textContent: function(node){ - if(node.textContent){ - return node.textContent; - }else if(node.innerText){ - return node.innerText; - } - return ''; - }, - - - /** - * abstracts DOM innerHTML - * - * @param {DOM Node} node - * @return {String} - */ - innerHTML: function(node){ - return node.innerHTML; - }, - - - /** - * abstracts DOM hasAttribute - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {Boolean} - */ - hasAttribute: function(node, attributeName) { - return node.hasAttribute(attributeName); - }, - - - /** - * does an attribute contain a value - * - * @param {DOM Node} node - * @param {String} attributeName - * @param {String} value - * @return {Boolean} - */ - hasAttributeValue: function(node, attributeName, value) { - return (this.getAttributeList(node, attributeName).indexOf(value) > -1); - }, - - - /** - * abstracts DOM getAttribute - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {String || null} - */ - getAttribute: function(node, attributeName) { - return node.getAttribute(attributeName); - }, - - - /** - * abstracts DOM setAttribute - * - * @param {DOM Node} node - * @param {String} attributeName - * @param {String} attributeValue - */ - setAttribute: function(node, attributeName, attributeValue){ - node.setAttribute(attributeName, attributeValue); - }, - - - /** - * abstracts DOM removeAttribute - * - * @param {DOM Node} node - * @param {String} attributeName - */ - removeAttribute: function(node, attributeName) { - node.removeAttribute(attributeName); - }, - - - /** - * abstracts DOM getElementById - * - * @param {DOM Node || DOM Document} node - * @param {String} id - * @return {DOM Node} - */ - getElementById: function(docNode, id) { - return docNode.querySelector( '#' + id ); - }, - - - /** - * abstracts DOM querySelector - * - * @param {DOM Node || DOM Document} node - * @param {String} selector - * @return {DOM Node} - */ - querySelector: function(docNode, selector) { - return docNode.querySelector( selector ); - }, - - - /** - * get value of a Node attribute as an array - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {Array} - */ - getAttributeList: function(node, attributeName) { - var out = [], - attList; - - attList = node.getAttribute(attributeName); - if(attList && attList !== '') { - if(attList.indexOf(' ') > -1) { - out = attList.split(' '); - } else { - out.push(attList); - } - } - return out; - }, - - - /** - * gets all child nodes with a given attribute - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {NodeList} - */ - getNodesByAttribute: function(node, attributeName) { - var selector = '[' + attributeName + ']'; - return node.querySelectorAll(selector); - }, - - - /** - * gets all child nodes with a given attribute containing a given value - * - * @param {DOM Node} node - * @param {String} attributeName - * @return {DOM NodeList} - */ - getNodesByAttributeValue: function(rootNode, name, value) { - var arr = [], - x = 0, - i, - out = []; - - arr = this.getNodesByAttribute(rootNode, name); - if(arr) { - i = arr.length; - while(x < i) { - if(this.hasAttributeValue(arr[x], name, value)) { - out.push(arr[x]); - } - x++; - } - } - return out; - }, - - - /** - * gets attribute value from controlled list of tags - * - * @param {Array} tagNames - * @param {String} attributeName - * @return {String || null} - */ - getAttrValFromTagList: function(node, tagNames, attributeName) { - var i = tagNames.length; - - while(i--) { - if(node.tagName.toLowerCase() === tagNames[i]) { - var attrValue = this.getAttribute(node, attributeName); - if(attrValue && attrValue !== '') { - return attrValue; - } - } - } - return null; - }, - - - /** - * get node if it has no siblings. CSS equivalent is :only-child - * - * @param {DOM Node} rootNode - * @param {Array} tagNames - * @return {DOM Node || null} - */ - getSingleDescendant: function(node){ - return this.getDescendant( node, null, false ); - }, - - /** - * get node if it has no siblings of the same type. CSS equivalent is :only-of-type - * - * @param {DOM Node} rootNode - * @param {Array} tagNames - * @return {DOM Node || null} - */ - getSingleDescendantOfType: function(node, tagNames){ - return this.getDescendant( node, tagNames, true ); - }, + * abstracts DOM ownerDocument + * + * @param {DOM Node} node + * @return {Dom Document} + */ + ownerDocument: function(node){ + return node.ownerDocument; + }, - /** - * get child node limited by presence of siblings - either CSS :only-of-type or :only-child - * - * @param {DOM Node} rootNode - * @param {Array} tagNames - * @return {DOM Node || null} - */ - getDescendant: function( node, tagNames, onlyOfType ){ - var i = node.children.length, - countAll = 0, - countOfType = 0, - child, - out = null; - - while(i--) { - child = node.children[i]; - if(child.nodeType === 1) { - if(tagNames){ - // count just only-of-type - if(this.hasTagName(child, tagNames)){ - out = child; - countOfType++; - } - }else{ - // count all elements - out = child; - countAll++; - } - } - } - if(onlyOfType === true){ - return (countOfType === 1)? out : null; - }else{ - return (countAll === 1)? out : null; - } - }, + /** + * abstracts DOM textContent + * + * @param {DOM Node} node + * @return {String} + */ + textContent: function(node){ + if(node.textContent){ + return node.textContent; + }else if(node.innerText){ + return node.innerText; + } + return ''; + }, - /** - * is a node one of a list of tags - * - * @param {DOM Node} rootNode - * @param {Array} tagNames - * @return {Boolean} - */ - hasTagName: function(node, tagNames){ - var i = tagNames.length; - while(i--) { - if(node.tagName.toLowerCase() === tagNames[i]) { - return true; - } - } - return false; - }, + /** + * abstracts DOM innerHTML + * + * @param {DOM Node} node + * @return {String} + */ + innerHTML: function(node){ + return node.innerHTML; + }, - /** - * abstracts DOM appendChild - * - * @param {DOM Node} node - * @param {DOM Node} childNode - * @return {DOM Node} - */ - appendChild: function(node, childNode){ - return node.appendChild(childNode); - }, + /** + * abstracts DOM hasAttribute + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {Boolean} + */ + hasAttribute: function(node, attributeName) { + return node.hasAttribute(attributeName); + }, - /** - * abstracts DOM removeChild - * - * @param {DOM Node} childNode - * @return {DOM Node || null} - */ - removeChild: function(childNode){ - if (childNode.parentNode) { - return childNode.parentNode.removeChild(childNode); - }else{ - return null; - } - }, + /** + * does an attribute contain a value + * + * @param {DOM Node} node + * @param {String} attributeName + * @param {String} value + * @return {Boolean} + */ + hasAttributeValue: function(node, attributeName, value) { + return (this.getAttributeList(node, attributeName).indexOf(value) > -1); + }, - /** - * abstracts DOM cloneNode - * - * @param {DOM Node} node - * @return {DOM Node} - */ - clone: function(node) { - var newNode = node.cloneNode(true); - newNode.removeAttribute('id'); - return newNode; - }, + /** + * abstracts DOM getAttribute + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {String || null} + */ + getAttribute: function(node, attributeName) { + return node.getAttribute(attributeName); + }, - /** - * gets the text of a node - * - * @param {DOM Node} node - * @return {String} - */ - getElementText: function( node ){ - if(node && node.data){ - return node.data; - }else{ - return ''; - } - }, + /** + * abstracts DOM setAttribute + * + * @param {DOM Node} node + * @param {String} attributeName + * @param {String} attributeValue + */ + setAttribute: function(node, attributeName, attributeValue){ + node.setAttribute(attributeName, attributeValue); + }, - /** - * gets the attributes of a node - ordered by sequence in html - * - * @param {DOM Node} node - * @return {Array} - */ - getOrderedAttributes: function( node ){ - var nodeStr = node.outerHTML, - attrs = []; - - for (var i = 0; i < node.attributes.length; i++) { - var attr = node.attributes[i]; - attr.indexNum = nodeStr.indexOf(attr.name); - - attrs.push( attr ); - } - return attrs.sort( modules.utils.sortObjects( 'indexNum' ) ); - }, + /** + * abstracts DOM removeAttribute + * + * @param {DOM Node} node + * @param {String} attributeName + */ + removeAttribute: function(node, attributeName) { + node.removeAttribute(attributeName); + }, - /** - * decodes html entities in given text - * - * @param {DOM Document} doc - * @param String} text - * @return {String} - */ - decodeEntities: function( doc, text ){ - //return text; - return doc.createTextNode( text ).nodeValue; - }, + /** + * abstracts DOM getElementById + * + * @param {DOM Node || DOM Document} node + * @param {String} id + * @return {DOM Node} + */ + getElementById: function(docNode, id) { + return docNode.querySelector( '#' + id ); + }, - /** - * clones a DOM document - * - * @param {DOM Document} document - * @return {DOM Document} - */ - cloneDocument: function( document ){ - var newNode, - newDocument = null; - - if( this.canCloneDocument( document )){ - newDocument = document.implementation.createHTMLDocument(''); - newNode = newDocument.importNode( document.documentElement, true ); - newDocument.replaceChild(newNode, newDocument.querySelector('html')); - } - return (newNode && newNode.nodeType && newNode.nodeType === 1)? newDocument : document; - }, + /** + * abstracts DOM querySelector + * + * @param {DOM Node || DOM Document} node + * @param {String} selector + * @return {DOM Node} + */ + querySelector: function(docNode, selector) { + return docNode.querySelector( selector ); + }, - /** - * can environment clone a DOM document - * - * @param {DOM Document} document - * @return {Boolean} - */ - canCloneDocument: function( document ){ - return (document && document.importNode && document.implementation && document.implementation.createHTMLDocument); - }, + /** + * get value of a Node attribute as an array + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {Array} + */ + getAttributeList: function(node, attributeName) { + var out = [], + attList; + + attList = node.getAttribute(attributeName); + if(attList && attList !== '') { + if(attList.indexOf(' ') > -1) { + out = attList.split(' '); + } else { + out.push(attList); + } + } + return out; + }, - /** - * get the child index of a node. Used to create a node path - * - * @param {DOM Node} node - * @return {Int} - */ - getChildIndex: function (node) { - var parent = node.parentNode, - i = -1, - child; - while (parent && (child = parent.childNodes[++i])){ - if (child === node){ - return i; - } - } - return -1; - }, + /** + * gets all child nodes with a given attribute + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {NodeList} + */ + getNodesByAttribute: function(node, attributeName) { + var selector = '[' + attributeName + ']'; + return node.querySelectorAll(selector); + }, - /** - * get a node's path - * - * @param {DOM Node} node - * @return {Array} - */ - getNodePath: function (node) { - var parent = node.parentNode, - path = [], - index = this.getChildIndex(node); + /** + * gets all child nodes with a given attribute containing a given value + * + * @param {DOM Node} node + * @param {String} attributeName + * @return {DOM NodeList} + */ + getNodesByAttributeValue: function(rootNode, name, value) { + var arr = [], + x = 0, + i, + out = []; - if(parent && (path = this.getNodePath(parent))){ - if(index > -1){ - path.push(index); - } - } - return path; - }, + arr = this.getNodesByAttribute(rootNode, name); + if(arr) { + i = arr.length; + while(x < i) { + if(this.hasAttributeValue(arr[x], name, value)) { + out.push(arr[x]); + } + x++; + } + } + return out; + }, - /** - * get a node from a path. - * - * @param {DOM document} document - * @param {Array} path - * @return {DOM Node} - */ - getNodeByPath: function (document, path) { - var node = document.documentElement, - i = 0, - index; - while ((index = path[++i]) > -1){ - node = node.childNodes[index]; - } - return node; - }, + /** + * gets attribute value from controlled list of tags + * + * @param {Array} tagNames + * @param {String} attributeName + * @return {String || null} + */ + getAttrValFromTagList: function(node, tagNames, attributeName) { + var i = tagNames.length; + + while(i--) { + if(node.tagName.toLowerCase() === tagNames[i]) { + var attrValue = this.getAttribute(node, attributeName); + if(attrValue && attrValue !== '') { + return attrValue; + } + } + } + return null; + }, - /** - * get an array/nodeList of child nodes - * - * @param {DOM node} node - * @return {Array} - */ - getChildren: function( node ){ - return node.children; - }, + /** + * get node if it has no siblings. CSS equivalent is :only-child + * + * @param {DOM Node} rootNode + * @param {Array} tagNames + * @return {DOM Node || null} + */ + getSingleDescendant: function(node){ + return this.getDescendant( node, null, false ); + }, - /** - * create a node - * - * @param {String} tagName - * @return {DOM node} - */ - createNode: function( tagName ){ - return this.document.createElement(tagName); - }, + /** + * get node if it has no siblings of the same type. CSS equivalent is :only-of-type + * + * @param {DOM Node} rootNode + * @param {Array} tagNames + * @return {DOM Node || null} + */ + getSingleDescendantOfType: function(node, tagNames){ + return this.getDescendant( node, tagNames, true ); + }, - /** - * create a node with text content - * - * @param {String} tagName - * @param {String} text - * @return {DOM node} - */ - createNodeWithText: function( tagName, text ){ - var node = this.document.createElement(tagName); - node.innerHTML = text; - return node; - } + /** + * get child node limited by presence of siblings - either CSS :only-of-type or :only-child + * + * @param {DOM Node} rootNode + * @param {Array} tagNames + * @return {DOM Node || null} + */ + getDescendant: function( node, tagNames, onlyOfType ){ + var i = node.children.length, + countAll = 0, + countOfType = 0, + child, + out = null; + + while(i--) { + child = node.children[i]; + if(child.nodeType === 1) { + if(tagNames){ + // count just only-of-type + if(this.hasTagName(child, tagNames)){ + out = child; + countOfType++; + } + }else{ + // count all elements + out = child; + countAll++; + } + } + } + if(onlyOfType === true){ + return (countOfType === 1)? out : null; + }else{ + return (countAll === 1)? out : null; + } + }, + + + /** + * is a node one of a list of tags + * + * @param {DOM Node} rootNode + * @param {Array} tagNames + * @return {Boolean} + */ + hasTagName: function(node, tagNames){ + var i = tagNames.length; + while(i--) { + if(node.tagName.toLowerCase() === tagNames[i]) { + return true; + } + } + return false; + }, + + + /** + * abstracts DOM appendChild + * + * @param {DOM Node} node + * @param {DOM Node} childNode + * @return {DOM Node} + */ + appendChild: function(node, childNode){ + return node.appendChild(childNode); + }, + + + /** + * abstracts DOM removeChild + * + * @param {DOM Node} childNode + * @return {DOM Node || null} + */ + removeChild: function(childNode){ + if (childNode.parentNode) { + return childNode.parentNode.removeChild(childNode); + }else{ + return null; + } + }, + + + /** + * abstracts DOM cloneNode + * + * @param {DOM Node} node + * @return {DOM Node} + */ + clone: function(node) { + var newNode = node.cloneNode(true); + newNode.removeAttribute('id'); + return newNode; + }, + + + /** + * gets the text of a node + * + * @param {DOM Node} node + * @return {String} + */ + getElementText: function( node ){ + if(node && node.data){ + return node.data; + }else{ + return ''; + } + }, + + + /** + * gets the attributes of a node - ordered by sequence in html + * + * @param {DOM Node} node + * @return {Array} + */ + getOrderedAttributes: function( node ){ + var nodeStr = node.outerHTML, + attrs = []; + + for (var i = 0; i < node.attributes.length; i++) { + var attr = node.attributes[i]; + attr.indexNum = nodeStr.indexOf(attr.name); + + attrs.push( attr ); + } + return attrs.sort( modules.utils.sortObjects( 'indexNum' ) ); + }, + + + /** + * decodes html entities in given text + * + * @param {DOM Document} doc + * @param String} text + * @return {String} + */ + decodeEntities: function( doc, text ){ + //return text; + return doc.createTextNode( text ).nodeValue; + }, + + + /** + * clones a DOM document + * + * @param {DOM Document} document + * @return {DOM Document} + */ + cloneDocument: function( document ){ + var newNode, + newDocument = null; + + if( this.canCloneDocument( document )){ + newDocument = document.implementation.createHTMLDocument(''); + newNode = newDocument.importNode( document.documentElement, true ); + newDocument.replaceChild(newNode, newDocument.querySelector('html')); + } + return (newNode && newNode.nodeType && newNode.nodeType === 1)? newDocument : document; + }, + + + /** + * can environment clone a DOM document + * + * @param {DOM Document} document + * @return {Boolean} + */ + canCloneDocument: function( document ){ + return (document && document.importNode && document.implementation && document.implementation.createHTMLDocument); + }, + + + /** + * get the child index of a node. Used to create a node path + * + * @param {DOM Node} node + * @return {Int} + */ + getChildIndex: function (node) { + var parent = node.parentNode, + i = -1, + child; + while (parent && (child = parent.childNodes[++i])){ + if (child === node){ + return i; + } + } + return -1; + }, + + + /** + * get a node's path + * + * @param {DOM Node} node + * @return {Array} + */ + getNodePath: function (node) { + var parent = node.parentNode, + path = [], + index = this.getChildIndex(node); + + if(parent && (path = this.getNodePath(parent))){ + if(index > -1){ + path.push(index); + } + } + return path; + }, + + + /** + * get a node from a path. + * + * @param {DOM document} document + * @param {Array} path + * @return {DOM Node} + */ + getNodeByPath: function (document, path) { + var node = document.documentElement, + i = 0, + index; + while ((index = path[++i]) > -1){ + node = node.childNodes[index]; + } + return node; + }, + + + /** + * get an array/nodeList of child nodes + * + * @param {DOM node} node + * @return {Array} + */ + getChildren: function( node ){ + return node.children; + }, + + + /** + * create a node + * + * @param {String} tagName + * @return {DOM node} + */ + createNode: function( tagName ){ + return this.document.createElement(tagName); + }, + + + /** + * create a node with text content + * + * @param {String} tagName + * @param {String} text + * @return {DOM node} + */ + createNodeWithText: function( tagName, text ){ + var node = this.document.createElement(tagName); + node.innerHTML = text; + return node; + } - }; + }; - return modules; + return modules; } (Modules || {})); diff --git a/toolkit/components/microformats/test/module-tests/index.html b/toolkit/components/microformats/test/module-tests/index.html index faea486a38..df60ba7344 100644 --- a/toolkit/components/microformats/test/module-tests/index.html +++ b/toolkit/components/microformats/test/module-tests/index.html @@ -1,76 +1,76 @@ -Mocha - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Microformats-shiv: module tests

-
- - - +Mocha + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Microformats-shiv: module tests

+
+ + + diff --git a/toolkit/components/microformats/test/standards-tests/index.html b/toolkit/components/microformats/test/standards-tests/index.html index 01cf967232..9758547d7a 100644 --- a/toolkit/components/microformats/test/standards-tests/index.html +++ b/toolkit/components/microformats/test/standards-tests/index.html @@ -1,178 +1,178 @@ -Mocha - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Microformats-shiv: standards tests

-

Standards tests built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST). Downloaded from github repo: microformats/tests version v0.1.24

-
- - - +Mocha + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Microformats-shiv: standards tests

+

Standards tests built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST). Downloaded from github repo: microformats/tests version v0.1.24

+
+ + + diff --git a/toolkit/components/microformats/test/standards-tests/mf-mixed-h-card-mixedpropertries.js b/toolkit/components/microformats/test/standards-tests/mf-mixed-h-card-mixedpropertries.js index cf6ca9ea1a..db99dc92a2 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-mixed-h-card-mixedpropertries.js +++ b/toolkit/components/microformats/test/standards-tests/mf-mixed-h-card-mixedpropertries.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-mixed/h-card/mixedpropertries -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "
\n

\n Mozilla Foundation\n \n

\n

\n 665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A. \n

\n
"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mozilla Foundation"],"org":["Mozilla Foundation"],"url":["http://mozilla.org/"],"adr":[{"value":"665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A.","type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"]}}]}}],"rels":{},"rel-urls":{}}; - - it('mixedpropertries', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-mixed/h-card/mixedpropertries +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "
\n

\n Mozilla Foundation\n \n

\n

\n 665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A. \n

\n
"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mozilla Foundation"],"org":["Mozilla Foundation"],"url":["http://mozilla.org/"],"adr":[{"value":"665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A.","type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"]}}]}}],"rels":{},"rel-urls":{}}; + + it('mixedpropertries', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-mixed-h-card-tworoots.js b/toolkit/components/microformats/test/standards-tests/mf-mixed-h-card-tworoots.js index 414d3db1bc..be43abcd86 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-mixed-h-card-tworoots.js +++ b/toolkit/components/microformats/test/standards-tests/mf-mixed-h-card-tworoots.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-mixed/h-card/tworoots -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "

Frances Berriman

"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Frances Berriman"]}}],"rels":{},"rel-urls":{}}; - - it('tworoots', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-mixed/h-card/tworoots +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "

Frances Berriman

"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Frances Berriman"]}}],"rels":{},"rel-urls":{}}; + + it('tworoots', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-mixed-h-entry-mixedroots.js b/toolkit/components/microformats/test/standards-tests/mf-mixed-h-entry-mixedroots.js index c606a19b3c..705ffeebf2 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-mixed-h-entry-mixedroots.js +++ b/toolkit/components/microformats/test/standards-tests/mf-mixed-h-entry-mixedroots.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-mixed/h-entry/mixedroots -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-entry', function() { - var htmlFragment = "\n\n\n
\n \n
Did you play\n @playmapattackat\n #realtimeconf? Here is some more info about how we built it!\n http://pdx.esri.com/blog/2013/10/17/introducting-mapattack/\n
\n
"; - var expected = {"items":[{"type":["h-entry"],"properties":{"author":[{"value":"aaronparecki.com\n Aaron Parecki\n Aaron Parecki","type":["h-card"],"properties":{"photo":["https://aaronparecki.com/images/aaronpk.png"],"logo":["https://aaronparecki.com/images/aaronpk.png"],"url":["https://aaronparecki.com/"],"uid":["https://aaronparecki.com/"],"name":["Aaron Parecki"]}}],"content":[{"value":"Did you play\n @playmapattackat\n #realtimeconf? Here is some more info about how we built it!\n http://pdx.esri.com/blog/2013/10/17/introducting-mapattack/","html":"Did you play\n @playmapattackat\n #realtimeconf? Here is some more info about how we built it!\n http://pdx.esri.com/blog/2013/10/17/introducting-mapattack/\n "}],"name":["Did you play\n @playmapattackat\n #realtimeconf? Here is some more info about how we built it!\n http://pdx.esri.com/blog/2013/10/17/introducting-mapattack/"],"category":["realtimeconf"]}}],"rels":{"author":["https://aaronparecki.com/","https://plus.google.com/117847912875913905493"]},"rel-urls":{"https://aaronparecki.com/":{"text":"aaronparecki.com","rels":["author"]},"https://plus.google.com/117847912875913905493":{"text":"Aaron Parecki","rels":["author"]}}}; - - it('mixedroots', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-mixed/h-entry/mixedroots +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-entry', function() { + var htmlFragment = "\n\n\n
\n \n
Did you play\n @playmapattackat\n #realtimeconf? Here is some more info about how we built it!\n http://pdx.esri.com/blog/2013/10/17/introducting-mapattack/\n
\n
"; + var expected = {"items":[{"type":["h-entry"],"properties":{"author":[{"value":"aaronparecki.com\n Aaron Parecki\n Aaron Parecki","type":["h-card"],"properties":{"photo":["https://aaronparecki.com/images/aaronpk.png"],"logo":["https://aaronparecki.com/images/aaronpk.png"],"url":["https://aaronparecki.com/"],"uid":["https://aaronparecki.com/"],"name":["Aaron Parecki"]}}],"content":[{"value":"Did you play\n @playmapattackat\n #realtimeconf? Here is some more info about how we built it!\n http://pdx.esri.com/blog/2013/10/17/introducting-mapattack/","html":"Did you play\n @playmapattackat\n #realtimeconf? Here is some more info about how we built it!\n http://pdx.esri.com/blog/2013/10/17/introducting-mapattack/\n "}],"name":["Did you play\n @playmapattackat\n #realtimeconf? Here is some more info about how we built it!\n http://pdx.esri.com/blog/2013/10/17/introducting-mapattack/"],"category":["realtimeconf"]}}],"rels":{"author":["https://aaronparecki.com/","https://plus.google.com/117847912875913905493"]},"rel-urls":{"https://aaronparecki.com/":{"text":"aaronparecki.com","rels":["author"]},"https://plus.google.com/117847912875913905493":{"text":"Aaron Parecki","rels":["author"]}}}; + + it('mixedroots', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-mixed-h-resume-mixedroots.js b/toolkit/components/microformats/test/standards-tests/mf-mixed-h-resume-mixedroots.js index c521cd576c..5147866c6b 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-mixed-h-resume-mixedroots.js +++ b/toolkit/components/microformats/test/standards-tests/mf-mixed-h-resume-mixedroots.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-mixed/h-resume/mixedroots -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-resume', function() { - var htmlFragment = "\n
\n
\n

Tim Berners-Lee

\n

Director of the World Wide Web Foundation

\n
\n

Invented the World Wide Web.


\n
\n

Director

\n

World Wide Web Foundation

\n

\n – Present\n \n

\n
\n
"; - var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"],"job-title":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"experience":[{"value":"World Wide Web Foundation","type":["h-event","h-card"],"properties":{"job-title":["Director"],"name":["World Wide Web Foundation"],"org":["World Wide Web Foundation"],"url":["http://www.webfoundation.org/"],"start":["2009-01-18"],"duration":["P2Y11M"]}}],"name":["Tim Berners-Lee\n Director of the World Wide Web Foundation\n \n Invented the World Wide Web.\n \n Director\n World Wide Web Foundation\n \n Jan 2009 – Present\n (2 years 11 month)"]}}],"rels":{},"rel-urls":{}}; - - it('mixedroots', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-mixed/h-resume/mixedroots +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-resume', function() { + var htmlFragment = "\n
\n
\n

Tim Berners-Lee

\n

Director of the World Wide Web Foundation

\n
\n

Invented the World Wide Web.


\n
\n

Director

\n

World Wide Web Foundation

\n

\n – Present\n \n

\n
\n
"; + var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"],"job-title":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"experience":[{"value":"World Wide Web Foundation","type":["h-event","h-card"],"properties":{"job-title":["Director"],"name":["World Wide Web Foundation"],"org":["World Wide Web Foundation"],"url":["http://www.webfoundation.org/"],"start":["2009-01-18"],"duration":["P2Y11M"]}}],"name":["Tim Berners-Lee\n Director of the World Wide Web Foundation\n \n Invented the World Wide Web.\n \n Director\n World Wide Web Foundation\n \n Jan 2009 – Present\n (2 years 11 month)"]}}],"rels":{},"rel-urls":{}}; + + it('mixedroots', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-adr-simpleproperties.js b/toolkit/components/microformats/test/standards-tests/mf-v1-adr-simpleproperties.js index cd380fd778..09a346e0c4 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-adr-simpleproperties.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-adr-simpleproperties.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/adr/simpleproperties -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('adr', function() { - var htmlFragment = "

\n 665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A. \n

"; - var expected = {"items":[{"type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."]}}],"rels":{},"rel-urls":{}}; - - it('simpleproperties', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/adr/simpleproperties +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('adr', function() { + var htmlFragment = "

\n 665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A. \n

"; + var expected = {"items":[{"type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."]}}],"rels":{},"rel-urls":{}}; + + it('simpleproperties', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-geo-abbrpattern.js b/toolkit/components/microformats/test/standards-tests/mf-v1-geo-abbrpattern.js index 7ff9576c0d..090e98bb99 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-geo-abbrpattern.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-geo-abbrpattern.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/geo/abbrpattern -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('geo', function() { - var htmlFragment = "\n

\n N 37° 24.491, \n W 122° 08.313\n

"; - var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["37.408183"],"longitude":["-122.13855"]}}],"rels":{},"rel-urls":{}}; - - it('abbrpattern', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/geo/abbrpattern +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('geo', function() { + var htmlFragment = "\n

\n N 37° 24.491, \n W 122° 08.313\n

"; + var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["37.408183"],"longitude":["-122.13855"]}}],"rels":{},"rel-urls":{}}; + + it('abbrpattern', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-geo-hidden.js b/toolkit/components/microformats/test/standards-tests/mf-v1-geo-hidden.js index a14f05e9f1..d67a03b4f3 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-geo-hidden.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-geo-hidden.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/geo/hidden -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('geo', function() { - var htmlFragment = "

\n The Bricklayer's Arms\n \n \n \n \n \n \n \n

"; - var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"]}}],"rels":{},"rel-urls":{}}; - - it('hidden', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/geo/hidden +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('geo', function() { + var htmlFragment = "

\n The Bricklayer's Arms\n \n \n \n \n \n \n \n

"; + var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"]}}],"rels":{},"rel-urls":{}}; + + it('hidden', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-geo-simpleproperties.js b/toolkit/components/microformats/test/standards-tests/mf-v1-geo-simpleproperties.js index fe495fd91b..82cd7d3d92 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-geo-simpleproperties.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-geo-simpleproperties.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/geo/simpleproperties -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('geo', function() { - var htmlFragment = "We are meeting at \n \n The Bricklayer's Arms\n (Geo: 51.513458:\n -0.14812)\n"; - var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"]}}],"rels":{},"rel-urls":{}}; - - it('simpleproperties', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/geo/simpleproperties +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('geo', function() { + var htmlFragment = "We are meeting at \n \n The Bricklayer's Arms\n (Geo: 51.513458:\n -0.14812)\n"; + var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"]}}],"rels":{},"rel-urls":{}}; + + it('simpleproperties', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-geo-valuetitleclass.js b/toolkit/components/microformats/test/standards-tests/mf-v1-geo-valuetitleclass.js index ad35d3b4d1..196e07f7dd 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-geo-valuetitleclass.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-geo-valuetitleclass.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/geo/valuetitleclass -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('geo', function() { - var htmlFragment = "\n

\n \n \n N 51° 51.345, \n \n \n W -0° 14.812\n \n \n

"; - var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"]}}],"rels":{},"rel-urls":{}}; - - it('valuetitleclass', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/geo/valuetitleclass +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('geo', function() { + var htmlFragment = "\n

\n \n \n N 51° 51.345, \n \n \n W -0° 14.812\n \n \n

"; + var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"]}}],"rels":{},"rel-urls":{}}; + + it('valuetitleclass', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-ampm.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-ampm.js index 27b1c383a6..5da5fd7df9 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-ampm.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-ampm.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcalendar/ampm -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcalendar', function() { - var htmlFragment = "
\n The 4th Microformat party will be on \n
    \n
  • \n , from\n 07:00:00pm \n
  • \n
  • \n , from\n 07:00:00am \n
  • \n
  • \n , from\n 07:00pm \n
  • \n
  • \n , from\n 07pm \n
  • \n
  • \n , from\n 7pm \n
  • \n
  • \n , from\n 7:00pm \n
  • \n
  • \n , from\n 07:00p.m. \n
  • \n
  • \n , from\n 07:00PM \n
  • \n
  • \n , from\n 7:00am \n
  • \n
\n
"; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00:00","2009-06-26 07:00:00","2009-06-26 19:00","2009-06-26 19","2009-06-26 19","2009-06-26 19:00","2009-06-26 19:00","2009-06-26 19:00","2009-06-26 07:00"]}}],"rels":{},"rel-urls":{}}; - - it('ampm', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcalendar/ampm +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcalendar', function() { + var htmlFragment = "
\n The 4th Microformat party will be on \n
    \n
  • \n , from\n 07:00:00pm \n
  • \n
  • \n , from\n 07:00:00am \n
  • \n
  • \n , from\n 07:00pm \n
  • \n
  • \n , from\n 07pm \n
  • \n
  • \n , from\n 7pm \n
  • \n
  • \n , from\n 7:00pm \n
  • \n
  • \n , from\n 07:00p.m. \n
  • \n
  • \n , from\n 07:00PM \n
  • \n
  • \n , from\n 7:00am \n
  • \n
\n
"; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00:00","2009-06-26 07:00:00","2009-06-26 19:00","2009-06-26 19","2009-06-26 19","2009-06-26 19:00","2009-06-26 19:00","2009-06-26 19:00","2009-06-26 07:00"]}}],"rels":{},"rel-urls":{}}; + + it('ampm', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-attendees.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-attendees.js index 6ea1788a4c..ca28ad431c 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-attendees.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-attendees.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcalendar/attendees -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcalendar', function() { - var htmlFragment = "\n
\n CPJ Online Press Freedom Summit\n () in\n San Francisco.\n Attendees:\n
    \n
  • Brian Warner
  • \n
  • Kyle Machulis
  • \n
  • Tantek Çelik
  • \n
  • Sid Sutter
  • \n
\n
\n"; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["CPJ Online Press Freedom Summit"],"start":["2012-10-10"],"location":["San Francisco"],"attendee":[{"value":"Brian Warner","type":["h-card"],"properties":{"name":["Brian Warner"]}},{"value":"Kyle Machulis","type":["h-card"],"properties":{"name":["Kyle Machulis"]}},{"value":"Tantek Çelik","type":["h-card"],"properties":{"name":["Tantek Çelik"]}},{"value":"Sid Sutter","type":["h-card"],"properties":{"name":["Sid Sutter"]}}]}}],"rels":{},"rel-urls":{}}; - - it('attendees', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcalendar/attendees +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcalendar', function() { + var htmlFragment = "\n
\n CPJ Online Press Freedom Summit\n () in\n San Francisco.\n Attendees:\n
    \n
  • Brian Warner
  • \n
  • Kyle Machulis
  • \n
  • Tantek Çelik
  • \n
  • Sid Sutter
  • \n
\n
\n"; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["CPJ Online Press Freedom Summit"],"start":["2012-10-10"],"location":["San Francisco"],"attendee":[{"value":"Brian Warner","type":["h-card"],"properties":{"name":["Brian Warner"]}},{"value":"Kyle Machulis","type":["h-card"],"properties":{"name":["Kyle Machulis"]}},{"value":"Tantek Çelik","type":["h-card"],"properties":{"name":["Tantek Çelik"]}},{"value":"Sid Sutter","type":["h-card"],"properties":{"name":["Sid Sutter"]}}]}}],"rels":{},"rel-urls":{}}; + + it('attendees', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-combining.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-combining.js index 852a4da20e..7e5a361b12 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-combining.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-combining.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcalendar/combining -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcalendar', function() { - var htmlFragment = "
\n \n IndieWebCamp 2012\n \n from \n to at \n \n Geoloqi, \n \n 920 SW 3rd Ave. Suite 400, \n Portland, \n OR\n \n \n
"; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["IndieWebCamp 2012"],"url":["http://indiewebcamp.com/2012"],"start":["2012-06-30"],"end":["2012-07-01"],"location":[{"value":"Geoloqi","type":["h-card"],"properties":{"name":["Geoloqi"],"org":["Geoloqi"],"url":["http://geoloqi.com/"],"adr":[{"value":"920 SW 3rd Ave. Suite 400, \n Portland, \n OR","type":["h-adr"],"properties":{"street-address":["920 SW 3rd Ave. Suite 400"],"locality":["Portland"],"region":["Oregon"]}}]}}]}}],"rels":{},"rel-urls":{}}; - - it('combining', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcalendar/combining +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcalendar', function() { + var htmlFragment = "
\n \n IndieWebCamp 2012\n \n from \n to at \n \n Geoloqi, \n \n 920 SW 3rd Ave. Suite 400, \n Portland, \n OR\n \n \n
"; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["IndieWebCamp 2012"],"url":["http://indiewebcamp.com/2012"],"start":["2012-06-30"],"end":["2012-07-01"],"location":[{"value":"Geoloqi","type":["h-card"],"properties":{"name":["Geoloqi"],"org":["Geoloqi"],"url":["http://geoloqi.com/"],"adr":[{"value":"920 SW 3rd Ave. Suite 400, \n Portland, \n OR","type":["h-adr"],"properties":{"street-address":["920 SW 3rd Ave. Suite 400"],"locality":["Portland"],"region":["Oregon"]}}]}}]}}],"rels":{},"rel-urls":{}}; + + it('combining', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-concatenate.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-concatenate.js index f4d60aee22..d17914e1c3 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-concatenate.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-concatenate.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcalendar/concatenate -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcalendar', function() { - var htmlFragment = "
\n The 4th Microformat party will be on \n \n , from\n to \n .\n
"; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00"],"end":["2009-06-26 22:00"]}}],"rels":{},"rel-urls":{}}; - - it('concatenate', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcalendar/concatenate +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcalendar', function() { + var htmlFragment = "
\n The 4th Microformat party will be on \n \n , from\n to \n .\n
"; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00"],"end":["2009-06-26 22:00"]}}],"rels":{},"rel-urls":{}}; + + it('concatenate', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-time.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-time.js index 81b7c55453..edb26d6ad8 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-time.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcalendar-time.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcalendar/time -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcalendar', function() { - var htmlFragment = "
\n The 4th Microformat party will be on \n
    \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n \n
  • \n
\n
"; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00:00-08:00","2009-06-26 19:00:00-08:00","2009-06-26 19:00:00+08:00","2009-06-26 19:00:00Z","2009-06-26 19:00:00","2009-06-26 19:00-08:00","2009-06-26 19:00+08:00","2009-06-26 19:00Z","2009-06-26 19:00"],"end":["2013-034"]}}],"rels":{},"rel-urls":{}}; - - it('time', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcalendar/time +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcalendar', function() { + var htmlFragment = "
\n The 4th Microformat party will be on \n
    \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n , from\n \n
  • \n
  • \n \n
  • \n
\n
"; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00:00-08:00","2009-06-26 19:00:00-08:00","2009-06-26 19:00:00+08:00","2009-06-26 19:00:00Z","2009-06-26 19:00:00","2009-06-26 19:00-08:00","2009-06-26 19:00+08:00","2009-06-26 19:00Z","2009-06-26 19:00"],"end":["2013-034"]}}],"rels":{},"rel-urls":{}}; + + it('time', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-email.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-email.js index 3c7e8368f5..48660ffb1b 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-email.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-email.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcard/email -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcard', function() { - var htmlFragment = "
\n John Doe \n \n
"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["John Doe"],"email":["mailto:john@example.com","mailto:john@example.com","mailto:john@example.com?subject=parser-test","john@example.com"]}}],"rels":{},"rel-urls":{}}; - - it('email', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcard/email +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcard', function() { + var htmlFragment = "
\n John Doe \n \n
"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["John Doe"],"email":["mailto:john@example.com","mailto:john@example.com","mailto:john@example.com?subject=parser-test","john@example.com"]}}],"rels":{},"rel-urls":{}}; + + it('email', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-format.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-format.js index ffe52ed549..eb539fd870 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-format.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-format.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcard/format -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcard', function() { - var htmlFragment = "

\n \n John \n Doe \n \n

"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["John \n Doe"],"given-name":["John"]}}],"rels":{},"rel-urls":{}}; - - it('format', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcard/format +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcard', function() { + var htmlFragment = "

\n \n John \n Doe \n \n

"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["John \n Doe"],"given-name":["John"]}}],"rels":{},"rel-urls":{}}; + + it('format', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-hyperlinkedphoto.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-hyperlinkedphoto.js index 175eed3992..7f348b4a80 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-hyperlinkedphoto.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-hyperlinkedphoto.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcard/hyperlinkedphoto -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcard', function() { - var htmlFragment = "\n \"Rohit\n"; - var expected = {"items":[{"type":["h-card"],"properties":{}}],"rels":{},"rel-urls":{}}; - - it('hyperlinkedphoto', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcard/hyperlinkedphoto +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcard', function() { + var htmlFragment = "\n \"Rohit\n"; + var expected = {"items":[{"type":["h-card"],"properties":{}}],"rels":{},"rel-urls":{}}; + + it('hyperlinkedphoto', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-justahyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-justahyperlink.js index 22a067982e..e320f0f8af 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-justahyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-justahyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcard/justahyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcard', function() { - var htmlFragment = "Ben Ward"; - var expected = {"items":[{"type":["h-card"],"properties":{}}],"rels":{},"rel-urls":{}}; - - it('justahyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcard/justahyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcard', function() { + var htmlFragment = "Ben Ward"; + var expected = {"items":[{"type":["h-card"],"properties":{}}],"rels":{},"rel-urls":{}}; + + it('justahyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-justaname.js index 365f708f9f..ba2a6d47dd 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcard/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcard', function() { - var htmlFragment = "

Frances Berriman

"; - var expected = {"items":[{"type":["h-card"],"properties":{}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcard/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcard', function() { + var htmlFragment = "

Frances Berriman

"; + var expected = {"items":[{"type":["h-card"],"properties":{}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-multiple.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-multiple.js index 102233a793..058e5e2aef 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-multiple.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-multiple.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcard/multiple -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcard', function() { - var htmlFragment = "\n
\n \n
John Doe
\n Pronunciation of my name\n
\"Photo
\n\n

Nicknames:

\n
    \n
  • Man with no name
  • \n
  • Lost boy
  • \n
\n\n

About:

\n

John Doe is one of those names you always have issues with.

\n

It can be a real problem booking a hotel room with the name John Doe.

\n\n

Companies:

\n
\n \"Madgex\n \"Web\n
\n \n \n

Tags: \n design, \n development and\n web\n

\n \n

Phone numbers:

\n
    \n
  • \n Work (preferred):\n +1 415 555 100\n
  • \n
  • Home: +1 415 555 200
  • \n
  • Postal: +1 415 555 300
  • \n
\n \n

Emails:

\n \n

John Doe uses PigeonMail 2.1 or Outlook 2007 for email.

\n\n

Addresses:

\n
    \n
  • \n \n Work: \n North Street, \n Brighton, \n United Kingdom\n \n \n
  • \n
  • \n \n Home: \n West Street, \n Brighton, \n United Kingdom\n \n
  • \n
\n \n

In emergency contact: Jane Doe or Dave Doe.

\n

Key: hd02$Gfu*d%dh87KTa2=23934532479

\n
"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["John Doe"],"given-name":["John"],"family-name":["Doe"],"sound":["http://www.madgex.com/johndoe.mpeg"],"photo":["http://example.com/images/photo.gif"],"nickname":["Man with no name","Lost boy"],"note":["John Doe is one of those names you always have issues with.","It can be a real problem booking a hotel room with the name John Doe."],"logo":["http://example.com/images/logo.gif","http://example.com/images/logo.gif"],"url":["http://www.madgex.com/","http://www.webfeetmedia.com/"],"org":["Madgex","Web Feet Media Ltd"],"job-title":["Creative Director","Owner"],"category":["design","development","web"],"tel":["+1 415 555 100","+1 415 555 200","+1 415 555 300"],"email":["mailto:john.doe@madgex.com","mailto:john.doe@webfeetmedia.com"],"mailer":["PigeonMail 2.1","Outlook 2007"],"label":["Work: \n North Street, \n Brighton, \n United Kingdom","Home: \n West Street, \n Brighton, \n United Kingdom"],"adr":[{"value":"Work: \n North Street, \n Brighton, \n United Kingdom","type":["h-adr"],"properties":{"street-address":["North Street"],"locality":["Brighton"],"country-name":["United Kingdom"]}},{"value":"Home: \n West Street, \n Brighton, \n United Kingdom","type":["h-adr"],"properties":{"street-address":["West Street"],"locality":["Brighton"],"country-name":["United Kingdom"]}}],"agent":["Jane Doe",{"value":"Dave Doe","type":["h-card"],"properties":{"name":["Dave Doe"]}}],"key":["hd02$Gfu*d%dh87KTa2=23934532479"]}}],"rels":{"tag":["http://en.wikipedia.org/wiki/design","http://en.wikipedia.org/wiki/development","http://en.wikipedia.org/wiki/web"]},"rel-urls":{"http://en.wikipedia.org/wiki/design":{"text":"design","rels":["tag"]},"http://en.wikipedia.org/wiki/development":{"text":"development","rels":["tag"]},"http://en.wikipedia.org/wiki/web":{"text":"web","rels":["tag"]}}}; - - it('multiple', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcard/multiple +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcard', function() { + var htmlFragment = "\n
\n \n
John Doe
\n Pronunciation of my name\n
\"Photo
\n\n

Nicknames:

\n
    \n
  • Man with no name
  • \n
  • Lost boy
  • \n
\n\n

About:

\n

John Doe is one of those names you always have issues with.

\n

It can be a real problem booking a hotel room with the name John Doe.

\n\n

Companies:

\n
\n \"Madgex\n \"Web\n
\n \n \n

Tags: \n design, \n development and\n web\n

\n \n

Phone numbers:

\n
    \n
  • \n Work (preferred):\n +1 415 555 100\n
  • \n
  • Home: +1 415 555 200
  • \n
  • Postal: +1 415 555 300
  • \n
\n \n

Emails:

\n \n

John Doe uses PigeonMail 2.1 or Outlook 2007 for email.

\n\n

Addresses:

\n
    \n
  • \n \n Work: \n North Street, \n Brighton, \n United Kingdom\n \n \n
  • \n
  • \n \n Home: \n West Street, \n Brighton, \n United Kingdom\n \n
  • \n
\n \n

In emergency contact: Jane Doe or Dave Doe.

\n

Key: hd02$Gfu*d%dh87KTa2=23934532479

\n
"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["John Doe"],"given-name":["John"],"family-name":["Doe"],"sound":["http://www.madgex.com/johndoe.mpeg"],"photo":["http://example.com/images/photo.gif"],"nickname":["Man with no name","Lost boy"],"note":["John Doe is one of those names you always have issues with.","It can be a real problem booking a hotel room with the name John Doe."],"logo":["http://example.com/images/logo.gif","http://example.com/images/logo.gif"],"url":["http://www.madgex.com/","http://www.webfeetmedia.com/"],"org":["Madgex","Web Feet Media Ltd"],"job-title":["Creative Director","Owner"],"category":["design","development","web"],"tel":["+1 415 555 100","+1 415 555 200","+1 415 555 300"],"email":["mailto:john.doe@madgex.com","mailto:john.doe@webfeetmedia.com"],"mailer":["PigeonMail 2.1","Outlook 2007"],"label":["Work: \n North Street, \n Brighton, \n United Kingdom","Home: \n West Street, \n Brighton, \n United Kingdom"],"adr":[{"value":"Work: \n North Street, \n Brighton, \n United Kingdom","type":["h-adr"],"properties":{"street-address":["North Street"],"locality":["Brighton"],"country-name":["United Kingdom"]}},{"value":"Home: \n West Street, \n Brighton, \n United Kingdom","type":["h-adr"],"properties":{"street-address":["West Street"],"locality":["Brighton"],"country-name":["United Kingdom"]}}],"agent":["Jane Doe",{"value":"Dave Doe","type":["h-card"],"properties":{"name":["Dave Doe"]}}],"key":["hd02$Gfu*d%dh87KTa2=23934532479"]}}],"rels":{"tag":["http://en.wikipedia.org/wiki/design","http://en.wikipedia.org/wiki/development","http://en.wikipedia.org/wiki/web"]},"rel-urls":{"http://en.wikipedia.org/wiki/design":{"text":"design","rels":["tag"]},"http://en.wikipedia.org/wiki/development":{"text":"development","rels":["tag"]},"http://en.wikipedia.org/wiki/web":{"text":"web","rels":["tag"]}}}; + + it('multiple', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-name.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-name.js index 613a94c75b..ef75899cff 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-name.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-name.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcard/name -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcard', function() { - var htmlFragment = "\n
\n
\n Dr \n John \n P \n Doe \n \n \"PHD\"\n
\n
"; - var expected = {"items":[{"type":["h-card"],"properties":{"honorific-prefix":["Dr"],"given-name":["John"],"additional-name":["Peter"],"family-name":["Doe"],"honorific-suffix":["MSc","PHD"],"photo":["http://example.com/images/logo.gif"]}}],"rels":{},"rel-urls":{}}; - - it('name', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcard/name +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcard', function() { + var htmlFragment = "\n
\n
\n Dr \n John \n P \n Doe \n \n \"PHD\"\n
\n
"; + var expected = {"items":[{"type":["h-card"],"properties":{"honorific-prefix":["Dr"],"given-name":["John"],"additional-name":["Peter"],"family-name":["Doe"],"honorific-suffix":["MSc","PHD"],"photo":["http://example.com/images/logo.gif"]}}],"rels":{},"rel-urls":{}}; + + it('name', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-single.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-single.js index eabfdd4c68..a7ef7628b8 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-single.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hcard-single.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hcard/single -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hcard', function() { - var htmlFragment = "
\n \n
John Doe
\n
Birthday: January 1st, 2000
\n
Role: Designer
\n
Location: Brighton
\n
Time zone: Eastern Standard Time
\n \n
Profile details:\n
Profile id: http://example.com/profiles/johndoe
\n
Details are: Public
\n
Last updated: January 1st, 2008 - 13:45
\n
\n
"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["John Doe"],"given-name":["John"],"sort-string":["John"],"bday":["2000-01-01 00:00:00-08:00"],"role":["Designer"],"geo":[{"value":"30.267991;-97.739568","type":["h-geo"],"properties":{"name":["30.267991;-97.739568"]}}],"tz":["-05:00"],"uid":["http://example.com/profiles/johndoe"],"class":["Public"],"rev":["2008-01-01 13:45:00"]}}],"rels":{},"rel-urls":{}}; - - it('single', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hcard/single +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hcard', function() { + var htmlFragment = "
\n \n
John Doe
\n
Birthday: January 1st, 2000
\n
Role: Designer
\n
Location: Brighton
\n
Time zone: Eastern Standard Time
\n \n
Profile details:\n
Profile id: http://example.com/profiles/johndoe
\n
Details are: Public
\n
Last updated: January 1st, 2008 - 13:45
\n
\n
"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["John Doe"],"given-name":["John"],"sort-string":["John"],"bday":["2000-01-01 00:00:00-08:00"],"role":["Designer"],"geo":[{"value":"30.267991;-97.739568","type":["h-geo"],"properties":{"name":["30.267991;-97.739568"]}}],"tz":["-05:00"],"uid":["http://example.com/profiles/johndoe"],"class":["Public"],"rev":["2008-01-01 13:45:00"]}}],"rels":{},"rel-urls":{}}; + + it('single', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hentry-summarycontent.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hentry-summarycontent.js index 9aff8844f2..5280efb047 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hentry-summarycontent.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hentry-summarycontent.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hentry/summarycontent -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hentry', function() { - var htmlFragment = "\n
\n

microformats.org at 7

\n
\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n
\n

Updated \n by\n Tantek\n

\n
"; - var expected = {"items":[{"type":["h-entry"],"properties":{"name":["microformats.org at 7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"rels":{},"rel-urls":{}}; - - it('summarycontent', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hentry/summarycontent +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hentry', function() { + var htmlFragment = "\n
\n

microformats.org at 7

\n
\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n
\n

Updated \n by\n Tantek\n

\n
"; + var expected = {"items":[{"type":["h-entry"],"properties":{"name":["microformats.org at 7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"rels":{},"rel-urls":{}}; + + it('summarycontent', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hfeed-simple.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hfeed-simple.js index 32779966e5..4c8294d499 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hfeed-simple.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hfeed-simple.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hfeed/simple -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hfeed', function() { - var htmlFragment = "
\n\t

Microformats blog

\n\tTantek\n\tpermlink\n\t\n\t

\n\t\tTags: microformats, \n\t\thtml\n\t

\n\t\n\t
\n\t

microformats.org at 7

\n\t
\n\t

Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.

\n\t\n\t

The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service

\n\t
\n\t

Updated \n\t \n\t

\n\t
\n\t\n
"; - var expected = {"items":[{"type":["h-feed"],"properties":{"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}],"url":["http://microformats.org/blog"],"photo":["http://example.com/photo.jpeg"],"category":["microformats","html"]},"children":[{"value":"microformats.org at 7\n\t \n\t Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.\n\t\n\t The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service \n\t \n\t Updated \n\t June 25th, 2012","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.\n\t\n\t The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service","html":"\n\t

Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.

\n\t\n\t

The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service

\n\t "}],"summary":["Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities."],"updated":["2012-06-25 17:08:26"]}}]}],"rels":{"tag":["http://example.com/tags/microformats","http://example.com/tags/html"],"bookmark":["http://microformats.org/2012/06/25/microformats-org-at-7"]},"rel-urls":{"http://example.com/tags/microformats":{"text":"microformats","rels":["tag"]},"http://example.com/tags/html":{"text":"html","rels":["tag"]},"http://microformats.org/2012/06/25/microformats-org-at-7":{"text":"microformats.org at 7","rels":["bookmark"]}}}; - - it('simple', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hfeed/simple +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hfeed', function() { + var htmlFragment = "
\n\t

Microformats blog

\n\tTantek\n\tpermlink\n\t\n\t

\n\t\tTags: microformats, \n\t\thtml\n\t

\n\t\n\t
\n\t

microformats.org at 7

\n\t
\n\t

Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.

\n\t\n\t

The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service

\n\t
\n\t

Updated \n\t \n\t

\n\t
\n\t\n
"; + var expected = {"items":[{"type":["h-feed"],"properties":{"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}],"url":["http://microformats.org/blog"],"photo":["http://example.com/photo.jpeg"],"category":["microformats","html"]},"children":[{"value":"microformats.org at 7\n\t \n\t Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.\n\t\n\t The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service \n\t \n\t Updated \n\t June 25th, 2012","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.\n\t\n\t The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service","html":"\n\t

Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.

\n\t\n\t

The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service

\n\t "}],"summary":["Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities."],"updated":["2012-06-25 17:08:26"]}}]}],"rels":{"tag":["http://example.com/tags/microformats","http://example.com/tags/html"],"bookmark":["http://microformats.org/2012/06/25/microformats-org-at-7"]},"rel-urls":{"http://example.com/tags/microformats":{"text":"microformats","rels":["tag"]},"http://example.com/tags/html":{"text":"html","rels":["tag"]},"http://microformats.org/2012/06/25/microformats-org-at-7":{"text":"microformats.org at 7","rels":["bookmark"]}}}; + + it('simple', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hnews-all.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hnews-all.js index 90bae1a01b..82eb37b958 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hnews-all.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hnews-all.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hnews/all -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hnews', function() { - var htmlFragment = "
\n
\n

microformats.org at 7

\n
\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n
\n

Updated \n by\n Tantek\n

\n
\n\n

\n \n \n San Francisco, \n CA \n \n \n (Geo: 37.774921;-122.445202) \n \n microformats.org\n \n

\n

\n Publishing policy\n

\n
"; - var expected = {"items":[{"type":["h-news"],"properties":{"entry":[{"value":"microformats.org at 7","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"dateline":[{"value":"San Francisco, \n CA","type":["h-card"],"properties":{"adr":[{"value":"San Francisco, \n CA","type":["h-adr"],"properties":{"locality":["San Francisco"],"region":["CA"]}}]}}],"geo":[{"value":"37.774921;-122.445202","type":["h-geo"],"properties":{"name":["37.774921;-122.445202"]}}],"source-org":[{"value":"microformats.org","type":["h-card"],"properties":{"name":["microformats.org"],"org":["microformats.org"],"url":["http://microformats.org/"]}}],"principles":["http://microformats.org/wiki/Category:public_domain_license"]}}],"rels":{"bookmark":["http://microformats.org/2012/06/25/microformats-org-at-7"],"principles":["http://microformats.org/wiki/Category:public_domain_license"]},"rel-urls":{"http://microformats.org/2012/06/25/microformats-org-at-7":{"text":"microformats.org at 7","rels":["bookmark"]},"http://microformats.org/wiki/Category:public_domain_license":{"text":"Publishing policy","rels":["principles"]}}}; - - it('all', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hnews/all +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hnews', function() { + var htmlFragment = "
\n
\n

microformats.org at 7

\n
\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n
\n

Updated \n by\n Tantek\n

\n
\n\n

\n \n \n San Francisco, \n CA \n \n \n (Geo: 37.774921;-122.445202) \n \n microformats.org\n \n

\n

\n Publishing policy\n

\n
"; + var expected = {"items":[{"type":["h-news"],"properties":{"entry":[{"value":"microformats.org at 7","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"dateline":[{"value":"San Francisco, \n CA","type":["h-card"],"properties":{"adr":[{"value":"San Francisco, \n CA","type":["h-adr"],"properties":{"locality":["San Francisco"],"region":["CA"]}}]}}],"geo":[{"value":"37.774921;-122.445202","type":["h-geo"],"properties":{"name":["37.774921;-122.445202"]}}],"source-org":[{"value":"microformats.org","type":["h-card"],"properties":{"name":["microformats.org"],"org":["microformats.org"],"url":["http://microformats.org/"]}}],"principles":["http://microformats.org/wiki/Category:public_domain_license"]}}],"rels":{"bookmark":["http://microformats.org/2012/06/25/microformats-org-at-7"],"principles":["http://microformats.org/wiki/Category:public_domain_license"]},"rel-urls":{"http://microformats.org/2012/06/25/microformats-org-at-7":{"text":"microformats.org at 7","rels":["bookmark"]},"http://microformats.org/wiki/Category:public_domain_license":{"text":"Publishing policy","rels":["principles"]}}}; + + it('all', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hnews-minimum.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hnews-minimum.js index cb686e5cfe..5faf13d7b8 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hnews-minimum.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hnews-minimum.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hnews/minimum -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hnews', function() { - var htmlFragment = "
\n
\n

microformats.org at 7

\n
\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n
\n

Updated \n by\n Tantek\n

\n
\n\n

\n microformats.org \n

\n
"; - var expected = {"items":[{"type":["h-news"],"properties":{"entry":[{"value":"microformats.org at 7","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"source-org":[{"value":"microformats.org","type":["h-card"],"properties":{"name":["microformats.org"],"org":["microformats.org"],"url":["http://microformats.org/"]}}]}}],"rels":{"bookmark":["http://microformats.org/2012/06/25/microformats-org-at-7"]},"rel-urls":{"http://microformats.org/2012/06/25/microformats-org-at-7":{"text":"microformats.org at 7","rels":["bookmark"]}}}; - - it('minimum', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hnews/minimum +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hnews', function() { + var htmlFragment = "
\n
\n

microformats.org at 7

\n
\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n
\n

Updated \n by\n Tantek\n

\n
\n\n

\n microformats.org \n

\n
"; + var expected = {"items":[{"type":["h-news"],"properties":{"entry":[{"value":"microformats.org at 7","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

\n\n

The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

\n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"source-org":[{"value":"microformats.org","type":["h-card"],"properties":{"name":["microformats.org"],"org":["microformats.org"],"url":["http://microformats.org/"]}}]}}],"rels":{"bookmark":["http://microformats.org/2012/06/25/microformats-org-at-7"]},"rel-urls":{"http://microformats.org/2012/06/25/microformats-org-at-7":{"text":"microformats.org at 7","rels":["bookmark"]}}}; + + it('minimum', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hproduct-aggregate.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hproduct-aggregate.js index a3b2452bc1..7171bc7264 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hproduct-aggregate.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hproduct-aggregate.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hproduct/aggregate -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hproduct', function() { - var htmlFragment = "\n
\n

Raspberry Pi

\n \n

The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.

\n More info about the Raspberry Pi\n

£29.95

\n

\n \n 9.2 out of \n 10 \n based on 178 reviews\n \n

\n

Categories: \n Computer, \n Education\n

\n

From: \n The Raspberry Pi Foundation - \n \n Cambridge \n UK\n \n

\n
"; - var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/RaspberryPi.jpg/320px-RaspberryPi.jpg"],"description":[{"value":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.","html":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."}],"url":["http://www.raspberrypi.org/"],"price":["£29.95"],"review":[{"value":"9.2 out of \n 10 \n based on 178 reviews","type":["h-review-aggregate"],"properties":{"rating":["9.2"],"average":["9.2"],"best":["10"],"count":["178"]}}],"category":["Computer","Education"],"brand":[{"value":"The Raspberry Pi Foundation","type":["h-card"],"properties":{"name":["The Raspberry Pi Foundation"],"org":["The Raspberry Pi Foundation"],"adr":[{"value":"Cambridge \n UK","type":["h-adr"],"properties":{"locality":["Cambridge"],"country-name":["UK"]}}]}}]}}],"rels":{"tag":["http://en.wikipedia.org/wiki/computer","http://en.wikipedia.org/wiki/education"]},"rel-urls":{"http://en.wikipedia.org/wiki/computer":{"text":"Computer","rels":["tag"]},"http://en.wikipedia.org/wiki/education":{"text":"Education","rels":["tag"]}}}; - - it('aggregate', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hproduct/aggregate +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hproduct', function() { + var htmlFragment = "\n
\n

Raspberry Pi

\n \n

The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.

\n More info about the Raspberry Pi\n

£29.95

\n

\n \n 9.2 out of \n 10 \n based on 178 reviews\n \n

\n

Categories: \n Computer, \n Education\n

\n

From: \n The Raspberry Pi Foundation - \n \n Cambridge \n UK\n \n

\n
"; + var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/RaspberryPi.jpg/320px-RaspberryPi.jpg"],"description":[{"value":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.","html":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."}],"url":["http://www.raspberrypi.org/"],"price":["£29.95"],"review":[{"value":"9.2 out of \n 10 \n based on 178 reviews","type":["h-review-aggregate"],"properties":{"rating":["9.2"],"average":["9.2"],"best":["10"],"count":["178"]}}],"category":["Computer","Education"],"brand":[{"value":"The Raspberry Pi Foundation","type":["h-card"],"properties":{"name":["The Raspberry Pi Foundation"],"org":["The Raspberry Pi Foundation"],"adr":[{"value":"Cambridge \n UK","type":["h-adr"],"properties":{"locality":["Cambridge"],"country-name":["UK"]}}]}}]}}],"rels":{"tag":["http://en.wikipedia.org/wiki/computer","http://en.wikipedia.org/wiki/education"]},"rel-urls":{"http://en.wikipedia.org/wiki/computer":{"text":"Computer","rels":["tag"]},"http://en.wikipedia.org/wiki/education":{"text":"Education","rels":["tag"]}}}; + + it('aggregate', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hproduct-simpleproperties.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hproduct-simpleproperties.js index 3c6a2c265c..7ec61f27b5 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hproduct-simpleproperties.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hproduct-simpleproperties.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hproduct/simpleproperties -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hproduct', function() { - var htmlFragment = "\n
\n

Raspberry Pi

\n \n

The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.

\n More info about the Raspberry Pi\n

£29.95

\n

4.5 out of 5

\n

Categories: \n Computer, \n Education\n

\n
"; - var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/RaspberryPi.jpg/320px-RaspberryPi.jpg"],"description":[{"value":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.","html":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."}],"url":["http://www.raspberrypi.org/"],"price":["£29.95"],"category":["Computer","Education"],"review":[{"value":"4.5 out of 5","type":["h-review"],"properties":{"rating":["4.5"]}}]}}],"rels":{"tag":["http://en.wikipedia.org/wiki/computer","http://en.wikipedia.org/wiki/education"]},"rel-urls":{"http://en.wikipedia.org/wiki/computer":{"text":"Computer","rels":["tag"]},"http://en.wikipedia.org/wiki/education":{"text":"Education","rels":["tag"]}}}; - - it('simpleproperties', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hproduct/simpleproperties +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hproduct', function() { + var htmlFragment = "\n
\n

Raspberry Pi

\n \n

The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.

\n More info about the Raspberry Pi\n

£29.95

\n

4.5 out of 5

\n

Categories: \n Computer, \n Education\n

\n
"; + var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/RaspberryPi.jpg/320px-RaspberryPi.jpg"],"description":[{"value":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.","html":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."}],"url":["http://www.raspberrypi.org/"],"price":["£29.95"],"category":["Computer","Education"],"review":[{"value":"4.5 out of 5","type":["h-review"],"properties":{"rating":["4.5"]}}]}}],"rels":{"tag":["http://en.wikipedia.org/wiki/computer","http://en.wikipedia.org/wiki/education"]},"rel-urls":{"http://en.wikipedia.org/wiki/computer":{"text":"Computer","rels":["tag"]},"http://en.wikipedia.org/wiki/education":{"text":"Education","rels":["tag"]}}}; + + it('simpleproperties', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-affiliation.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-affiliation.js index ea3ef2d73e..d580d68dbb 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-affiliation.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-affiliation.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hresume/affiliation -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hresume', function() { - var htmlFragment = "
\n

\n Tim Berners-Lee, \n invented the World Wide Web.\n

\n Belongs to following groups:\n

\n \n \"W3C\"\n \n

\n
"; - var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"]}}],"summary":["invented the World Wide Web"],"affiliation":[{"type":["h-card"],"properties":{"name":["W3C"],"photo":["http://www.w3.org/Icons/WWW/w3c_home_nb.png"]}}]}}],"rels":{},"rel-urls":{}}; - - it('affiliation', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hresume/affiliation +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hresume', function() { + var htmlFragment = "
\n

\n Tim Berners-Lee, \n invented the World Wide Web.\n

\n Belongs to following groups:\n

\n \n \"W3C\"\n \n

\n
"; + var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"]}}],"summary":["invented the World Wide Web"],"affiliation":[{"type":["h-card"],"properties":{"name":["W3C"],"photo":["http://www.w3.org/Icons/WWW/w3c_home_nb.png"]}}]}}],"rels":{},"rel-urls":{}}; + + it('affiliation', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-contact.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-contact.js index e511adf821..595087af44 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-contact.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-contact.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hresume/contact -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hresume', function() { - var htmlFragment = "
\n
\n

Tim Berners-Lee

\n

MIT

\n

\n 32 Vassar Street, \n Room 32-G524, \n Cambridge, \n MA \n 02139, \n USA. \n (Work)\n

\n

Tel:+1 (617) 253 5702

\n

Email:timbl@w3.org

\n
\n

Invented the World Wide Web.

\n
"; - var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"],"org":["MIT"],"adr":[{"value":"32 Vassar Street, \n Room 32-G524, \n Cambridge, \n MA \n 02139, \n USA. \n (Work)","type":["h-adr"],"properties":{"street-address":["32 Vassar Street"],"extended-address":["Room 32-G524"],"locality":["Cambridge"],"region":["MA"],"postal-code":["02139"],"country-name":["USA"]}}],"tel":["+1 (617) 253 5702"],"email":["mailto:timbl@w3.org"]}}],"summary":["Invented the World Wide Web."]}}],"rels":{},"rel-urls":{}}; - - it('contact', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hresume/contact +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hresume', function() { + var htmlFragment = "
\n
\n

Tim Berners-Lee

\n

MIT

\n

\n 32 Vassar Street, \n Room 32-G524, \n Cambridge, \n MA \n 02139, \n USA. \n (Work)\n

\n

Tel:+1 (617) 253 5702

\n

Email:timbl@w3.org

\n
\n

Invented the World Wide Web.

\n
"; + var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"],"org":["MIT"],"adr":[{"value":"32 Vassar Street, \n Room 32-G524, \n Cambridge, \n MA \n 02139, \n USA. \n (Work)","type":["h-adr"],"properties":{"street-address":["32 Vassar Street"],"extended-address":["Room 32-G524"],"locality":["Cambridge"],"region":["MA"],"postal-code":["02139"],"country-name":["USA"]}}],"tel":["+1 (617) 253 5702"],"email":["mailto:timbl@w3.org"]}}],"summary":["Invented the World Wide Web."]}}],"rels":{},"rel-urls":{}}; + + it('contact', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-education.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-education.js index 876af45717..7a0114f538 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-education.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-education.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hresume/education -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hresume', function() { - var htmlFragment = "
\n
\n

Tim Berners-Lee

\n

Director of the World Wide Web Foundation

\n
\n

Invented the World Wide Web.


\n

\n The Queen's College, Oxford University, \n BA Hons (I) Physics \n –\n \n

\n
"; - var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"],"job-title":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"education":[{"value":"The Queen's College, Oxford University","type":["h-event","h-card"],"properties":{"name":["The Queen's College, Oxford University"],"org":["The Queen's College, Oxford University"],"description":["BA Hons (I) Physics"],"start":["1973-09"],"end":["1976-06"]}}]}}],"rels":{},"rel-urls":{}}; - - it('education', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hresume/education +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hresume', function() { + var htmlFragment = "
\n
\n

Tim Berners-Lee

\n

Director of the World Wide Web Foundation

\n
\n

Invented the World Wide Web.


\n

\n The Queen's College, Oxford University, \n BA Hons (I) Physics \n –\n \n

\n
"; + var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"],"job-title":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"education":[{"value":"The Queen's College, Oxford University","type":["h-event","h-card"],"properties":{"name":["The Queen's College, Oxford University"],"org":["The Queen's College, Oxford University"],"description":["BA Hons (I) Physics"],"start":["1973-09"],"end":["1976-06"]}}]}}],"rels":{},"rel-urls":{}}; + + it('education', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-skill.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-skill.js index d52021f387..b082567004 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-skill.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-skill.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hresume/skill -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hresume', function() { - var htmlFragment = "
\n

\n Tim Berners-Lee, \n invented the World Wide Web.\n

\n Skills: \n \n
"; - var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"]}}],"summary":["invented the World Wide Web"],"skill":["information systems","advocacy","leadership"]}}],"rels":{"tag":["http://example.com/skills/informationsystems","http://example.com/skills/advocacy","http://example.com/skills/leadership"]},"rel-urls":{"http://example.com/skills/informationsystems":{"text":"information systems","rels":["tag"]},"http://example.com/skills/advocacy":{"text":"advocacy","rels":["tag"]},"http://example.com/skills/leadership":{"text":"leadership","rels":["tag"]}}}; - - it('skill', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hresume/skill +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hresume', function() { + var htmlFragment = "
\n

\n Tim Berners-Lee, \n invented the World Wide Web.\n

\n Skills: \n \n
"; + var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"]}}],"summary":["invented the World Wide Web"],"skill":["information systems","advocacy","leadership"]}}],"rels":{"tag":["http://example.com/skills/informationsystems","http://example.com/skills/advocacy","http://example.com/skills/leadership"]},"rel-urls":{"http://example.com/skills/informationsystems":{"text":"information systems","rels":["tag"]},"http://example.com/skills/advocacy":{"text":"advocacy","rels":["tag"]},"http://example.com/skills/leadership":{"text":"leadership","rels":["tag"]}}}; + + it('skill', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-work.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-work.js index d5672b1dad..4ece3a3890 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-work.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hresume-work.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hresume/work -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hresume', function() { - var htmlFragment = "\n
\n
\n

Tim Berners-Lee

\n

Director of the World Wide Web Foundation

\n
\n

Invented the World Wide Web.


\n
\n

Director

\n

World Wide Web Foundation

\n

\n – Present\n \n

\n
\n
"; - var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"],"job-title":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"experience":[{"value":"World Wide Web Foundation","type":["h-event","h-card"],"properties":{"job-title":["Director"],"name":["World Wide Web Foundation"],"org":["World Wide Web Foundation"],"url":["http://www.webfoundation.org/"],"start":["2009-01-18"],"duration":["P2Y11M"]}}]}}],"rels":{},"rel-urls":{}}; - - it('work', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hresume/work +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hresume', function() { + var htmlFragment = "\n
\n
\n

Tim Berners-Lee

\n

Director of the World Wide Web Foundation

\n
\n

Invented the World Wide Web.


\n
\n

Director

\n

World Wide Web Foundation

\n

\n – Present\n \n

\n
\n
"; + var expected = {"items":[{"type":["h-resume"],"properties":{"contact":[{"value":"Tim Berners-Lee","type":["h-card"],"properties":{"name":["Tim Berners-Lee"],"job-title":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"experience":[{"value":"World Wide Web Foundation","type":["h-event","h-card"],"properties":{"job-title":["Director"],"name":["World Wide Web Foundation"],"org":["World Wide Web Foundation"],"url":["http://www.webfoundation.org/"],"start":["2009-01-18"],"duration":["P2Y11M"]}}]}}],"rels":{},"rel-urls":{}}; + + it('work', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-hcard.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-hcard.js index 012f12bc0c..6cdf65484f 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-hcard.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-hcard.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hreview-aggregate/hcard -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hreview-aggregate', function() { - var htmlFragment = "
\n
\n

Mediterranean Wraps

\n

\n \n 433 S California Ave, \n Palo Alto, \n CA - \n \n (650) 321-8189\n

\n
\n

\n 9.2 out of \n 10 \n based on 17 reviews\n

\n
"; - var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Mediterranean Wraps","type":["h-item","h-card"],"properties":{"name":["Mediterranean Wraps"],"org":["Mediterranean Wraps"],"adr":[{"value":"433 S California Ave, \n Palo Alto, \n CA","type":["h-adr"],"properties":{"street-address":["433 S California Ave"],"locality":["Palo Alto"],"region":["CA"]}}],"tel":["(650) 321-8189"]}}],"rating":["9.2"],"average":["9.2"],"best":["10"],"count":["17"]}}],"rels":{},"rel-urls":{}}; - - it('hcard', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hreview-aggregate/hcard +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hreview-aggregate', function() { + var htmlFragment = "
\n
\n

Mediterranean Wraps

\n

\n \n 433 S California Ave, \n Palo Alto, \n CA - \n \n (650) 321-8189\n

\n
\n

\n 9.2 out of \n 10 \n based on 17 reviews\n

\n
"; + var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Mediterranean Wraps","type":["h-item","h-card"],"properties":{"name":["Mediterranean Wraps"],"org":["Mediterranean Wraps"],"adr":[{"value":"433 S California Ave, \n Palo Alto, \n CA","type":["h-adr"],"properties":{"street-address":["433 S California Ave"],"locality":["Palo Alto"],"region":["CA"]}}],"tel":["(650) 321-8189"]}}],"rating":["9.2"],"average":["9.2"],"best":["10"],"count":["17"]}}],"rels":{},"rel-urls":{}}; + + it('hcard', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-justahyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-justahyperlink.js index 50c9c2e217..56d106fdbf 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-justahyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-justahyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hreview-aggregate/justahyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hreview-aggregate', function() { - var htmlFragment = "

\n \n Mediterranean Wraps\n - Rated: \n 4.5 out of 5 (6 reviews)\n

"; - var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Mediterranean Wraps","type":["h-item"],"properties":{"name":["Mediterranean Wraps"],"url":["http://example.com/mediterraneanwraps"]}}],"rating":["4.5"],"count":["6"]}}],"rels":{},"rel-urls":{}}; - - it('justahyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hreview-aggregate/justahyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hreview-aggregate', function() { + var htmlFragment = "

\n \n Mediterranean Wraps\n - Rated: \n 4.5 out of 5 (6 reviews)\n

"; + var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Mediterranean Wraps","type":["h-item"],"properties":{"name":["Mediterranean Wraps"],"url":["http://example.com/mediterraneanwraps"]}}],"rating":["4.5"],"count":["6"]}}],"rels":{},"rel-urls":{}}; + + it('justahyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-vevent.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-vevent.js index f9ddd42ce0..896bbdc1d3 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-vevent.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-aggregate-vevent.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hreview-aggregate/vevent -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hreview-aggregate', function() { - var htmlFragment = "
\n
\n

Fullfrontal

\n

A one day JavaScript Conference held in Brighton

\n

\n
\n \n

\n 9.9 out of \n 10 \n based on 62 reviews\n

\n
"; - var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Fullfrontal","type":["h-item","h-event"],"properties":{"name":["Fullfrontal"],"description":["A one day JavaScript Conference held in Brighton"],"start":["2012-11-09"]}}],"rating":["9.9"],"average":["9.9"],"best":["10"],"count":["62"]}}],"rels":{},"rel-urls":{}}; - - it('vevent', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hreview-aggregate/vevent +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hreview-aggregate', function() { + var htmlFragment = "
\n
\n

Fullfrontal

\n

A one day JavaScript Conference held in Brighton

\n

\n
\n \n

\n 9.9 out of \n 10 \n based on 62 reviews\n

\n
"; + var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Fullfrontal","type":["h-item","h-event"],"properties":{"name":["Fullfrontal"],"description":["A one day JavaScript Conference held in Brighton"],"start":["2012-11-09"]}}],"rating":["9.9"],"average":["9.9"],"best":["10"],"count":["62"]}}],"rels":{},"rel-urls":{}}; + + it('vevent', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-item.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-item.js index fda082906b..4a00ac46a1 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-item.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-item.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hreview/item -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hreview', function() { - var htmlFragment = "\n
\n

\n \n Crepes on Cole\n

\n

5 out of 5 stars

\n
"; - var expected = {"items":[{"type":["h-review"],"properties":{"item":[{"value":"Crepes on Cole","type":["h-item"],"properties":{"photo":["http://example.com/images/photo.gif"],"name":["Crepes on Cole"],"url":["http://example.com/crepeoncole"]}}],"rating":["5"]}}],"rels":{},"rel-urls":{}}; - - it('item', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hreview/item +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hreview', function() { + var htmlFragment = "\n
\n

\n \n Crepes on Cole\n

\n

5 out of 5 stars

\n
"; + var expected = {"items":[{"type":["h-review"],"properties":{"item":[{"value":"Crepes on Cole","type":["h-item"],"properties":{"photo":["http://example.com/images/photo.gif"],"name":["Crepes on Cole"],"url":["http://example.com/crepeoncole"]}}],"rating":["5"]}}],"rels":{},"rel-urls":{}}; + + it('item', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-vcard.js b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-vcard.js index addadf2b87..d59decb7fa 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-vcard.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-hreview-vcard.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/hreview/vcard -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('hreview', function() { - var htmlFragment = "
\n 5 out of 5 stars\n

Crepes on Cole is awesome

\n \n Reviewer: Tantek - \n \n \n
\n

\n Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.\n

\n
\n

Visit date: April 2005

\n

Food eaten: crepe

\n

Permanent link for review: http://example.com/crepe

\n

Creative Commons Attribution-ShareAlike License

\n
"; - var expected = {"items":[{"type":["h-review"],"properties":{"rating":["5"],"name":["Crepes on Cole is awesome"],"reviewer":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"]}}],"description":[{"value":"Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.","html":"\n

\n Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.\n

\n "}],"item":[{"value":"Crepes on Cole","type":["h-item","h-card"],"properties":{"name":["Crepes on Cole"],"org":["Crepes on Cole"],"adr":[{"value":"San Francisco","type":["h-adr"],"properties":{"locality":["San Francisco"]}}]}}],"category":["crepe"],"url":["http://example.com/crepe"]}}],"rels":{"tag":["http://en.wikipedia.org/wiki/crepe"],"self":["http://example.com/crepe"],"bookmark":["http://example.com/crepe"],"license":["http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License"]},"rel-urls":{"http://en.wikipedia.org/wiki/crepe":{"text":"crepe","rels":["tag"]},"http://example.com/crepe":{"text":"http://example.com/crepe","rels":["self","bookmark"]},"http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License":{"text":"Creative Commons Attribution-ShareAlike License","rels":["license"]}}}; - - it('vcard', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/hreview/vcard +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('hreview', function() { + var htmlFragment = "
\n 5 out of 5 stars\n

Crepes on Cole is awesome

\n \n Reviewer: Tantek - \n \n \n
\n

\n Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.\n

\n
\n

Visit date: April 2005

\n

Food eaten: crepe

\n

Permanent link for review: http://example.com/crepe

\n

Creative Commons Attribution-ShareAlike License

\n
"; + var expected = {"items":[{"type":["h-review"],"properties":{"rating":["5"],"name":["Crepes on Cole is awesome"],"reviewer":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"]}}],"description":[{"value":"Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.","html":"\n

\n Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.\n

\n "}],"item":[{"value":"Crepes on Cole","type":["h-item","h-card"],"properties":{"name":["Crepes on Cole"],"org":["Crepes on Cole"],"adr":[{"value":"San Francisco","type":["h-adr"],"properties":{"locality":["San Francisco"]}}]}}],"category":["crepe"],"url":["http://example.com/crepe"]}}],"rels":{"tag":["http://en.wikipedia.org/wiki/crepe"],"self":["http://example.com/crepe"],"bookmark":["http://example.com/crepe"],"license":["http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License"]},"rel-urls":{"http://en.wikipedia.org/wiki/crepe":{"text":"crepe","rels":["tag"]},"http://example.com/crepe":{"text":"http://example.com/crepe","rels":["self","bookmark"]},"http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License":{"text":"Creative Commons Attribution-ShareAlike License","rels":["license"]}}}; + + it('vcard', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-hcarditemref.js b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-hcarditemref.js index f72c401cbe..5ffa373d75 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-hcarditemref.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-hcarditemref.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/includes/hcarditemref -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('includes', function() { - var htmlFragment = "
\n Brendan Eich\n
\n
\n Mitchell Baker\n
\n\n

Mozilla

\n

\n 665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A. \n

"; - var expected = {"items":[{"type":["h-card"],"properties":{"org":["Mozilla"],"adr":[{"value":"665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A.","type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."]}}]}},{"type":["h-card"],"properties":{"org":["Mozilla"],"adr":[{"value":"665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A.","type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."]}}]}},{"type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."]}}],"rels":{},"rel-urls":{}}; - - it('hcarditemref', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/includes/hcarditemref +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('includes', function() { + var htmlFragment = "
\n Brendan Eich\n
\n
\n Mitchell Baker\n
\n\n

Mozilla

\n

\n 665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A. \n

"; + var expected = {"items":[{"type":["h-card"],"properties":{"org":["Mozilla"],"adr":[{"value":"665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A.","type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."]}}]}},{"type":["h-card"],"properties":{"org":["Mozilla"],"adr":[{"value":"665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A.","type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."]}}]}},{"type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."]}}],"rels":{},"rel-urls":{}}; + + it('hcarditemref', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-heventitemref.js b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-heventitemref.js index c37612ce28..b3a16025bb 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-heventitemref.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-heventitemref.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/includes/heventitemref -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('includes', function() { - var htmlFragment = "
\n Monetizing Android Apps - spaekers: \n Chrix Finne, \n Kenneth Lui - \n \n Room 10\n \n
\n
\n New Low-Level Media APIs in Android - spaekers: \n Dave Burke -\n \n Room 11\n \n
\n\n

\n Session 01 is between: \n to \n \n

\n

\n Moscone Center, \n San Francisco \n

"; - var expected = {"items":[{"type":["h-event"],"properties":{"location":[{"value":"Room 10\n \n Moscone Center, \n San Francisco","type":["h-adr"],"properties":{"extended-address":["Room 10","Moscone Center"],"locality":["San Francisco"]}}],"start":["2012-06-27 15:45:00-08:00"],"end":["2012-06-27 16:45:00-08:00"]}},{"type":["h-event"],"properties":{"location":[{"value":"Room 11\n \n Moscone Center, \n San Francisco","type":["h-adr"],"properties":{"extended-address":["Room 11","Moscone Center"],"locality":["San Francisco"]}}],"start":["2012-06-27 15:45:00-08:00"],"end":["2012-06-27 16:45:00-08:00"]}}],"rels":{},"rel-urls":{}}; - - it('heventitemref', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/includes/heventitemref +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('includes', function() { + var htmlFragment = "
\n Monetizing Android Apps - spaekers: \n Chrix Finne, \n Kenneth Lui - \n \n Room 10\n \n
\n
\n New Low-Level Media APIs in Android - spaekers: \n Dave Burke -\n \n Room 11\n \n
\n\n

\n Session 01 is between: \n to \n \n

\n

\n Moscone Center, \n San Francisco \n

"; + var expected = {"items":[{"type":["h-event"],"properties":{"location":[{"value":"Room 10\n \n Moscone Center, \n San Francisco","type":["h-adr"],"properties":{"extended-address":["Room 10","Moscone Center"],"locality":["San Francisco"]}}],"start":["2012-06-27 15:45:00-08:00"],"end":["2012-06-27 16:45:00-08:00"]}},{"type":["h-event"],"properties":{"location":[{"value":"Room 11\n \n Moscone Center, \n San Francisco","type":["h-adr"],"properties":{"extended-address":["Room 11","Moscone Center"],"locality":["San Francisco"]}}],"start":["2012-06-27 15:45:00-08:00"],"end":["2012-06-27 16:45:00-08:00"]}}],"rels":{},"rel-urls":{}}; + + it('heventitemref', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-hyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-hyperlink.js index dbda015aa7..3a789bb1ba 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-hyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-hyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/includes/hyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('includes', function() { - var htmlFragment = "
\n Ben Ward\n Twitter\n
\n
\n Dan Webb\n Twitter\n
\n\n
\n

Twitter

\n

\n 1355 Market St,\n San Francisco, \n CA\n 94103\n

\n
"; - var expected = {"items":[{"type":["h-card"],"properties":{"org":["Twitter"],"adr":[{"value":"1355 Market St,\n San Francisco, \n CA\n 94103","type":["h-adr"],"properties":{"street-address":["1355 Market St"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94103"]}}]}},{"type":["h-card"],"properties":{"org":["Twitter"],"adr":[{"value":"1355 Market St,\n San Francisco, \n CA\n 94103","type":["h-adr"],"properties":{"street-address":["1355 Market St"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94103"]}}]}},{"type":["h-adr"],"properties":{"street-address":["1355 Market St"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94103"]}}],"rels":{},"rel-urls":{}}; - - it('hyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/includes/hyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('includes', function() { + var htmlFragment = "
\n Ben Ward\n Twitter\n
\n
\n Dan Webb\n Twitter\n
\n\n
\n

Twitter

\n

\n 1355 Market St,\n San Francisco, \n CA\n 94103\n

\n
"; + var expected = {"items":[{"type":["h-card"],"properties":{"org":["Twitter"],"adr":[{"value":"1355 Market St,\n San Francisco, \n CA\n 94103","type":["h-adr"],"properties":{"street-address":["1355 Market St"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94103"]}}]}},{"type":["h-card"],"properties":{"org":["Twitter"],"adr":[{"value":"1355 Market St,\n San Francisco, \n CA\n 94103","type":["h-adr"],"properties":{"street-address":["1355 Market St"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94103"]}}]}},{"type":["h-adr"],"properties":{"street-address":["1355 Market St"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94103"]}}],"rels":{},"rel-urls":{}}; + + it('hyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-object.js b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-object.js index 0f0328102a..3bc15bd459 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-object.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-object.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/includes/object -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('includes', function() { - var htmlFragment = "
\n HTML5 & CSS3 latest features in action! - \n David Rousset -\n \n \n
\n
\n Building High-Performing JavaScript for Modern Engines -\n John-David Dalton and \n Amanda Silver -\n \n \n
\n\n\n
\n

Build Conference

\n

\n Redmond, \n Washington, \n USA\n

\n
"; - var expected = {"items":[{"type":["h-event"],"properties":{"start":["2012-10-30 11:45:00-08:00"],"name":["Build Conference"],"location":[{"value":"Redmond, \n Washington, \n USA","type":["h-adr"],"properties":{"locality":["Redmond"],"region":["Washington"],"country-name":["USA"]}}]}},{"type":["h-event"],"properties":{"start":["2012-10-31 11:15:00-08:00"],"name":["Build Conference"],"location":[{"value":"Redmond, \n Washington, \n USA","type":["h-adr"],"properties":{"locality":["Redmond"],"region":["Washington"],"country-name":["USA"]}}]}},{"type":["h-adr"],"properties":{"locality":["Redmond"],"region":["Washington"],"country-name":["USA"]}}],"rels":{},"rel-urls":{}}; - - it('object', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/includes/object +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('includes', function() { + var htmlFragment = "
\n HTML5 & CSS3 latest features in action! - \n David Rousset -\n \n \n
\n
\n Building High-Performing JavaScript for Modern Engines -\n John-David Dalton and \n Amanda Silver -\n \n \n
\n\n\n
\n

Build Conference

\n

\n Redmond, \n Washington, \n USA\n

\n
"; + var expected = {"items":[{"type":["h-event"],"properties":{"start":["2012-10-30 11:45:00-08:00"],"name":["Build Conference"],"location":[{"value":"Redmond, \n Washington, \n USA","type":["h-adr"],"properties":{"locality":["Redmond"],"region":["Washington"],"country-name":["USA"]}}]}},{"type":["h-event"],"properties":{"start":["2012-10-31 11:15:00-08:00"],"name":["Build Conference"],"location":[{"value":"Redmond, \n Washington, \n USA","type":["h-adr"],"properties":{"locality":["Redmond"],"region":["Washington"],"country-name":["USA"]}}]}},{"type":["h-adr"],"properties":{"locality":["Redmond"],"region":["Washington"],"country-name":["USA"]}}],"rels":{},"rel-urls":{}}; + + it('object', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-table.js b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-table.js index bac4c97b22..a0d3ef55c1 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v1-includes-table.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v1-includes-table.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v1/includes/table -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('includes', function() { - var htmlFragment = "\n\n \n \n \n \n \n \n \n \n \n
Opera
Chris Mills
Erik Möller
"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Chris Mills"],"url":["http://dev.opera.com/"],"org":["Opera"]}},{"type":["h-card"],"properties":{"name":["Erik Möller"],"url":["http://dev.opera.com/"],"org":["Opera"]}}],"rels":{},"rel-urls":{}}; - - it('table', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v1/includes/table +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('includes', function() { + var htmlFragment = "\n\n \n \n \n \n \n \n \n \n \n
Opera
Chris Mills
Erik Möller
"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Chris Mills"],"url":["http://dev.opera.com/"],"org":["Opera"]}},{"type":["h-card"],"properties":{"name":["Erik Möller"],"url":["http://dev.opera.com/"],"org":["Opera"]}}],"rels":{},"rel-urls":{}}; + + it('table', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-geo.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-geo.js index 77f5712a66..8ed7d747d4 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-geo.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-geo.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-adr/geo -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-adr', function() { - var htmlFragment = "

\n Bricklayer's Arms\n \n 3 Charlotte Road, \n City of London, \n EC2A 3PE, \n UK \n – \n Geo:(51.526421;-0.081067) \n

"; - var expected = {"items":[{"type":["h-adr"],"properties":{"name":["Bricklayer's Arms"],"label":["3 Charlotte Road, \n City of London, \n EC2A 3PE, \n UK"],"street-address":["3 Charlotte Road"],"locality":["City of London"],"postal-code":["EC2A 3PE"],"country-name":["UK"],"geo":["51.526421;-0.081067"]}}],"rels":{},"rel-urls":{}}; - - it('geo', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-adr/geo +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-adr', function() { + var htmlFragment = "

\n Bricklayer's Arms\n \n 3 Charlotte Road, \n City of London, \n EC2A 3PE, \n UK \n – \n Geo:(51.526421;-0.081067) \n

"; + var expected = {"items":[{"type":["h-adr"],"properties":{"name":["Bricklayer's Arms"],"label":["3 Charlotte Road, \n City of London, \n EC2A 3PE, \n UK"],"street-address":["3 Charlotte Road"],"locality":["City of London"],"postal-code":["EC2A 3PE"],"country-name":["UK"],"geo":["51.526421;-0.081067"]}}],"rels":{},"rel-urls":{}}; + + it('geo', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-geourl.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-geourl.js index 1c0192fcd5..b97e76f60a 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-geourl.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-geourl.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-adr/geourl -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-adr', function() { - var htmlFragment = "

\n Bricklayer's Arms, \n London \n

"; - var expected = {"items":[{"type":["h-adr"],"properties":{"name":["Bricklayer's Arms"],"geo":["geo:51.526421;-0.081067;crs=wgs84;u=40"],"locality":["London"],"url":["geo:51.526421;-0.081067;crs=wgs84;u=40"]}}],"rels":{},"rel-urls":{}}; - - it('geourl', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-adr/geourl +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-adr', function() { + var htmlFragment = "

\n Bricklayer's Arms, \n London \n

"; + var expected = {"items":[{"type":["h-adr"],"properties":{"name":["Bricklayer's Arms"],"geo":["geo:51.526421;-0.081067;crs=wgs84;u=40"],"locality":["London"],"url":["geo:51.526421;-0.081067;crs=wgs84;u=40"]}}],"rels":{},"rel-urls":{}}; + + it('geourl', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-justaname.js index a6231cca9f..c943fbafce 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-adr/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-adr', function() { - var htmlFragment = "

665 3rd St. Suite 207 San Francisco, CA 94107 U.S.A.

"; - var expected = {"items":[{"type":["h-adr"],"properties":{"name":["665 3rd St. Suite 207 San Francisco, CA 94107 U.S.A."]}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-adr/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-adr', function() { + var htmlFragment = "

665 3rd St. Suite 207 San Francisco, CA 94107 U.S.A.

"; + var expected = {"items":[{"type":["h-adr"],"properties":{"name":["665 3rd St. Suite 207 San Francisco, CA 94107 U.S.A."]}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-simpleproperties.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-simpleproperties.js index bbf6017f80..084dac4405 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-simpleproperties.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-adr-simpleproperties.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-adr/simpleproperties -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-adr', function() { - var htmlFragment = "

\n 665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A. \n

"; - var expected = {"items":[{"type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."],"name":["665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A."]}}],"rels":{},"rel-urls":{}}; - - it('simpleproperties', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-adr/simpleproperties +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-adr', function() { + var htmlFragment = "

\n 665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A. \n

"; + var expected = {"items":[{"type":["h-adr"],"properties":{"street-address":["665 3rd St."],"extended-address":["Suite 207"],"locality":["San Francisco"],"region":["CA"],"postal-code":["94107"],"country-name":["U.S.A."],"name":["665 3rd St. \n Suite 207 \n San Francisco, \n CA \n 94107 \n U.S.A."]}}],"rels":{},"rel-urls":{}}; + + it('simpleproperties', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-as-note-note.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-as-note-note.js index 9795141ce6..7e0ac260c4 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-as-note-note.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-as-note-note.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-as-note/note -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-as-note', function() { - var htmlFragment = "\n\n\n
  • \n
    \n \n
    \n \n \"Tantek\n

    \n @benwerd\n @erinjoalso proud of you &\n @withknown— so much #indieweb & especially user empathy. Keep up the great work!

    \n \n \n on\n \n \n (ttk.me t4bT2)using\n BBEdit\n \n
    \n
    \n
    \n \n
    \n
    \n
    \n \n
  • "; - var expected = {"items":[{"type":["h-entry","h-as-note"],"properties":{"in-reply-to":[{"value":"http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far","type":["h-cite"],"properties":{"name":["http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far"],"url":["http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far"]}},{"value":"https://twitter.com/benwerd/status/604733231284383744","type":["h-cite"],"properties":{"name":["https://twitter.com/benwerd/status/604733231284383744"],"url":["https://twitter.com/benwerd/status/604733231284383744"]}}],"author":[{"type":["h-card"],"properties":{"name":["Tantek Çelik"],"photo":["http://tantek.com/images/photo.gif"],"url":["http://tantek.com/"]}}],"name":["@benwerd\n @erinjoalso proud of you &\n @withknown— so much #indieweb & especially user empathy. Keep up the great work!"],"content":[{"value":"@benwerd\n @erinjoalso proud of you &\n @withknown— so much #indieweb & especially user empathy. Keep up the great work!","html":"\n @benwerd\n @erinjoalso proud of you &\n @withknown— so much #indieweb & especially user empathy. Keep up the great work!"}],"published":["2015-06-01 22:20-07:00"],"updated":["2015-06-01 22:20-07:00"],"url":["http://tantek.com/2015/152/t2/proud-withknown-indieweb-user-empathy"],"uid":["http://tantek.com/2015/152/t2/proud-withknown-indieweb-user-empathy"],"syndication":["https://twitter.com/t/status/605604965566906369"]},"children":[{"value":"@benwerd","type":["h-x-username"],"properties":{"name":["@benwerd"],"url":["https://twitter.com/benwerd"]}},{"value":"@erinjo","type":["h-x-username"],"properties":{"name":["@erinjo"],"url":["https://twitter.com/erinjo"]}},{"value":"@withknown","type":["h-x-username"],"properties":{"name":["@withknown"],"url":["https://twitter.com/withknown"]}}]}],"rels":{"prev":["http://tantek.com/152/t1/congrats-fellow-elected-w3cab-members"],"next":["http://tantek.com/152/t3/going-indiewebcamp-2015-portland"],"in-reply-to":["http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far","https://twitter.com/benwerd/status/604733231284383744"],"author":["http://tantek.com/"],"syndication":["https://twitter.com/t/status/605604965566906369"]},"rel-urls":{"http://tantek.com/152/t1/congrats-fellow-elected-w3cab-members":{"title":"View the previous (older) item in the stream.","text":"←","rels":["prev"]},"http://tantek.com/152/t3/going-indiewebcamp-2015-portland":{"title":"View the next (newer) item in the stream","text":"→","rels":["next"]},"http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far":{"text":"http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far","rels":["in-reply-to"]},"https://twitter.com/benwerd/status/604733231284383744":{"text":"https://twitter.com/benwerd/status/604733231284383744","rels":["in-reply-to"]},"http://tantek.com/":{"title":"Tantek Çelik","rels":["author"]},"https://twitter.com/t/status/605604965566906369":{"text":"View \n Conversation\n on Twitter","rels":["syndication"]}}}; - - it('note', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-as-note/note +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-as-note', function() { + var htmlFragment = "\n\n\n
  • \n
    \n \n
    \n \n \"Tantek\n

    \n @benwerd\n @erinjoalso proud of you &\n @withknown— so much #indieweb & especially user empathy. Keep up the great work!

    \n \n \n on\n \n \n (ttk.me t4bT2)using\n BBEdit\n \n
    \n
    \n
    \n \n
    \n
    \n
    \n \n
  • "; + var expected = {"items":[{"type":["h-entry","h-as-note"],"properties":{"in-reply-to":[{"value":"http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far","type":["h-cite"],"properties":{"name":["http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far"],"url":["http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far"]}},{"value":"https://twitter.com/benwerd/status/604733231284383744","type":["h-cite"],"properties":{"name":["https://twitter.com/benwerd/status/604733231284383744"],"url":["https://twitter.com/benwerd/status/604733231284383744"]}}],"author":[{"type":["h-card"],"properties":{"name":["Tantek Çelik"],"photo":["http://tantek.com/images/photo.gif"],"url":["http://tantek.com/"]}}],"name":["@benwerd\n @erinjoalso proud of you &\n @withknown— so much #indieweb & especially user empathy. Keep up the great work!"],"content":[{"value":"@benwerd\n @erinjoalso proud of you &\n @withknown— so much #indieweb & especially user empathy. Keep up the great work!","html":"\n @benwerd\n @erinjoalso proud of you &\n @withknown— so much #indieweb & especially user empathy. Keep up the great work!"}],"published":["2015-06-01 22:20-07:00"],"updated":["2015-06-01 22:20-07:00"],"url":["http://tantek.com/2015/152/t2/proud-withknown-indieweb-user-empathy"],"uid":["http://tantek.com/2015/152/t2/proud-withknown-indieweb-user-empathy"],"syndication":["https://twitter.com/t/status/605604965566906369"]},"children":[{"value":"@benwerd","type":["h-x-username"],"properties":{"name":["@benwerd"],"url":["https://twitter.com/benwerd"]}},{"value":"@erinjo","type":["h-x-username"],"properties":{"name":["@erinjo"],"url":["https://twitter.com/erinjo"]}},{"value":"@withknown","type":["h-x-username"],"properties":{"name":["@withknown"],"url":["https://twitter.com/withknown"]}}]}],"rels":{"prev":["http://tantek.com/152/t1/congrats-fellow-elected-w3cab-members"],"next":["http://tantek.com/152/t3/going-indiewebcamp-2015-portland"],"in-reply-to":["http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far","https://twitter.com/benwerd/status/604733231284383744"],"author":["http://tantek.com/"],"syndication":["https://twitter.com/t/status/605604965566906369"]},"rel-urls":{"http://tantek.com/152/t1/congrats-fellow-elected-w3cab-members":{"title":"View the previous (older) item in the stream.","text":"←","rels":["prev"]},"http://tantek.com/152/t3/going-indiewebcamp-2015-portland":{"title":"View the next (newer) item in the stream","text":"→","rels":["next"]},"http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far":{"text":"http://werd.io/2015/im-super-proud-of-everything-weve-done-on-withknown-so-far","rels":["in-reply-to"]},"https://twitter.com/benwerd/status/604733231284383744":{"text":"https://twitter.com/benwerd/status/604733231284383744","rels":["in-reply-to"]},"http://tantek.com/":{"title":"Tantek Çelik","rels":["author"]},"https://twitter.com/t/status/605604965566906369":{"text":"View \n Conversation\n on Twitter","rels":["syndication"]}}}; + + it('note', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-baseurl.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-baseurl.js index bf456cd3be..d098db3926 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-baseurl.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-baseurl.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/baseurl -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "\n"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mitchell Baker"],"url":["http://blog.lizardwrangler.com/"],"org":[{"value":"Mozilla Foundation","type":["h-card"],"properties":{"name":["Mozilla Foundation"],"url":["http://example.org/bios/mitchell-baker/"]}}],"photo":["http://example.org/images/photo.gif"]}}],"rels":{},"rel-urls":{}}; - - it('baseurl', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/baseurl +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "\n"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mitchell Baker"],"url":["http://blog.lizardwrangler.com/"],"org":[{"value":"Mozilla Foundation","type":["h-card"],"properties":{"name":["Mozilla Foundation"],"url":["http://example.org/bios/mitchell-baker/"]}}],"photo":["http://example.org/images/photo.gif"]}}],"rels":{},"rel-urls":{}}; + + it('baseurl', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-childimplied.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-childimplied.js index ae61d554ee..3ab1fa8ca6 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-childimplied.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-childimplied.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/childimplied -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "\n\n
    \n

    Håkon Wium Lie

    \n \n
    \n
    "; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Håkon Wium Lie"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/9/96/H%C3%A5kon-Wium-Lie-2009-03.jpg/215px-H%C3%A5kon-Wium-Lie-2009-03.jpg"],"url":["http://people.opera.com/howcome/"]}}],"rels":{},"rel-urls":{}}; - - it('childimplied', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/childimplied +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "\n\n
    \n

    Håkon Wium Lie

    \n \n
    \n
    "; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Håkon Wium Lie"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/9/96/H%C3%A5kon-Wium-Lie-2009-03.jpg/215px-H%C3%A5kon-Wium-Lie-2009-03.jpg"],"url":["http://people.opera.com/howcome/"]}}],"rels":{},"rel-urls":{}}; + + it('childimplied', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-extendeddescription.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-extendeddescription.js index 9af48c7ed3..8ee35b022b 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-extendeddescription.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-extendeddescription.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/extendeddescription -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "
    \n \"photo\n

    \n Mitchell Baker\n (@MitchellBaker)\n Mozilla Foundation\n

    \n

    Mitchell is responsible for setting the direction and scope of the Mozilla Foundation and its activities.

    \n

    Strategy and Leadership

    \n
    "; - var expected = {"items":[{"type":["h-card"],"properties":{"photo":["http://blog.mozilla.org/press/files/2012/04/mitchell-baker.jpg"],"url":["http://blog.lizardwrangler.com/","https://twitter.com/MitchellBaker"],"name":["Mitchell Baker"],"org":["Mozilla Foundation"],"note":["Mitchell is responsible for setting the direction and scope of the Mozilla Foundation and its activities."],"category":["Strategy","Leadership"]}}],"rels":{},"rel-urls":{}}; - - it('extendeddescription', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/extendeddescription +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "
    \n \"photo\n

    \n Mitchell Baker\n (@MitchellBaker)\n Mozilla Foundation\n

    \n

    Mitchell is responsible for setting the direction and scope of the Mozilla Foundation and its activities.

    \n

    Strategy and Leadership

    \n
    "; + var expected = {"items":[{"type":["h-card"],"properties":{"photo":["http://blog.mozilla.org/press/files/2012/04/mitchell-baker.jpg"],"url":["http://blog.lizardwrangler.com/","https://twitter.com/MitchellBaker"],"name":["Mitchell Baker"],"org":["Mozilla Foundation"],"note":["Mitchell is responsible for setting the direction and scope of the Mozilla Foundation and its activities."],"category":["Strategy","Leadership"]}}],"rels":{},"rel-urls":{}}; + + it('extendeddescription', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-hcard.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-hcard.js index c9536f005c..9613816109 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-hcard.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-hcard.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/hcard -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = ""; - var expected = {"items":[{"type":["h-card"],"properties":{"url":["http://blog.lizardwrangler.com/"],"name":["Mitchell Baker"],"org":[{"value":"Mozilla Foundation","type":["h-card"],"properties":{"name":["Mozilla Foundation"],"url":["http://mozilla.org/"]}}]}}],"rels":{},"rel-urls":{}}; - - it('hcard', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/hcard +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = ""; + var expected = {"items":[{"type":["h-card"],"properties":{"url":["http://blog.lizardwrangler.com/"],"name":["Mitchell Baker"],"org":[{"value":"Mozilla Foundation","type":["h-card"],"properties":{"name":["Mozilla Foundation"],"url":["http://mozilla.org/"]}}]}}],"rels":{},"rel-urls":{}}; + + it('hcard', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-horghcard.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-horghcard.js index 5e19b9bd17..bc6329ae62 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-horghcard.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-horghcard.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/horghcard -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = ""; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mitchell Baker"],"url":["http://blog.lizardwrangler.com/"],"org":[{"value":"Mozilla Foundation","type":["h-card","h-org"],"properties":{"name":["Mozilla Foundation"],"url":["http://mozilla.org/"]}}]}}],"rels":{},"rel-urls":{}}; - - it('horghcard', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/horghcard +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = ""; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mitchell Baker"],"url":["http://blog.lizardwrangler.com/"],"org":[{"value":"Mozilla Foundation","type":["h-card","h-org"],"properties":{"name":["Mozilla Foundation"],"url":["http://mozilla.org/"]}}]}}],"rels":{},"rel-urls":{}}; + + it('horghcard', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-hyperlinkedphoto.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-hyperlinkedphoto.js index 281fcd1989..70febcb844 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-hyperlinkedphoto.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-hyperlinkedphoto.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/hyperlinkedphoto -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "\n \"Rohit\n "; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Rohit Khare"],"photo":["http://example.com/images/photo.gif"],"url":["http://rohit.khare.org/"]}}],"rels":{},"rel-urls":{}}; - - it('hyperlinkedphoto', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/hyperlinkedphoto +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "\n \"Rohit\n "; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Rohit Khare"],"photo":["http://example.com/images/photo.gif"],"url":["http://rohit.khare.org/"]}}],"rels":{},"rel-urls":{}}; + + it('hyperlinkedphoto', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedname.js index b28ddc55ef..bcf45aecb6 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/impliedname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "\n\"Jane\n\"Jane\nJD\n\n
    \"Jane
    \n
    \"Jane
    \n
    JD
    \n\n
    \"Jane
    \n
    \"Jane
    \n
    JD
    \n\n
    \"JohnName
    \n
    \"JohnName
    \n"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]}},{"type":["h-card"],"properties":{"name":["Name"]},"children":[{"type":["h-card"],"properties":{"name":["John Doe"],"photo":["http://example.com/john.html"]}}]},{"type":["h-card"],"properties":{"name":["Name"]},"children":[{"value":"Name","type":["h-card"],"properties":{"name":["John Doe"],"photo":["http://example.com/john.html"]}}]}],"rels":{},"rel-urls":{}}; - - it('impliedname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/impliedname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "\n\"Jane\n\"Jane\nJD\n\n
    \"Jane
    \n
    \"Jane
    \n
    JD
    \n\n
    \"Jane
    \n
    \"Jane
    \n
    JD
    \n\n
    \"JohnName
    \n
    \"JohnName
    \n"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]}},{"type":["h-card"],"properties":{"name":["Name"]},"children":[{"type":["h-card"],"properties":{"name":["John Doe"],"photo":["http://example.com/john.html"]}}]},{"type":["h-card"],"properties":{"name":["Name"]},"children":[{"value":"Name","type":["h-card"],"properties":{"name":["John Doe"],"photo":["http://example.com/john.html"]}}]}],"rels":{},"rel-urls":{}}; + + it('impliedname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedphoto.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedphoto.js index 5933bf6ba8..3248a1d0fc 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedphoto.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedphoto.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/impliedphoto -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "\"Jane\nJane Doe\n\n
    \"Jane
    \n
    Jane Doe
    \n\n
    \"Jane
    \n
    Jane Doe
    \n\n
    \"JaneJane Doe
    \n
    Jane Doe
    "; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]},"children":[{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}}]},{"type":["h-card"],"properties":{"name":["Jane Doe"]},"children":[{"value":"Jane Doe","type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}}]}],"rels":{},"rel-urls":{}}; - - it('impliedphoto', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/impliedphoto +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "\"Jane\nJane Doe\n\n
    \"Jane
    \n
    Jane Doe
    \n\n
    \"Jane
    \n
    Jane Doe
    \n\n
    \"JaneJane Doe
    \n
    Jane Doe
    "; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]},"children":[{"type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}}]},{"type":["h-card"],"properties":{"name":["Jane Doe"]},"children":[{"value":"Jane Doe","type":["h-card"],"properties":{"name":["Jane Doe"],"photo":["http://example.com/jane.jpeg"]}}]}],"rels":{},"rel-urls":{}}; + + it('impliedphoto', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedurl.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedurl.js index 6d9beb9782..4034194ce9 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedurl.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-impliedurl.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/impliedurl -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "Jane Doe\n\"Jane\n \n
    Jane Doe

    \n "; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]},"children":[{"value":"Jane Doe","type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}}]}],"rels":{},"rel-urls":{}}; - - it('impliedurl', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/impliedurl +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "Jane Doe\n\"Jane\n \n
    Jane Doe

    \n "; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}},{"type":["h-card"],"properties":{"name":["Jane Doe"]},"children":[{"value":"Jane Doe","type":["h-card"],"properties":{"name":["Jane Doe"],"url":["http://example.com/jane.html"]}}]}],"rels":{},"rel-urls":{}}; + + it('impliedurl', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-justahyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-justahyperlink.js index 9a4dad6408..5911cc00fa 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-justahyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-justahyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/justahyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "Ben Ward"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Ben Ward"],"url":["http://benward.me/"]}}],"rels":{},"rel-urls":{}}; - - it('justahyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/justahyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "Ben Ward"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Ben Ward"],"url":["http://benward.me/"]}}],"rels":{},"rel-urls":{}}; + + it('justahyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-justaname.js index 44f0ea6149..4f239fab5c 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "

    Frances Berriman

    "; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Frances Berriman"]}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "

    Frances Berriman

    "; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Frances Berriman"]}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-nested.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-nested.js index 3cf83a8f40..da2336e293 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-nested.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-nested.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/nested -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = ""; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mitchell Baker"],"url":["http://blog.lizardwrangler.com/"]},"children":[{"value":"Mozilla Foundation","type":["h-org","h-card"],"properties":{"name":["Mozilla Foundation"],"url":["http://mozilla.org/"]}}]}],"rels":{},"rel-urls":{}}; - - it('nested', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/nested +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = ""; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mitchell Baker"],"url":["http://blog.lizardwrangler.com/"]},"children":[{"value":"Mozilla Foundation","type":["h-org","h-card"],"properties":{"name":["Mozilla Foundation"],"url":["http://mozilla.org/"]}}]}],"rels":{},"rel-urls":{}}; + + it('nested', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-p-property.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-p-property.js index 6920e1c77e..0a365e34b8 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-p-property.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-p-property.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/p-property -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "
    \n \n \n John \n P \n Doe \n \n \n \n \n
    BSc
    \n
    BA\n \n \n \"PHD\"\n \"company\n \n \"Madgex\"\n \"Mozilla\"\n \n
    "; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["John Doe"],"given-name":["John"],"additional-name":["Peter"],"family-name":["Doe"],"honorific-suffix":["MSc","PHD"],"org":["Madgex","Mozilla"]}}],"rels":{},"rel-urls":{}}; - - it('p-property', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/p-property +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "
    \n \n \n John \n P \n Doe \n \n \n \n \n
    BSc
    \n
    BA\n \n \n \"PHD\"\n \"company\n \n \"Madgex\"\n \"Mozilla\"\n \n
    "; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["John Doe"],"given-name":["John"],"additional-name":["Peter"],"family-name":["Doe"],"honorific-suffix":["MSc","PHD"],"org":["Madgex","Mozilla"]}}],"rels":{},"rel-urls":{}}; + + it('p-property', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-relativeurls.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-relativeurls.js index b9a43d116e..712a8cf722 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-relativeurls.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-card-relativeurls.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-card/relativeurls -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-card', function() { - var htmlFragment = "\n"; - var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mitchell Baker"],"url":["http://blog.lizardwrangler.com/"],"org":[{"value":"Mozilla Foundation","type":["h-card"],"properties":{"name":["Mozilla Foundation"],"url":["http://example.com/bios/mitchell-baker/"]}}],"photo":["http://example.com/bios/mitchell-baker/picture.jpeg"]}}],"rels":{},"rel-urls":{}}; - - it('relativeurls', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-card/relativeurls +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-card', function() { + var htmlFragment = "\n"; + var expected = {"items":[{"type":["h-card"],"properties":{"name":["Mitchell Baker"],"url":["http://blog.lizardwrangler.com/"],"org":[{"value":"Mozilla Foundation","type":["h-card"],"properties":{"name":["Mozilla Foundation"],"url":["http://example.com/bios/mitchell-baker/"]}}],"photo":["http://example.com/bios/mitchell-baker/picture.jpeg"]}}],"rels":{},"rel-urls":{}}; + + it('relativeurls', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-impliedvalue-nested.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-impliedvalue-nested.js index 7420916162..e729b48b0f 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-impliedvalue-nested.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-impliedvalue-nested.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-entry/impliedvalue-nested -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-entry', function() { - var htmlFragment = "
    \n
    \n \n Example Author\n Home\n \n Example Post\n
    \n
    "; - var expected = {"items":[{"type":["h-entry"],"properties":{"in-reply-to":[{"type":["h-cite"],"properties":{"name":["Example Post"],"url":["http://example.com/post"],"author":[{"type":["h-card"],"properties":{"url":["http://example.com"],"name":["Example Author"]},"value":"Example Author"}]},"value":"http://example.com/post"}],"name":["Example Author\n Home\n \n Example Post"]}}],"rels":{},"rel-urls":{}}; - - it('impliedvalue-nested', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-entry/impliedvalue-nested +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-entry', function() { + var htmlFragment = "
    \n
    \n \n Example Author\n Home\n \n Example Post\n
    \n
    "; + var expected = {"items":[{"type":["h-entry"],"properties":{"in-reply-to":[{"type":["h-cite"],"properties":{"name":["Example Post"],"url":["http://example.com/post"],"author":[{"type":["h-card"],"properties":{"url":["http://example.com"],"name":["Example Author"]},"value":"Example Author"}]},"value":"http://example.com/post"}],"name":["Example Author\n Home\n \n Example Post"]}}],"rels":{},"rel-urls":{}}; + + it('impliedvalue-nested', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-justahyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-justahyperlink.js index db514587da..1e793e7273 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-justahyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-justahyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-entry/justahyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-entry', function() { - var htmlFragment = "microformats.org at 7"; - var expected = {"items":[{"type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"]}}],"rels":{},"rel-urls":{}}; - - it('justahyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-entry/justahyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-entry', function() { + var htmlFragment = "microformats.org at 7"; + var expected = {"items":[{"type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"]}}],"rels":{},"rel-urls":{}}; + + it('justahyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-justaname.js index 2f729d58d7..f4d31bf878 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-entry/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-entry', function() { - var htmlFragment = "

    microformats.org at 7

    "; - var expected = {"items":[{"type":["h-entry"],"properties":{"name":["microformats.org at 7"]}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-entry/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-entry', function() { + var htmlFragment = "

    microformats.org at 7

    "; + var expected = {"items":[{"type":["h-entry"],"properties":{"name":["microformats.org at 7"]}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-summarycontent.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-summarycontent.js index 87a6aa29c6..b697f6c7c6 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-summarycontent.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-summarycontent.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-entry/summarycontent -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-entry', function() { - var htmlFragment = "\n
    \n

    microformats.org at 7

    \n
    \n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n
    \n

    Updated \n by\n Tantek\n

    \n
    "; - var expected = {"items":[{"type":["h-entry"],"properties":{"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"name":["microformats.org at 7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"rels":{},"rel-urls":{}}; - - it('summarycontent', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-entry/summarycontent +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-entry', function() { + var htmlFragment = "\n
    \n

    microformats.org at 7

    \n
    \n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n
    \n

    Updated \n by\n Tantek\n

    \n
    "; + var expected = {"items":[{"type":["h-entry"],"properties":{"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"name":["microformats.org at 7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"rels":{},"rel-urls":{}}; + + it('summarycontent', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-u-property.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-u-property.js index 61de69001b..510f0aa90d 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-u-property.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-u-property.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-entry/u-property -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-entry', function() { - var htmlFragment = "\n
    \n

    microformats.org at 7

    \n\n \n

    \n \n Article permalink\n

    \n

    \n http://microformats.org/ - \n 2012/06/25/microformats-org-at-7 \n

    \n\n

    Article permalink

    \n\n \"company\n \n \"microformats.org\"\n \n\n \"company\n\n \n\n value-class-pattern \n \n

    http://microformats.org/discuss

    \n
    "; - var expected = {"items":[{"type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/","http://microformats.org/2012/06/25/microformats-org-at-7","http://microformats.org/2012/06/25/microformats-org-at-7","http://microformats.org/","http://microformats.org/wiki/microformats2-parsing","http://microformats.org/wiki/value-class-pattern","http://microformats.org/wiki/","http://microformats.org/discuss"],"photo":["http://example.com/images/logo.gif"]}}],"rels":{},"rel-urls":{}}; - - it('u-property', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-entry/u-property +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-entry', function() { + var htmlFragment = "\n
    \n

    microformats.org at 7

    \n\n \n

    \n \n Article permalink\n

    \n

    \n http://microformats.org/ - \n 2012/06/25/microformats-org-at-7 \n

    \n\n

    Article permalink

    \n\n \"company\n \n \"microformats.org\"\n \n\n \"company\n\n \n\n value-class-pattern \n \n

    http://microformats.org/discuss

    \n
    "; + var expected = {"items":[{"type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/","http://microformats.org/2012/06/25/microformats-org-at-7","http://microformats.org/2012/06/25/microformats-org-at-7","http://microformats.org/","http://microformats.org/wiki/microformats2-parsing","http://microformats.org/wiki/value-class-pattern","http://microformats.org/wiki/","http://microformats.org/discuss"],"photo":["http://example.com/images/logo.gif"]}}],"rels":{},"rel-urls":{}}; + + it('u-property', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-urlincontent.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-urlincontent.js index d4c29053c2..295ac9925a 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-urlincontent.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-entry-urlincontent.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-entry/urlincontent -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-entry', function() { - var htmlFragment = ""; - var expected = {"items":[{"type":["h-entry"],"properties":{"name":["Expanding URLs within HTML content"],"content":[{"value":"Should not change: http://www.w3.org/\n Should not change: http://example.com/\n File relative: test.html = http://example.com/test.html\n Directory relative: /test/test.html = http://example.com/test/test.html\n Relative to root: /test.html = http://example.com/test.html","html":"\n \n \n "}]}}],"rels":{},"rel-urls":{}}; - - it('urlincontent', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-entry/urlincontent +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-entry', function() { + var htmlFragment = ""; + var expected = {"items":[{"type":["h-entry"],"properties":{"name":["Expanding URLs within HTML content"],"content":[{"value":"Should not change: http://www.w3.org/\n Should not change: http://example.com/\n File relative: test.html = http://example.com/test.html\n Directory relative: /test/test.html = http://example.com/test/test.html\n Relative to root: /test.html = http://example.com/test.html","html":"\n \n \n "}]}}],"rels":{},"rel-urls":{}}; + + it('urlincontent', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-ampm.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-ampm.js index 4900a99798..814c3c42ed 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-ampm.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-ampm.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/ampm -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "\n The 4th Microformat party will be on \n
      \n
    • \n , from\n 07:00:00pm \n
    • \n
    • \n , from\n 07:00:00am \n
    • \n
    • \n , from\n 07:00pm \n
    • \n
    • \n , from\n 07pm \n
    • \n
    • \n , from\n 7pm \n
    • \n
    • \n , from\n 7:00pm \n
    • \n
    • \n , from\n 07:00p.m. \n
    • \n
    • \n , from\n 07:00PM \n
    • \n
    • \n , from\n 7:00am \n
    • \n
    \n
    "; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00:00","2009-06-26 07:00:00","2009-06-26 19:00","2009-06-26 19","2009-06-26 19","2009-06-26 19:00","2009-06-26 19:00","2009-06-26 19:00","2009-06-26 07:00"]}}],"rels":{},"rel-urls":{}}; - - it('ampm', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/ampm +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "\n The 4th Microformat party will be on \n
      \n
    • \n , from\n 07:00:00pm \n
    • \n
    • \n , from\n 07:00:00am \n
    • \n
    • \n , from\n 07:00pm \n
    • \n
    • \n , from\n 07pm \n
    • \n
    • \n , from\n 7pm \n
    • \n
    • \n , from\n 7:00pm \n
    • \n
    • \n , from\n 07:00p.m. \n
    • \n
    • \n , from\n 07:00PM \n
    • \n
    • \n , from\n 7:00am \n
    • \n
    \n
    "; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00:00","2009-06-26 07:00:00","2009-06-26 19:00","2009-06-26 19","2009-06-26 19","2009-06-26 19:00","2009-06-26 19:00","2009-06-26 19:00","2009-06-26 07:00"]}}],"rels":{},"rel-urls":{}}; + + it('ampm', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-attendees.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-attendees.js index 00f9e6fc89..2315dbe912 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-attendees.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-attendees.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/attendees -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "\n
    \n CPJ Online Press Freedom Summit\n () in\n San Francisco.\n Attendees:\n
      \n
    • Brian Warner
    • \n
    • Kyle Machulis
    • \n
    • Tantek Çelik
    • \n
    • Sid Sutter
    • \n
    \n
    \n"; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["CPJ Online Press Freedom Summit"],"start":["2012-10-10"],"location":["San Francisco"],"attendee":[{"value":"Brian Warner","type":["h-card"],"properties":{"name":["Brian Warner"]}},{"value":"Kyle Machulis","type":["h-card"],"properties":{"name":["Kyle Machulis"]}},{"value":"Tantek Çelik","type":["h-card"],"properties":{"name":["Tantek Çelik"]}},{"value":"Sid Sutter","type":["h-card"],"properties":{"name":["Sid Sutter"]}}]}}],"rels":{},"rel-urls":{}}; - - it('attendees', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/attendees +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "\n
    \n CPJ Online Press Freedom Summit\n () in\n San Francisco.\n Attendees:\n
      \n
    • Brian Warner
    • \n
    • Kyle Machulis
    • \n
    • Tantek Çelik
    • \n
    • Sid Sutter
    • \n
    \n
    \n"; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["CPJ Online Press Freedom Summit"],"start":["2012-10-10"],"location":["San Francisco"],"attendee":[{"value":"Brian Warner","type":["h-card"],"properties":{"name":["Brian Warner"]}},{"value":"Kyle Machulis","type":["h-card"],"properties":{"name":["Kyle Machulis"]}},{"value":"Tantek Çelik","type":["h-card"],"properties":{"name":["Tantek Çelik"]}},{"value":"Sid Sutter","type":["h-card"],"properties":{"name":["Sid Sutter"]}}]}}],"rels":{},"rel-urls":{}}; + + it('attendees', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-combining.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-combining.js index 10265a0dd2..e91b381bab 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-combining.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-combining.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/combining -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "
    \n \n IndieWebCamp 2012\n \n from \n to at \n \n Geoloqi, \n 920 SW 3rd Ave. Suite 400, \n Portland, \n OR\n \n
    "; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["IndieWebCamp 2012"],"url":["http://indiewebcamp.com/2012"],"start":["2012-06-30"],"end":["2012-07-01"],"location":[{"value":"Geoloqi","type":["h-card"],"properties":{"name":["Geoloqi"],"org":["Geoloqi"],"url":["http://geoloqi.com/"],"street-address":["920 SW 3rd Ave. Suite 400"],"locality":["Portland"],"region":["Oregon"]}}]}}],"rels":{},"rel-urls":{}}; - - it('combining', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/combining +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "
    \n \n IndieWebCamp 2012\n \n from \n to at \n \n Geoloqi, \n 920 SW 3rd Ave. Suite 400, \n Portland, \n OR\n \n
    "; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["IndieWebCamp 2012"],"url":["http://indiewebcamp.com/2012"],"start":["2012-06-30"],"end":["2012-07-01"],"location":[{"value":"Geoloqi","type":["h-card"],"properties":{"name":["Geoloqi"],"org":["Geoloqi"],"url":["http://geoloqi.com/"],"street-address":["920 SW 3rd Ave. Suite 400"],"locality":["Portland"],"region":["Oregon"]}}]}}],"rels":{},"rel-urls":{}}; + + it('combining', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-concatenate.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-concatenate.js index e1721e55d7..8972106745 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-concatenate.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-concatenate.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/concatenate -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "\n The 4th Microformat party will be on \n \n , from\n to \n .\n"; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00"],"end":["2009-06-26 22:00"]}}],"rels":{},"rel-urls":{}}; - - it('concatenate', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/concatenate +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "\n The 4th Microformat party will be on \n \n , from\n to \n .\n"; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00"],"end":["2009-06-26 22:00"]}}],"rels":{},"rel-urls":{}}; + + it('concatenate', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-dates.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-dates.js index 80e274cd04..c26b5bccc2 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-dates.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-dates.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/dates -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "
    \n\t

    The 4th Microformat party will be on:

    \n\t
      \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t
    \n
    "; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00-08:00","2009-06-26 19:00-08","2009-06-26 19:00-08:00","2009-06-26 19:00+08:00","2009-06-26 19:00+08:00","2009-06-26 19:00Z","2009-06-26 19:00-08:00","2009-06-26 19:00:00-08:00"]}}],"rels":{},"rel-urls":{}}; - - it('dates', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/dates +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "
    \n\t

    The 4th Microformat party will be on:

    \n\t
      \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t\t
    • \n\t
    \n
    "; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00-08:00","2009-06-26 19:00-08","2009-06-26 19:00-08:00","2009-06-26 19:00+08:00","2009-06-26 19:00+08:00","2009-06-26 19:00Z","2009-06-26 19:00-08:00","2009-06-26 19:00:00-08:00"]}}],"rels":{},"rel-urls":{}}; + + it('dates', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-dt-property.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-dt-property.js index 535edfa81d..eb97beccb5 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-dt-property.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-dt-property.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/dt-property -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "\n The party will be on \n \n

    \n \n March 14th 2013\n

    \n

    \n , from\n 07:00:00am \n

    \n \n

    \n \n \n Just added, \n Removed\n

    \n June 29 \n \n

    2013-07-02

    \n \n
    "; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["The party"],"start":["2013-03-14","2013-06-25 07:00:00","2013-06-26","2013-06-27","2013-06-28","2013-06-29","2013-07-01","2013-07-02"]}}],"rels":{},"rel-urls":{}}; - - it('dt-property', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/dt-property +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "\n The party will be on \n \n

    \n \n March 14th 2013\n

    \n

    \n , from\n 07:00:00am \n

    \n \n

    \n \n \n Just added, \n Removed\n

    \n June 29 \n \n

    2013-07-02

    \n \n
    "; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["The party"],"start":["2013-03-14","2013-06-25 07:00:00","2013-06-26","2013-06-27","2013-06-28","2013-06-29","2013-07-01","2013-07-02"]}}],"rels":{},"rel-urls":{}}; + + it('dt-property', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-justahyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-justahyperlink.js index 63ad22d348..26c835863e 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-justahyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-justahyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/justahyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "IndieWebCamp 2012"; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["IndieWebCamp 2012"],"url":["http://indiewebcamp.com/2012"]}}],"rels":{},"rel-urls":{}}; - - it('justahyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/justahyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "IndieWebCamp 2012"; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["IndieWebCamp 2012"],"url":["http://indiewebcamp.com/2012"]}}],"rels":{},"rel-urls":{}}; + + it('justahyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-justaname.js index 729210ecb2..be3a5335db 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "

    IndieWebCamp 2012

    "; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["IndieWebCamp 2012"]}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "

    IndieWebCamp 2012

    "; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["IndieWebCamp 2012"]}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-time.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-time.js index 6aaad1aa7b..243b518bfb 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-time.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-event-time.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-event/time -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-event', function() { - var htmlFragment = "\n The 4th Microformat party will be on \n
      \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n \n
    • \n
    • \n \n
    • \n
    \n
    "; - var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00:00-08:00","2009-06-26 19:00:00-08:00","2009-06-26 19:00:00+08:00","2009-06-26 19:00:00Z","2009-06-26 19:00:00","2009-06-26 19:00-08:00","2009-06-26 19:00+08:00","2009-06-26 19:00Z","2009-06-26 19:00"],"end":["2013-034","2013-06-27 15:34"]}}],"rels":{},"rel-urls":{}}; - - it('time', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-event/time +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-event', function() { + var htmlFragment = "\n The 4th Microformat party will be on \n
      \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n , from\n \n
    • \n
    • \n \n
    • \n
    • \n \n
    • \n
    \n
    "; + var expected = {"items":[{"type":["h-event"],"properties":{"name":["The 4th Microformat party"],"start":["2009-06-26 19:00:00-08:00","2009-06-26 19:00:00-08:00","2009-06-26 19:00:00+08:00","2009-06-26 19:00:00Z","2009-06-26 19:00:00","2009-06-26 19:00-08:00","2009-06-26 19:00+08:00","2009-06-26 19:00Z","2009-06-26 19:00"],"end":["2013-034","2013-06-27 15:34"]}}],"rels":{},"rel-urls":{}}; + + it('time', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-feed-implied-title.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-feed-implied-title.js index bd99cc41f5..30bbf52df0 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-feed-implied-title.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-feed-implied-title.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-feed/implied-title -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-feed', function() { - var htmlFragment = "\n\n\t\n\t\tmicroformats blog\n\t\n\t\n\t
    \n\t\t\n\t\t
    \n\t\t

    microformats.org at 7

    \n\t\t
    \n\t\t

    Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities.

    \n\t\t\n\t\t

    The microformats tagline “humans first, machines second” \n\t\t forms the basis of many of our \n\t\t principles, and \n\t\t in that regard, we’d like to recognize a few people and \n\t\t thank them for their years of volunteer service

    \n\t\t
    \n\t\t

    Updated \n\t\t \n\t\t

    \n\t\t
    \n\t\t\n\t
    \n\t\n"; - var expected = {"items":[{"type":["h-feed"],"properties":{"name":["microformats blog"]},"children":[{"value":"microformats.org at 7\n\t\t \n\t\t Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities.\n\t\t\n\t\t The microformats tagline “humans first, machines second” \n\t\t forms the basis of many of our \n\t\t principles, and \n\t\t in that regard, we’d like to recognize a few people and \n\t\t thank them for their years of volunteer service \n\t\t \n\t\t Updated \n\t\t June 25th, 2012","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities.\n\t\t\n\t\t The microformats tagline “humans first, machines second” \n\t\t forms the basis of many of our \n\t\t principles, and \n\t\t in that regard, we’d like to recognize a few people and \n\t\t thank them for their years of volunteer service","html":"\n\t\t

    Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities.

    \n\t\t\n\t\t

    The microformats tagline “humans first, machines second” \n\t\t forms the basis of many of our \n\t\t principles, and \n\t\t in that regard, we’d like to recognize a few people and \n\t\t thank them for their years of volunteer service

    \n\t\t "}],"summary":["Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities."],"updated":["2012-06-25 17:08:26"]}}]}],"rels":{},"rel-urls":{}}; - - it('implied-title', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-feed/implied-title +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-feed', function() { + var htmlFragment = "\n\n\t\n\t\tmicroformats blog\n\t\n\t\n\t
    \n\t\t\n\t\t
    \n\t\t

    microformats.org at 7

    \n\t\t
    \n\t\t

    Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities.

    \n\t\t\n\t\t

    The microformats tagline “humans first, machines second” \n\t\t forms the basis of many of our \n\t\t principles, and \n\t\t in that regard, we’d like to recognize a few people and \n\t\t thank them for their years of volunteer service

    \n\t\t
    \n\t\t

    Updated \n\t\t \n\t\t

    \n\t\t
    \n\t\t\n\t
    \n\t\n"; + var expected = {"items":[{"type":["h-feed"],"properties":{"name":["microformats blog"]},"children":[{"value":"microformats.org at 7\n\t\t \n\t\t Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities.\n\t\t\n\t\t The microformats tagline “humans first, machines second” \n\t\t forms the basis of many of our \n\t\t principles, and \n\t\t in that regard, we’d like to recognize a few people and \n\t\t thank them for their years of volunteer service \n\t\t \n\t\t Updated \n\t\t June 25th, 2012","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities.\n\t\t\n\t\t The microformats tagline “humans first, machines second” \n\t\t forms the basis of many of our \n\t\t principles, and \n\t\t in that regard, we’d like to recognize a few people and \n\t\t thank them for their years of volunteer service","html":"\n\t\t

    Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities.

    \n\t\t\n\t\t

    The microformats tagline “humans first, machines second” \n\t\t forms the basis of many of our \n\t\t principles, and \n\t\t in that regard, we’d like to recognize a few people and \n\t\t thank them for their years of volunteer service

    \n\t\t "}],"summary":["Last week the microformats.org community \n\t\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t\t San Francisco and recognized accomplishments, challenges, and \n\t\t opportunities."],"updated":["2012-06-25 17:08:26"]}}]}],"rels":{},"rel-urls":{}}; + + it('implied-title', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-feed-simple.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-feed-simple.js index dd48e6436f..c72b241404 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-feed-simple.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-feed-simple.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-feed/simple -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-feed', function() { - var htmlFragment = "
    \n\t

    Microformats blog

    \n\tTantek\n\tpermlink\n\t\n\t\n\t
    \n\t

    microformats.org at 7

    \n\t
    \n\t

    Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.

    \n\t\n\t

    The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service

    \n\t
    \n\t

    Updated \n\t \n\t

    \n\t
    \n\t\n
    "; - var expected = {"items":[{"type":["h-feed"],"properties":{"name":["Microformats blog"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}],"url":["http://microformats.org/blog"],"photo":["http://example.com/photo.jpeg"]},"children":[{"value":"microformats.org at 7\n\t \n\t Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.\n\t\n\t The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service \n\t \n\t Updated \n\t June 25th, 2012","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.\n\t\n\t The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service","html":"\n\t

    Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.

    \n\t\n\t

    The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service

    \n\t "}],"summary":["Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities."],"updated":["2012-06-25 17:08:26"]}}]}],"rels":{},"rel-urls":{}}; - - it('simple', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-feed/simple +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-feed', function() { + var htmlFragment = "
    \n\t

    Microformats blog

    \n\tTantek\n\tpermlink\n\t\n\t\n\t
    \n\t

    microformats.org at 7

    \n\t
    \n\t

    Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.

    \n\t\n\t

    The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service

    \n\t
    \n\t

    Updated \n\t \n\t

    \n\t
    \n\t\n
    "; + var expected = {"items":[{"type":["h-feed"],"properties":{"name":["Microformats blog"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}],"url":["http://microformats.org/blog"],"photo":["http://example.com/photo.jpeg"]},"children":[{"value":"microformats.org at 7\n\t \n\t Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.\n\t\n\t The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service \n\t \n\t Updated \n\t June 25th, 2012","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.\n\t\n\t The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service","html":"\n\t

    Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities.

    \n\t\n\t

    The microformats tagline “humans first, machines second” \n\t forms the basis of many of our \n\t principles, and \n\t in that regard, we’d like to recognize a few people and \n\t thank them for their years of volunteer service

    \n\t "}],"summary":["Last week the microformats.org community \n\t celebrated its 7th birthday at a gathering hosted by Mozilla in \n\t San Francisco and recognized accomplishments, challenges, and \n\t opportunities."],"updated":["2012-06-25 17:08:26"]}}]}],"rels":{},"rel-urls":{}}; + + it('simple', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-abbrpattern.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-abbrpattern.js index 04ef6e1770..d26e9ed0d9 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-abbrpattern.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-abbrpattern.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-geo/abbrpattern -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-geo', function() { - var htmlFragment = "\n

    \n N 37° 24.491, \n W 122° 08.313\n

    "; - var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["37.408183"],"longitude":["-122.13855"],"name":["N 37° 24.491, \n W 122° 08.313"]}}],"rels":{},"rel-urls":{}}; - - it('abbrpattern', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-geo/abbrpattern +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-geo', function() { + var htmlFragment = "\n

    \n N 37° 24.491, \n W 122° 08.313\n

    "; + var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["37.408183"],"longitude":["-122.13855"],"name":["N 37° 24.491, \n W 122° 08.313"]}}],"rels":{},"rel-urls":{}}; + + it('abbrpattern', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-altitude.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-altitude.js index 4d35840cb1..45da683ff4 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-altitude.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-altitude.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-geo/altitude -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-geo', function() { - var htmlFragment = "

    My favourite hill in the lakes is \n \n Pen-y-ghent \n (Geo: 54.155278,\n -2.249722). It\n raises to 694m.\n \n

    "; - var expected = {"items":[{"type":["h-geo"],"properties":{"name":["Pen-y-ghent"],"latitude":["54.155278"],"longitude":["-2.249722"],"altitude":["694"]}}],"rels":{},"rel-urls":{}}; - - it('altitude', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-geo/altitude +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-geo', function() { + var htmlFragment = "

    My favourite hill in the lakes is \n \n Pen-y-ghent \n (Geo: 54.155278,\n -2.249722). It\n raises to 694m.\n \n

    "; + var expected = {"items":[{"type":["h-geo"],"properties":{"name":["Pen-y-ghent"],"latitude":["54.155278"],"longitude":["-2.249722"],"altitude":["694"]}}],"rels":{},"rel-urls":{}}; + + it('altitude', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-hidden.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-hidden.js index 4a03bf907e..968ed12853 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-hidden.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-hidden.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-geo/hidden -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-geo', function() { - var htmlFragment = "

    \n The Bricklayer's Arms\n \n \n \n \n \n \n \n

    "; - var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"],"name":["The Bricklayer's Arms"]}}],"rels":{},"rel-urls":{}}; - - it('hidden', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-geo/hidden +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-geo', function() { + var htmlFragment = "

    \n The Bricklayer's Arms\n \n \n \n \n \n \n \n

    "; + var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"],"name":["The Bricklayer's Arms"]}}],"rels":{},"rel-urls":{}}; + + it('hidden', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-justaname.js index df294dcd3f..23c142462c 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-geo/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-geo', function() { - var htmlFragment = "

    On my way to The Bricklayer's Arms\n (Geo: 51.513458;-0.14812)\n

    "; - var expected = {"items":[{"type":["h-geo"],"properties":{"name":["51.513458;-0.14812"]}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-geo/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-geo', function() { + var htmlFragment = "

    On my way to The Bricklayer's Arms\n (Geo: 51.513458;-0.14812)\n

    "; + var expected = {"items":[{"type":["h-geo"],"properties":{"name":["51.513458;-0.14812"]}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-simpleproperties.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-simpleproperties.js index 0b45f48a96..e9700a3e29 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-simpleproperties.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-simpleproperties.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-geo/simpleproperties -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-geo', function() { - var htmlFragment = "

    We are meeting at \n The Bricklayer's Arms\n (Geo: 51.513458:\n -0.14812)\n

    "; - var expected = {"items":[{"type":["h-geo"],"properties":{"name":["The Bricklayer's Arms"],"latitude":["51.513458"],"longitude":["-0.14812"]}}],"rels":{},"rel-urls":{}}; - - it('simpleproperties', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-geo/simpleproperties +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-geo', function() { + var htmlFragment = "

    We are meeting at \n The Bricklayer's Arms\n (Geo: 51.513458:\n -0.14812)\n

    "; + var expected = {"items":[{"type":["h-geo"],"properties":{"name":["The Bricklayer's Arms"],"latitude":["51.513458"],"longitude":["-0.14812"]}}],"rels":{},"rel-urls":{}}; + + it('simpleproperties', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-valuetitleclass.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-valuetitleclass.js index 75c3baa821..813d215927 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-valuetitleclass.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-geo-valuetitleclass.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-geo/valuetitleclass -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-geo', function() { - var htmlFragment = "\n

    \n \n \n N 51° 51.345, \n \n \n W -0° 14.812\n \n \n

    "; - var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"],"name":["N 51° 51.345, \n \n \n W -0° 14.812"]}}],"rels":{},"rel-urls":{}}; - - it('valuetitleclass', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-geo/valuetitleclass +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-geo', function() { + var htmlFragment = "\n

    \n \n \n N 51° 51.345, \n \n \n W -0° 14.812\n \n \n

    "; + var expected = {"items":[{"type":["h-geo"],"properties":{"latitude":["51.513458"],"longitude":["-0.14812"],"name":["N 51° 51.345, \n \n \n W -0° 14.812"]}}],"rels":{},"rel-urls":{}}; + + it('valuetitleclass', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-news-all.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-news-all.js index b49695ae9d..a7deb3f2f0 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-news-all.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-news-all.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-news/all -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-news', function() { - var htmlFragment = "
    \n
    \n

    microformats.org at 7

    \n
    \n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n
    \n

    Updated \n by\n Tantek\n

    \n
    \n\n

    \n \n (Geo: 37.774921;-122.445202) \n \n microformats.org\n \n

    \n

    \n Publishing policy\n

    \n
    "; - var expected = {"items":[{"type":["h-news"],"properties":{"entry":[{"value":"microformats.org at 7","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"dateline":[{"value":"San Francisco, \n CA","type":["h-adr"],"properties":{"locality":["San Francisco"],"region":["CA"],"name":["San Francisco, \n CA"]}}],"geo":["37.774921;-122.445202"],"source-org":[{"value":"microformats.org","type":["h-card"],"properties":{"name":["microformats.org"],"url":["http://microformats.org/"]}}],"principles":["http://microformats.org/wiki/Category:public_domain_license"],"name":["microformats.org at 7\n \n Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service \n \n Updated \n June 25th, 2012 by\n Tantek\n \n \n\n \n \n San Francisco, \n CA \n \n (Geo: 37.774921;-122.445202) \n \n microformats.org\n \n \n \n Publishing policy"]}}],"rels":{},"rel-urls":{}}; - - it('all', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-news/all +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-news', function() { + var htmlFragment = "
    \n
    \n

    microformats.org at 7

    \n
    \n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n
    \n

    Updated \n by\n Tantek\n

    \n
    \n\n

    \n \n (Geo: 37.774921;-122.445202) \n \n microformats.org\n \n

    \n

    \n Publishing policy\n

    \n
    "; + var expected = {"items":[{"type":["h-news"],"properties":{"entry":[{"value":"microformats.org at 7","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"dateline":[{"value":"San Francisco, \n CA","type":["h-adr"],"properties":{"locality":["San Francisco"],"region":["CA"],"name":["San Francisco, \n CA"]}}],"geo":["37.774921;-122.445202"],"source-org":[{"value":"microformats.org","type":["h-card"],"properties":{"name":["microformats.org"],"url":["http://microformats.org/"]}}],"principles":["http://microformats.org/wiki/Category:public_domain_license"],"name":["microformats.org at 7\n \n Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service \n \n Updated \n June 25th, 2012 by\n Tantek\n \n \n\n \n \n San Francisco, \n CA \n \n (Geo: 37.774921;-122.445202) \n \n microformats.org\n \n \n \n Publishing policy"]}}],"rels":{},"rel-urls":{}}; + + it('all', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-news-minimum.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-news-minimum.js index 935e96e08a..4494cb8ab3 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-news-minimum.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-news-minimum.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-news/minimum -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-news', function() { - var htmlFragment = "
    \n
    \n

    microformats.org at 7

    \n
    \n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n
    \n

    Updated \n by\n Tantek\n

    \n
    \n

    \n microformats.org \n

    \n
    "; - var expected = {"items":[{"type":["h-news"],"properties":{"entry":[{"value":"microformats.org at 7","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"source-org":[{"value":"microformats.org","type":["h-card"],"properties":{"name":["microformats.org"],"url":["http://microformats.org/"]}}],"name":["microformats.org at 7\n \n Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service \n \n Updated \n June 25th, 2012 by\n Tantek\n \n \n \n microformats.org"]}}],"rels":{},"rel-urls":{}}; - - it('minimum', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-news/minimum +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-news', function() { + var htmlFragment = "
    \n
    \n

    microformats.org at 7

    \n
    \n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n
    \n

    Updated \n by\n Tantek\n

    \n
    \n

    \n microformats.org \n

    \n
    "; + var expected = {"items":[{"type":["h-news"],"properties":{"entry":[{"value":"microformats.org at 7","type":["h-entry"],"properties":{"name":["microformats.org at 7"],"url":["http://microformats.org/2012/06/25/microformats-org-at-7"],"content":[{"value":"Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service","html":"\n

    Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.

    \n\n

    The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service

    \n "}],"summary":["Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities."],"updated":["2012-06-25 17:08:26"],"author":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"],"url":["http://tantek.com/"]}}]}}],"source-org":[{"value":"microformats.org","type":["h-card"],"properties":{"name":["microformats.org"],"url":["http://microformats.org/"]}}],"name":["microformats.org at 7\n \n Last week the microformats.org community \n celebrated its 7th birthday at a gathering hosted by Mozilla in \n San Francisco and recognized accomplishments, challenges, and \n opportunities.\n\n The microformats tagline “humans first, machines second” \n forms the basis of many of our \n principles, and \n in that regard, we’d like to recognize a few people and \n thank them for their years of volunteer service \n \n Updated \n June 25th, 2012 by\n Tantek\n \n \n \n microformats.org"]}}],"rels":{},"rel-urls":{}}; + + it('minimum', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-hyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-hyperlink.js index 6283dc69ba..b7150aae49 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-hyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-hyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-org/hyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-org', function() { - var htmlFragment = "Mozilla Foundation"; - var expected = {"items":[{"type":["h-org"],"properties":{"name":["Mozilla Foundation"],"url":["http://mozilla.org/"]}}],"rels":{},"rel-urls":{}}; - - it('hyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-org/hyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-org', function() { + var htmlFragment = "Mozilla Foundation"; + var expected = {"items":[{"type":["h-org"],"properties":{"name":["Mozilla Foundation"],"url":["http://mozilla.org/"]}}],"rels":{},"rel-urls":{}}; + + it('hyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-simple.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-simple.js index c42b4d2b13..4f5a75e881 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-simple.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-simple.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-org/simple -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-org', function() { - var htmlFragment = "Mozilla Foundation"; - var expected = {"items":[{"type":["h-org"],"properties":{"name":["Mozilla Foundation"]}}],"rels":{},"rel-urls":{}}; - - it('simple', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-org/simple +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-org', function() { + var htmlFragment = "Mozilla Foundation"; + var expected = {"items":[{"type":["h-org"],"properties":{"name":["Mozilla Foundation"]}}],"rels":{},"rel-urls":{}}; + + it('simple', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-simpleproperties.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-simpleproperties.js index 238b40f959..5c7e939e6e 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-simpleproperties.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-org-simpleproperties.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-org/simpleproperties -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-org', function() { - var htmlFragment = "

    \n W3C - \n CSS Working Group\n

    "; - var expected = {"items":[{"type":["h-org"],"properties":{"organization-name":["W3C"],"organization-unit":["CSS Working Group"],"name":["W3C - \n CSS Working Group"]}}],"rels":{},"rel-urls":{}}; - - it('simpleproperties', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-org/simpleproperties +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-org', function() { + var htmlFragment = "

    \n W3C - \n CSS Working Group\n

    "; + var expected = {"items":[{"type":["h-org"],"properties":{"organization-name":["W3C"],"organization-unit":["CSS Working Group"],"name":["W3C - \n CSS Working Group"]}}],"rels":{},"rel-urls":{}}; + + it('simpleproperties', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-aggregate.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-aggregate.js index a125cb1eb0..b07d3f5477 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-aggregate.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-aggregate.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-product/aggregate -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-product', function() { - var htmlFragment = "\n
    \n

    Raspberry Pi

    \n \n

    The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.

    \n More info about the Raspberry Pi\n

    £29.95

    \n

    \n \n 9.2 out of \n 10 \n based on 178 reviews\n \n

    \n

    Categories: Computer, Education

    \n

    From: \n The Raspberry Pi Foundation - \n Cambridge \n UK\n

    \n
    "; - var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/RaspberryPi.jpg/320px-RaspberryPi.jpg"],"description":[{"value":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.","html":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."}],"url":["http://www.raspberrypi.org/"],"price":["£29.95"],"review":[{"value":"9.2 out of \n 10 \n based on 178 reviews","type":["h-review-aggregate"],"properties":{"rating":[{"value":"9.2 out of \n 10 \n based on 178 reviews","type":["h-rating"],"properties":{"average":["9.2"],"best":["10"],"count":["178"],"name":["9.2 out of \n 10 \n based on 178 reviews"]}}],"name":["9.2 out of \n 10 \n based on 178 reviews"]}}],"category":["Computer","Education"],"brand":[{"value":"The Raspberry Pi Foundation","type":["h-card"],"properties":{"name":["The Raspberry Pi Foundation"],"org":["The Raspberry Pi Foundation"],"locality":["Cambridge"],"country-name":["UK"]}}]}}],"rels":{},"rel-urls":{}}; - - it('aggregate', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-product/aggregate +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-product', function() { + var htmlFragment = "\n
    \n

    Raspberry Pi

    \n \n

    The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.

    \n More info about the Raspberry Pi\n

    £29.95

    \n

    \n \n 9.2 out of \n 10 \n based on 178 reviews\n \n

    \n

    Categories: Computer, Education

    \n

    From: \n The Raspberry Pi Foundation - \n Cambridge \n UK\n

    \n
    "; + var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/RaspberryPi.jpg/320px-RaspberryPi.jpg"],"description":[{"value":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.","html":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."}],"url":["http://www.raspberrypi.org/"],"price":["£29.95"],"review":[{"value":"9.2 out of \n 10 \n based on 178 reviews","type":["h-review-aggregate"],"properties":{"rating":[{"value":"9.2 out of \n 10 \n based on 178 reviews","type":["h-rating"],"properties":{"average":["9.2"],"best":["10"],"count":["178"],"name":["9.2 out of \n 10 \n based on 178 reviews"]}}],"name":["9.2 out of \n 10 \n based on 178 reviews"]}}],"category":["Computer","Education"],"brand":[{"value":"The Raspberry Pi Foundation","type":["h-card"],"properties":{"name":["The Raspberry Pi Foundation"],"org":["The Raspberry Pi Foundation"],"locality":["Cambridge"],"country-name":["UK"]}}]}}],"rels":{},"rel-urls":{}}; + + it('aggregate', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-justahyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-justahyperlink.js index cc68b8c138..cf2638e314 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-justahyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-justahyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-product/justahyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-product', function() { - var htmlFragment = "Raspberry Pi"; - var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"url":["http://www.raspberrypi.org/"]}}],"rels":{},"rel-urls":{}}; - - it('justahyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-product/justahyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-product', function() { + var htmlFragment = "Raspberry Pi"; + var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"url":["http://www.raspberrypi.org/"]}}],"rels":{},"rel-urls":{}}; + + it('justahyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-justaname.js index bc55a5bb17..f946a10e06 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-product/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-product', function() { - var htmlFragment = "

    Raspberry Pi

    "; - var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"]}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-product/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-product', function() { + var htmlFragment = "

    Raspberry Pi

    "; + var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"]}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-simpleproperties.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-simpleproperties.js index 163758419f..1c5467d519 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-simpleproperties.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-product-simpleproperties.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-product/simpleproperties -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-product', function() { - var htmlFragment = "\n
    \n

    Raspberry Pi

    \n \n

    The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.

    \n More info about the Raspberry Pi\n

    £29.95

    \n

    4.5 out of 5

    \n

    Categories: Computer, Education

    \n
    "; - var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/RaspberryPi.jpg/320px-RaspberryPi.jpg"],"description":[{"value":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.","html":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."}],"url":["http://www.raspberrypi.org/"],"price":["£29.95"],"category":["Computer","Education"],"review":[{"value":"4.5 out of 5","type":["h-review"],"properties":{"rating":["4.5"],"name":["4.5 out of 5"]}}]}}],"rels":{},"rel-urls":{}}; - - it('simpleproperties', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-product/simpleproperties +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-product', function() { + var htmlFragment = "\n
    \n

    Raspberry Pi

    \n \n

    The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.

    \n More info about the Raspberry Pi\n

    £29.95

    \n

    4.5 out of 5

    \n

    Categories: Computer, Education

    \n
    "; + var expected = {"items":[{"type":["h-product"],"properties":{"name":["Raspberry Pi"],"photo":["http://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/RaspberryPi.jpg/320px-RaspberryPi.jpg"],"description":[{"value":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.","html":"The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."}],"url":["http://www.raspberrypi.org/"],"price":["£29.95"],"category":["Computer","Education"],"review":[{"value":"4.5 out of 5","type":["h-review"],"properties":{"rating":["4.5"],"name":["4.5 out of 5"]}}]}}],"rels":{},"rel-urls":{}}; + + it('simpleproperties', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-recipe-all.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-recipe-all.js index c951d75126..fa0e4cb376 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-recipe-all.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-recipe-all.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-recipe/all -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-recipe', function() { - var htmlFragment = "
    \n

    Yorkshire Puddings

    \n

    Makes 6 good sized Yorkshire puddings, the way my mum taught me

    \n\n\n

    \n\n \n \n 4.5 stars out 5 based on \n 35 reviews\n \n \n\n
    \n

    Ingredients

    \n
      \n
    • 1 egg
    • \n
    • 75g plain flour
    • \n
    • 70ml milk
    • \n
    • 60ml water
    • \n
    • Pinch of salt
    • \n
    \n
    \n\n

    Time

    \n
      \n
    • Preparation 10 mins
    • \n
    • Cook 25 mins
    • \n
    \n\n\n

    Instructions

    \n
    \n
      \n
    1. Pre-heat oven to 230C or gas mark 8. Pour the vegetable oil evenly into 2 x 4-hole \n Yorkshire pudding tins and place in the oven to heat through.
    2. \n \n
    3. To make the batter, add all the flour into a bowl and beat in the eggs until smooth. \n Gradually add the milk and water while beating the mixture. It should be smooth and \n without lumps. Finally add a pinch of salt.
    4. \n \n
    5. Make sure the oil is piping hot before pouring the batter evenly into the tins. \n Place in the oven for 20-25 minutes until pudding have risen and look golden brown
    6. \n
    \n
    \n\n

    Nutrition

    \n
      \n
    • Calories: 125
    • \n
    • Fat: 3.2g
    • \n
    • Cholesterol: 77mg
    • \n
    \n

    (Amount per pudding)

    \n\n

    \n Published on by \n \n Glenn Jones\n \n

    \n Photo by dithie\n
    "; - var expected = {"items":[{"type":["h-recipe"],"properties":{"name":["Yorkshire Puddings"],"summary":["Makes 6 good sized Yorkshire puddings, the way my mum taught me"],"yield":["6 good sized Yorkshire puddings"],"photo":["http://codebits.glennjones.net/semantic/yorkshire-puddings.jpg"],"review":[{"value":"4.5 stars out 5 based on \n 35 reviews","type":["h-review-aggregate"],"properties":{"rating":["4.5 stars out 5 based on"],"average":["4.5"],"count":["35"],"name":["4.5 stars out 5 based on \n 35 reviews"]}}],"ingredient":[{"value":"1 egg","html":"1 egg"},{"value":"75g plain flour","html":"75g plain flour"},{"value":"70ml milk","html":"70ml milk"},{"value":"60ml water","html":"60ml water"},{"value":"Pinch of salt","html":"Pinch of salt"}],"instructions":[{"value":"Pre-heat oven to 230C or gas mark 8. Pour the vegetable oil evenly into 2 x 4-hole \n Yorkshire pudding tins and place in the oven to heat through. \n \n To make the batter, add all the flour into a bowl and beat in the eggs until smooth. \n Gradually add the milk and water while beating the mixture. It should be smooth and \n without lumps. Finally add a pinch of salt.\n \n Make sure the oil is piping hot before pouring the batter evenly into the tins. \n Place in the oven for 20-25 minutes until pudding have risen and look golden brown","html":"\n
      \n
    1. Pre-heat oven to 230C or gas mark 8. Pour the vegetable oil evenly into 2 x 4-hole \n Yorkshire pudding tins and place in the oven to heat through.
    2. \n \n
    3. To make the batter, add all the flour into a bowl and beat in the eggs until smooth. \n Gradually add the milk and water while beating the mixture. It should be smooth and \n without lumps. Finally add a pinch of salt.
    4. \n \n
    5. Make sure the oil is piping hot before pouring the batter evenly into the tins. \n Place in the oven for 20-25 minutes until pudding have risen and look golden brown
    6. \n
    \n "}],"nutrition":["Calories: 125","Fat: 3.2g","Cholesterol: 77mg"],"published":["2011-10-27"],"author":[{"value":"Glenn Jones","type":["h-card"],"properties":{"name":["Glenn Jones"],"url":["http://glennjones.net"]}}],"url":["http://www.flickr.com/photos/dithie/4106528495/"]}}],"rels":{},"rel-urls":{}}; - - it('all', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-recipe/all +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-recipe', function() { + var htmlFragment = "
    \n

    Yorkshire Puddings

    \n

    Makes 6 good sized Yorkshire puddings, the way my mum taught me

    \n\n\n

    \n\n \n \n 4.5 stars out 5 based on \n 35 reviews\n \n \n\n
    \n

    Ingredients

    \n
      \n
    • 1 egg
    • \n
    • 75g plain flour
    • \n
    • 70ml milk
    • \n
    • 60ml water
    • \n
    • Pinch of salt
    • \n
    \n
    \n\n

    Time

    \n
      \n
    • Preparation 10 mins
    • \n
    • Cook 25 mins
    • \n
    \n\n\n

    Instructions

    \n
    \n
      \n
    1. Pre-heat oven to 230C or gas mark 8. Pour the vegetable oil evenly into 2 x 4-hole \n Yorkshire pudding tins and place in the oven to heat through.
    2. \n \n
    3. To make the batter, add all the flour into a bowl and beat in the eggs until smooth. \n Gradually add the milk and water while beating the mixture. It should be smooth and \n without lumps. Finally add a pinch of salt.
    4. \n \n
    5. Make sure the oil is piping hot before pouring the batter evenly into the tins. \n Place in the oven for 20-25 minutes until pudding have risen and look golden brown
    6. \n
    \n
    \n\n

    Nutrition

    \n
      \n
    • Calories: 125
    • \n
    • Fat: 3.2g
    • \n
    • Cholesterol: 77mg
    • \n
    \n

    (Amount per pudding)

    \n\n

    \n Published on by \n \n Glenn Jones\n \n

    \n Photo by dithie\n
    "; + var expected = {"items":[{"type":["h-recipe"],"properties":{"name":["Yorkshire Puddings"],"summary":["Makes 6 good sized Yorkshire puddings, the way my mum taught me"],"yield":["6 good sized Yorkshire puddings"],"photo":["http://codebits.glennjones.net/semantic/yorkshire-puddings.jpg"],"review":[{"value":"4.5 stars out 5 based on \n 35 reviews","type":["h-review-aggregate"],"properties":{"rating":["4.5 stars out 5 based on"],"average":["4.5"],"count":["35"],"name":["4.5 stars out 5 based on \n 35 reviews"]}}],"ingredient":[{"value":"1 egg","html":"1 egg"},{"value":"75g plain flour","html":"75g plain flour"},{"value":"70ml milk","html":"70ml milk"},{"value":"60ml water","html":"60ml water"},{"value":"Pinch of salt","html":"Pinch of salt"}],"instructions":[{"value":"Pre-heat oven to 230C or gas mark 8. Pour the vegetable oil evenly into 2 x 4-hole \n Yorkshire pudding tins and place in the oven to heat through. \n \n To make the batter, add all the flour into a bowl and beat in the eggs until smooth. \n Gradually add the milk and water while beating the mixture. It should be smooth and \n without lumps. Finally add a pinch of salt.\n \n Make sure the oil is piping hot before pouring the batter evenly into the tins. \n Place in the oven for 20-25 minutes until pudding have risen and look golden brown","html":"\n
      \n
    1. Pre-heat oven to 230C or gas mark 8. Pour the vegetable oil evenly into 2 x 4-hole \n Yorkshire pudding tins and place in the oven to heat through.
    2. \n \n
    3. To make the batter, add all the flour into a bowl and beat in the eggs until smooth. \n Gradually add the milk and water while beating the mixture. It should be smooth and \n without lumps. Finally add a pinch of salt.
    4. \n \n
    5. Make sure the oil is piping hot before pouring the batter evenly into the tins. \n Place in the oven for 20-25 minutes until pudding have risen and look golden brown
    6. \n
    \n "}],"nutrition":["Calories: 125","Fat: 3.2g","Cholesterol: 77mg"],"published":["2011-10-27"],"author":[{"value":"Glenn Jones","type":["h-card"],"properties":{"name":["Glenn Jones"],"url":["http://glennjones.net"]}}],"url":["http://www.flickr.com/photos/dithie/4106528495/"]}}],"rels":{},"rel-urls":{}}; + + it('all', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-recipe-minimum.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-recipe-minimum.js index 21032b21b2..ac3d91dc78 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-recipe-minimum.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-recipe-minimum.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-recipe/minimum -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-recipe', function() { - var htmlFragment = "
    \n

    Toast

    \n
      \n
    • Slice of bread
    • \n
    • Butter
    • \n
    \n
    "; - var expected = {"items":[{"type":["h-recipe"],"properties":{"name":["Toast"],"ingredient":[{"value":"Slice of bread","html":"Slice of bread"},{"value":"Butter","html":"Butter"}]}}],"rels":{},"rel-urls":{}}; - - it('minimum', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-recipe/minimum +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-recipe', function() { + var htmlFragment = "
    \n

    Toast

    \n
      \n
    • Slice of bread
    • \n
    • Butter
    • \n
    \n
    "; + var expected = {"items":[{"type":["h-recipe"],"properties":{"name":["Toast"],"ingredient":[{"value":"Slice of bread","html":"Slice of bread"},{"value":"Butter","html":"Butter"}]}}],"rels":{},"rel-urls":{}}; + + it('minimum', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-affiliation.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-affiliation.js index 85adb3f637..73329d46cb 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-affiliation.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-affiliation.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-resume/affiliation -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-resume', function() { - var htmlFragment = "
    \n

    \n Tim Berners-Lee, \n invented the World Wide Web. \n

    \n Belongs to following groups:\n

    \n \n \"W3C\"\n \n

    \n
    "; - var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"summary":["invented the World Wide Web"],"affiliation":[{"type":["h-card"],"properties":{"name":["W3C"],"photo":["http://www.w3.org/Icons/WWW/w3c_home_nb.png"],"url":["http://www.w3.org/"]}}]}}],"rels":{},"rel-urls":{}}; - - it('affiliation', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-resume/affiliation +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-resume', function() { + var htmlFragment = "
    \n

    \n Tim Berners-Lee, \n invented the World Wide Web. \n

    \n Belongs to following groups:\n

    \n \n \"W3C\"\n \n

    \n
    "; + var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"summary":["invented the World Wide Web"],"affiliation":[{"type":["h-card"],"properties":{"name":["W3C"],"photo":["http://www.w3.org/Icons/WWW/w3c_home_nb.png"],"url":["http://www.w3.org/"]}}]}}],"rels":{},"rel-urls":{}}; + + it('affiliation', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-contact.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-contact.js index 876a59970a..f2a1f76f7d 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-contact.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-contact.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-resume/contact -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-resume', function() { - var htmlFragment = "
    \n

    Tim Berners-Lee

    \n

    Invented the World Wide Web.


    \n
    \n

    MIT

    \n

    \n 32 Vassar Street, \n Room 32-G524, \n Cambridge, \n MA \n 02139, \n USA.\n

    \n

    Tel:+1 (617) 253 5702

    \n

    Email:timbl@w3.org

    \n
    \n
    "; - var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"summary":["Invented the World Wide Web."],"contact":[{"value":"MIT","type":["h-card"],"properties":{"name":["MIT"],"street-address":["32 Vassar Street"],"extended-address":["Room 32-G524"],"locality":["Cambridge"],"region":["MA"],"postal-code":["02139"],"country-name":["USA"],"tel":["+1 (617) 253 5702"],"email":["mailto:timbl@w3.org"]}}]}}],"rels":{},"rel-urls":{}}; - - it('contact', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-resume/contact +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-resume', function() { + var htmlFragment = "
    \n

    Tim Berners-Lee

    \n

    Invented the World Wide Web.


    \n
    \n

    MIT

    \n

    \n 32 Vassar Street, \n Room 32-G524, \n Cambridge, \n MA \n 02139, \n USA.\n

    \n

    Tel:+1 (617) 253 5702

    \n

    Email:timbl@w3.org

    \n
    \n
    "; + var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"summary":["Invented the World Wide Web."],"contact":[{"value":"MIT","type":["h-card"],"properties":{"name":["MIT"],"street-address":["32 Vassar Street"],"extended-address":["Room 32-G524"],"locality":["Cambridge"],"region":["MA"],"postal-code":["02139"],"country-name":["USA"],"tel":["+1 (617) 253 5702"],"email":["mailto:timbl@w3.org"]}}]}}],"rels":{},"rel-urls":{}}; + + it('contact', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-education.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-education.js index 8b6af4089d..5e38384344 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-education.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-education.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-resume/education -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-resume', function() { - var htmlFragment = "
    \n

    Tim Berners-Lee

    \n
    \n

    Director of the World Wide Web Foundation

    \n
    \n

    Invented the World Wide Web.


    \n

    \n The Queen's College, Oxford University, \n BA Hons (I) Physics \n –\n \n

    \n
    "; - var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"contact":[{"value":"Director of the World Wide Web Foundation","type":["h-card"],"properties":{"title":["Director of the World Wide Web Foundation"],"name":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"education":[{"value":"The Queen's College, Oxford University","type":["h-event","h-card"],"properties":{"name":["The Queen's College, Oxford University"],"org":["The Queen's College, Oxford University"],"description":["BA Hons (I) Physics"],"start":["1973-09"],"end":["1976-06"]}}]}}],"rels":{},"rel-urls":{}}; - - it('education', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-resume/education +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-resume', function() { + var htmlFragment = "
    \n

    Tim Berners-Lee

    \n
    \n

    Director of the World Wide Web Foundation

    \n
    \n

    Invented the World Wide Web.


    \n

    \n The Queen's College, Oxford University, \n BA Hons (I) Physics \n –\n \n

    \n
    "; + var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"contact":[{"value":"Director of the World Wide Web Foundation","type":["h-card"],"properties":{"title":["Director of the World Wide Web Foundation"],"name":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"education":[{"value":"The Queen's College, Oxford University","type":["h-event","h-card"],"properties":{"name":["The Queen's College, Oxford University"],"org":["The Queen's College, Oxford University"],"description":["BA Hons (I) Physics"],"start":["1973-09"],"end":["1976-06"]}}]}}],"rels":{},"rel-urls":{}}; + + it('education', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-justaname.js index 3ca5cad3a2..2357bf1a22 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-resume/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-resume', function() { - var htmlFragment = "

    Tim Berners-Lee, invented the World Wide Web.

    "; - var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee, invented the World Wide Web."]}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-resume/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-resume', function() { + var htmlFragment = "

    Tim Berners-Lee, invented the World Wide Web.

    "; + var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee, invented the World Wide Web."]}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-skill.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-skill.js index 843bc4a1c4..60a983e04c 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-skill.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-skill.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-resume/skill -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-resume', function() { - var htmlFragment = "
    \n

    \n Tim Berners-Lee, \n invented the World Wide Web.\n

    \n Skills: \n
      \n
    • information systems
    • \n
    • advocacy
    • \n
    • leadership
    • \n
        \n
    "; - var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"summary":["invented the World Wide Web"],"skill":["information systems","advocacy","leadership"]}}],"rels":{},"rel-urls":{}}; - - it('skill', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-resume/skill +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-resume', function() { + var htmlFragment = "
    \n

    \n Tim Berners-Lee, \n invented the World Wide Web.\n

    \n Skills: \n
      \n
    • information systems
    • \n
    • advocacy
    • \n
    • leadership
    • \n
        \n
    "; + var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"summary":["invented the World Wide Web"],"skill":["information systems","advocacy","leadership"]}}],"rels":{},"rel-urls":{}}; + + it('skill', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-work.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-work.js index f0aafed193..d61ea3de56 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-work.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-resume-work.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-resume/work -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-resume', function() { - var htmlFragment = "\n
    \n

    Tim Berners-Lee

    \n
    \n

    Director of the World Wide Web Foundation

    \n
    \n

    Invented the World Wide Web.


    \n
    \n

    Director

    \n

    World Wide Web Foundation

    \n

    \n – Present\n \n

    \n
    \n
    "; - var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"contact":[{"value":"Director of the World Wide Web Foundation","type":["h-card"],"properties":{"title":["Director of the World Wide Web Foundation"],"name":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"experience":[{"value":"World Wide Web Foundation","type":["h-event","h-card"],"properties":{"title":["Director"],"name":["World Wide Web Foundation"],"org":["World Wide Web Foundation"],"url":["http://www.webfoundation.org/"],"start":["2009-01-18"],"duration":["P2Y11M"]}}]}}],"rels":{},"rel-urls":{}}; - - it('work', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-resume/work +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-resume', function() { + var htmlFragment = "\n
    \n

    Tim Berners-Lee

    \n
    \n

    Director of the World Wide Web Foundation

    \n
    \n

    Invented the World Wide Web.


    \n
    \n

    Director

    \n

    World Wide Web Foundation

    \n

    \n – Present\n \n

    \n
    \n
    "; + var expected = {"items":[{"type":["h-resume"],"properties":{"name":["Tim Berners-Lee"],"contact":[{"value":"Director of the World Wide Web Foundation","type":["h-card"],"properties":{"title":["Director of the World Wide Web Foundation"],"name":["Director of the World Wide Web Foundation"]}}],"summary":["Invented the World Wide Web."],"experience":[{"value":"World Wide Web Foundation","type":["h-event","h-card"],"properties":{"title":["Director"],"name":["World Wide Web Foundation"],"org":["World Wide Web Foundation"],"url":["http://www.webfoundation.org/"],"start":["2009-01-18"],"duration":["P2Y11M"]}}]}}],"rels":{},"rel-urls":{}}; + + it('work', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-hevent.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-hevent.js index cb265acf40..e698ee37a9 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-hevent.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-hevent.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review-aggregate/hevent -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review-aggregate', function() { - var htmlFragment = "
    \n
    \n

    Fullfrontal

    \n

    A one day JavaScript Conference held in Brighton

    \n

    \n
    \n \n

    \n 9.9 out of \n 10 \n based on 62 reviews\n

    \n
    "; - var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Fullfrontal","type":["h-event"],"properties":{"name":["Fullfrontal"],"description":["A one day JavaScript Conference held in Brighton"],"start":["2012-11-09"]}}],"rating":["9.9"],"average":["9.9"],"best":["10"],"count":["62"],"name":["Fullfrontal\n A one day JavaScript Conference held in Brighton\n 9th November 2012 \n \n \n \n 9.9 out of \n 10 \n based on 62 reviews"]}}],"rels":{},"rel-urls":{}}; - - it('hevent', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review-aggregate/hevent +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review-aggregate', function() { + var htmlFragment = "
    \n
    \n

    Fullfrontal

    \n

    A one day JavaScript Conference held in Brighton

    \n

    \n
    \n \n

    \n 9.9 out of \n 10 \n based on 62 reviews\n

    \n
    "; + var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Fullfrontal","type":["h-event"],"properties":{"name":["Fullfrontal"],"description":["A one day JavaScript Conference held in Brighton"],"start":["2012-11-09"]}}],"rating":["9.9"],"average":["9.9"],"best":["10"],"count":["62"],"name":["Fullfrontal\n A one day JavaScript Conference held in Brighton\n 9th November 2012 \n \n \n \n 9.9 out of \n 10 \n based on 62 reviews"]}}],"rels":{},"rel-urls":{}}; + + it('hevent', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-justahyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-justahyperlink.js index 040227e701..729fdfb2d3 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-justahyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-justahyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review-aggregate/justahyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review-aggregate', function() { - var htmlFragment = "
    \n

    Mediterranean Wraps

    \n \n Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff.\n \n 4.5 out of 5 \n
    "; - var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Mediterranean Wraps","type":["h-item"],"properties":{"name":["Mediterranean Wraps"]}}],"summary":["Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff."],"rating":["4.5"],"name":["Mediterranean Wraps\n \n Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff.\n \n 4.5 out of 5"]}}],"rels":{},"rel-urls":{}}; - - it('justahyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review-aggregate/justahyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review-aggregate', function() { + var htmlFragment = "
    \n

    Mediterranean Wraps

    \n \n Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff.\n \n 4.5 out of 5 \n
    "; + var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Mediterranean Wraps","type":["h-item"],"properties":{"name":["Mediterranean Wraps"]}}],"summary":["Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff."],"rating":["4.5"],"name":["Mediterranean Wraps\n \n Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff.\n \n 4.5 out of 5"]}}],"rels":{},"rel-urls":{}}; + + it('justahyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-simpleproperties.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-simpleproperties.js index 4eba92bd34..d49cabb5fe 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-simpleproperties.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-aggregate-simpleproperties.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review-aggregate/simpleproperties -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review-aggregate', function() { - var htmlFragment = "
    \n
    \n

    Mediterranean Wraps

    \n

    \n 433 S California Ave, \n Palo Alto, \n CA - \n (650) 321-8189\n

    \n
    \n Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff.\n \n 9.2 out of \n 10 \n based on 17 reviews\n \n
    "; - var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Mediterranean Wraps","type":["h-card"],"properties":{"name":["Mediterranean Wraps"],"street-address":["433 S California Ave"],"locality":["Palo Alto"],"region":["CA"],"tel":["(650) 321-8189"]}}],"summary":["Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff."],"rating":["9.2"],"average":["9.2"],"best":["10"],"count":["17"],"name":["Mediterranean Wraps\n \n 433 S California Ave, \n Palo Alto, \n CA - \n (650) 321-8189\n \n \n Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff.\n \n 9.2 out of \n 10 \n based on 17 reviews"]}}],"rels":{},"rel-urls":{}}; - - it('simpleproperties', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review-aggregate/simpleproperties +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review-aggregate', function() { + var htmlFragment = "
    \n
    \n

    Mediterranean Wraps

    \n

    \n 433 S California Ave, \n Palo Alto, \n CA - \n (650) 321-8189\n

    \n
    \n Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff.\n \n 9.2 out of \n 10 \n based on 17 reviews\n \n
    "; + var expected = {"items":[{"type":["h-review-aggregate"],"properties":{"item":[{"value":"Mediterranean Wraps","type":["h-card"],"properties":{"name":["Mediterranean Wraps"],"street-address":["433 S California Ave"],"locality":["Palo Alto"],"region":["CA"],"tel":["(650) 321-8189"]}}],"summary":["Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff."],"rating":["9.2"],"average":["9.2"],"best":["10"],"count":["17"],"name":["Mediterranean Wraps\n \n 433 S California Ave, \n Palo Alto, \n CA - \n (650) 321-8189\n \n \n Customers flock to this small restaurant for their \n tasty falafel and shawerma wraps and welcoming staff.\n \n 9.2 out of \n 10 \n based on 17 reviews"]}}],"rels":{},"rel-urls":{}}; + + it('simpleproperties', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-hyperlink.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-hyperlink.js index ac81fbd414..3f547d7a90 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-hyperlink.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-hyperlink.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review/hyperlink -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review', function() { - var htmlFragment = "Crepes on Cole"; - var expected = {"items":[{"type":["h-review"],"properties":{"name":["Crepes on Cole"],"url":["https://plus.google.com/116941523817079328322/about"]}}],"rels":{},"rel-urls":{}}; - - it('hyperlink', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review/hyperlink +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review', function() { + var htmlFragment = "Crepes on Cole"; + var expected = {"items":[{"type":["h-review"],"properties":{"name":["Crepes on Cole"],"url":["https://plus.google.com/116941523817079328322/about"]}}],"rels":{},"rel-urls":{}}; + + it('hyperlink', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-implieditem.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-implieditem.js index 4a0af98895..ecde19277c 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-implieditem.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-implieditem.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review/implieditem -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review', function() { - var htmlFragment = "
    \n Crepes on Cole\n

    4.7 out of 5 stars

    \n
    "; - var expected = {"items":[{"type":["h-review"],"properties":{"item":[{"value":"Crepes on Cole","type":["h-item"],"properties":{"name":["Crepes on Cole"],"url":["http://example.com/crepeoncole"]}}],"rating":["4.7"],"name":["Crepes on Cole\n 4.7 out of 5 stars"]}}],"rels":{},"rel-urls":{}}; - - it('implieditem', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review/implieditem +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review', function() { + var htmlFragment = "
    \n Crepes on Cole\n

    4.7 out of 5 stars

    \n
    "; + var expected = {"items":[{"type":["h-review"],"properties":{"item":[{"value":"Crepes on Cole","type":["h-item"],"properties":{"name":["Crepes on Cole"],"url":["http://example.com/crepeoncole"]}}],"rating":["4.7"],"name":["Crepes on Cole\n 4.7 out of 5 stars"]}}],"rels":{},"rel-urls":{}}; + + it('implieditem', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-item.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-item.js index d0ba277701..d8aef51c85 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-item.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-item.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review/item -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review', function() { - var htmlFragment = "\n
    \n

    \n \n Crepes on Cole\n

    \n

    5 out of 5 stars

    \n
    "; - var expected = {"items":[{"type":["h-review"],"properties":{"item":[{"value":"Crepes on Cole","type":["h-item"],"properties":{"photo":["http://example.com/images/photo.gif"],"name":["Crepes on Cole"],"url":["http://example.com/crepeoncole"]}}],"rating":["5"],"name":["Crepes on Cole\n \n 5 out of 5 stars"]}}],"rels":{},"rel-urls":{}}; - - it('item', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review/item +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review', function() { + var htmlFragment = "\n
    \n

    \n \n Crepes on Cole\n

    \n

    5 out of 5 stars

    \n
    "; + var expected = {"items":[{"type":["h-review"],"properties":{"item":[{"value":"Crepes on Cole","type":["h-item"],"properties":{"photo":["http://example.com/images/photo.gif"],"name":["Crepes on Cole"],"url":["http://example.com/crepeoncole"]}}],"rating":["5"],"name":["Crepes on Cole\n \n 5 out of 5 stars"]}}],"rels":{},"rel-urls":{}}; + + it('item', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-justaname.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-justaname.js index dade08f1fa..89523e909f 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-justaname.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-justaname.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review/justaname -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review', function() { - var htmlFragment = "

    Crepes on Cole

    "; - var expected = {"items":[{"type":["h-review"],"properties":{"name":["Crepes on Cole"]}}],"rels":{},"rel-urls":{}}; - - it('justaname', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review/justaname +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review', function() { + var htmlFragment = "

    Crepes on Cole

    "; + var expected = {"items":[{"type":["h-review"],"properties":{"name":["Crepes on Cole"]}}],"rels":{},"rel-urls":{}}; + + it('justaname', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-photo.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-photo.js index 903570f3c6..ee0c41fe6c 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-photo.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-photo.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review/photo -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review', function() { - var htmlFragment = "\"Crepes"; - var expected = {"items":[{"type":["h-review"],"properties":{"name":["Crepes on Cole"],"photo":["http://example.com/images/photo.gif"]}}],"rels":{},"rel-urls":{}}; - - it('photo', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review/photo +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review', function() { + var htmlFragment = "\"Crepes"; + var expected = {"items":[{"type":["h-review"],"properties":{"name":["Crepes on Cole"],"photo":["http://example.com/images/photo.gif"]}}],"rels":{},"rel-urls":{}}; + + it('photo', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-vcard.js b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-vcard.js index 5d46e48d80..8411c4d2ad 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-vcard.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-h-review-vcard.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/h-review/vcard -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('h-review', function() { - var htmlFragment = "
    \n 5 out of 5 stars\n

    Crepes on Cole is awesome

    \n \n Reviewer: Tantek - \n \n \n
    \n

    \n Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.\n

    \n
    \n

    Visit date: April 2005

    \n

    Food eaten: crepe

    \n

    Permanent link for review: http://example.com/crepe

    \n

    Creative Commons Attribution-ShareAlike License

    \n
    "; - var expected = {"items":[{"type":["h-review"],"properties":{"rating":["5"],"name":["Crepes on Cole is awesome"],"reviewer":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"]}}],"reviewed":["2005-04-18"],"description":[{"value":"Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.","html":"\n

    \n Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.\n

    \n "}],"item":[{"value":"Crepes on Cole","type":["h-card"],"properties":{"name":["Crepes on Cole"],"org":["Crepes on Cole"],"adr":[{"value":"San Francisco","type":["h-adr"],"properties":{"locality":["San Francisco"],"name":["San Francisco"]}}]}}],"category":["crepe"],"url":["http://example.com/crepe"]}}],"rels":{"license":["http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License"]},"rel-urls":{"http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License":{"text":"Creative Commons Attribution-ShareAlike License","rels":["license"]}}}; - - it('vcard', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/h-review/vcard +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('h-review', function() { + var htmlFragment = "
    \n 5 out of 5 stars\n

    Crepes on Cole is awesome

    \n \n Reviewer: Tantek - \n \n \n
    \n

    \n Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.\n

    \n
    \n

    Visit date: April 2005

    \n

    Food eaten: crepe

    \n

    Permanent link for review: http://example.com/crepe

    \n

    Creative Commons Attribution-ShareAlike License

    \n
    "; + var expected = {"items":[{"type":["h-review"],"properties":{"rating":["5"],"name":["Crepes on Cole is awesome"],"reviewer":[{"value":"Tantek","type":["h-card"],"properties":{"name":["Tantek"]}}],"reviewed":["2005-04-18"],"description":[{"value":"Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.","html":"\n

    \n Crepes on Cole is one of the best little \n creperies in San Francisco.\n Excellent food and service. Plenty of tables in a variety of sizes \n for parties large and small. Window seating makes for excellent \n people watching to/from the N-Judah which stops right outside. \n I've had many fun social gatherings here, as well as gotten \n plenty of work done thanks to neighborhood WiFi.\n

    \n "}],"item":[{"value":"Crepes on Cole","type":["h-card"],"properties":{"name":["Crepes on Cole"],"org":["Crepes on Cole"],"adr":[{"value":"San Francisco","type":["h-adr"],"properties":{"locality":["San Francisco"],"name":["San Francisco"]}}]}}],"category":["crepe"],"url":["http://example.com/crepe"]}}],"rels":{"license":["http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License"]},"rel-urls":{"http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License":{"text":"Creative Commons Attribution-ShareAlike License","rels":["license"]}}}; + + it('vcard', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-duplicate-rels.js b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-duplicate-rels.js index e4a893ad7c..d65dfdf8b8 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-duplicate-rels.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-duplicate-rels.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/rel/duplicate-rels -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('rel', function() { - var htmlFragment = "\n\nAsides\n\nMatt\n\nAsides\nMatt\n"; - var expected = {"rels":{"bookmark":["http://ma.tt/2015/05/beethoven-mozart-bach/","http://ma.tt/2015/06/jefferson-on-idleness/"],"category":["http://ma.tt/category/asides/"],"tag":["http://ma.tt/category/asides/"],"author":["http://ma.tt/author/saxmatt/"]},"items":[{"type":["h-card"],"properties":{"url":["http://ma.tt/author/saxmatt/"],"name":["Matt"]}},{"type":["h-card"],"properties":{"url":["http://ma.tt/author/saxmatt/"],"name":["Matt"]}}],"rel-urls":{"http://ma.tt/category/asides/":{"rels":["category","tag"],"text":"Asides"},"http://ma.tt/author/saxmatt/":{"rels":["author"],"text":"Matt","title":"View all posts by Matt"},"http://ma.tt/2015/05/beethoven-mozart-bach/":{"rels":["bookmark"],"text":"May 31, 2015","title":"Permalink to Beethoven, Mozart, Bach"},"http://ma.tt/2015/06/jefferson-on-idleness/":{"rels":["bookmark"],"text":"June 2, 2015","title":"Permalink to Jefferson on Idleness"}}}; - - it('duplicate-rels', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/rel/duplicate-rels +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('rel', function() { + var htmlFragment = "\n\nAsides\n\nMatt\n\nAsides\nMatt\n"; + var expected = {"rels":{"bookmark":["http://ma.tt/2015/05/beethoven-mozart-bach/","http://ma.tt/2015/06/jefferson-on-idleness/"],"category":["http://ma.tt/category/asides/"],"tag":["http://ma.tt/category/asides/"],"author":["http://ma.tt/author/saxmatt/"]},"items":[{"type":["h-card"],"properties":{"url":["http://ma.tt/author/saxmatt/"],"name":["Matt"]}},{"type":["h-card"],"properties":{"url":["http://ma.tt/author/saxmatt/"],"name":["Matt"]}}],"rel-urls":{"http://ma.tt/category/asides/":{"rels":["category","tag"],"text":"Asides"},"http://ma.tt/author/saxmatt/":{"rels":["author"],"text":"Matt","title":"View all posts by Matt"},"http://ma.tt/2015/05/beethoven-mozart-bach/":{"rels":["bookmark"],"text":"May 31, 2015","title":"Permalink to Beethoven, Mozart, Bach"},"http://ma.tt/2015/06/jefferson-on-idleness/":{"rels":["bookmark"],"text":"June 2, 2015","title":"Permalink to Jefferson on Idleness"}}}; + + it('duplicate-rels', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-license.js b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-license.js index dead1f480b..d5606f5a3a 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-license.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-license.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/rel/license -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('rel', function() { - var htmlFragment = "cc by 2.5"; - var expected = {"items":[],"rels":{"license":["http://creativecommons.org/licenses/by/2.5/"]},"rel-urls":{"http://creativecommons.org/licenses/by/2.5/":{"text":"cc by 2.5","rels":["license"]}}}; - - it('license', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/rel/license +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('rel', function() { + var htmlFragment = "cc by 2.5"; + var expected = {"items":[],"rels":{"license":["http://creativecommons.org/licenses/by/2.5/"]},"rel-urls":{"http://creativecommons.org/licenses/by/2.5/":{"text":"cc by 2.5","rels":["license"]}}}; + + it('license', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-nofollow.js b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-nofollow.js index 8987399994..4332d35723 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-nofollow.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-nofollow.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/rel/nofollow -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('rel', function() { - var htmlFragment = "Copyrights"; - var expected = {"items":[],"rels":{"nofollow":["http://microformats.org/wiki/microformats:copyrights"]},"rel-urls":{"http://microformats.org/wiki/microformats:copyrights":{"text":"Copyrights","rels":["nofollow"]}}}; - - it('nofollow', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/rel/nofollow +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('rel', function() { + var htmlFragment = "Copyrights"; + var expected = {"items":[],"rels":{"nofollow":["http://microformats.org/wiki/microformats:copyrights"]},"rel-urls":{"http://microformats.org/wiki/microformats:copyrights":{"text":"Copyrights","rels":["nofollow"]}}}; + + it('nofollow', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-rel-urls.js b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-rel-urls.js index c85daee03b..685532f448 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-rel-urls.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-rel-urls.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/rel/rel-urls -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('rel', function() { - var htmlFragment = "author a\nauthor b\npost 1\npost 2\nFrench mobile homepage"; - var expected = {"items":[],"rels":{"author":["http://example.com/a","http://example.com/b"],"in-reply-to":["http://example.com/1","http://example.com/2"],"home":["http://example.com/fr"],"alternate":["http://example.com/fr"]},"rel-urls":{"http://example.com/a":{"rels":["author"],"text":"author a"},"http://example.com/b":{"rels":["author"],"text":"author b"},"http://example.com/1":{"rels":["in-reply-to"],"text":"post 1"},"http://example.com/2":{"rels":["in-reply-to"],"text":"post 2"},"http://example.com/fr":{"rels":["alternate","home"],"media":"handheld","hreflang":"fr","text":"French mobile homepage"}}}; - - it('rel-urls', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/rel/rel-urls +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('rel', function() { + var htmlFragment = "author a\nauthor b\npost 1\npost 2\nFrench mobile homepage"; + var expected = {"items":[],"rels":{"author":["http://example.com/a","http://example.com/b"],"in-reply-to":["http://example.com/1","http://example.com/2"],"home":["http://example.com/fr"],"alternate":["http://example.com/fr"]},"rel-urls":{"http://example.com/a":{"rels":["author"],"text":"author a"},"http://example.com/b":{"rels":["author"],"text":"author b"},"http://example.com/1":{"rels":["in-reply-to"],"text":"post 1"},"http://example.com/2":{"rels":["in-reply-to"],"text":"post 2"},"http://example.com/fr":{"rels":["alternate","home"],"media":"handheld","hreflang":"fr","text":"French mobile homepage"}}}; + + it('rel-urls', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-varying-text-duplicate-rels.js b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-varying-text-duplicate-rels.js index e4896e9818..3b1b72f447 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-varying-text-duplicate-rels.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-varying-text-duplicate-rels.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/rel/varying-text-duplicate-rels -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('rel', function() { - var htmlFragment = "This is a contrived example - not found links like this in the wild:\nAsides\nB-sides\nseasides"; - var expected = {"rels":{"category":["http://ma.tt/category/asides/"],"tag":["http://ma.tt/category/asides/"]},"items":[],"rel-urls":{"http://ma.tt/category/asides/":{"rels":["category","tag"],"text":"Asides"}}}; - - it('varying-text-duplicate-rels', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/rel/varying-text-duplicate-rels +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('rel', function() { + var htmlFragment = "This is a contrived example - not found links like this in the wild:\nAsides\nB-sides\nseasides"; + var expected = {"rels":{"category":["http://ma.tt/category/asides/"],"tag":["http://ma.tt/category/asides/"]},"items":[],"rel-urls":{"http://ma.tt/category/asides/":{"rels":["category","tag"],"text":"Asides"}}}; + + it('varying-text-duplicate-rels', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-xfn-all.js b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-xfn-all.js index cbd6327e7b..3850ad5649 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-xfn-all.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-xfn-all.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/rel/xfn-all -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('rel', function() { - var htmlFragment = ""; - var expected = {"items":[],"rels":{"friend":["http://example.com/profile/jane"],"acquaintance":["http://example.com/profile/jeo"],"contact":["http://example.com/profile/lily"],"met":["http://example.com/profile/oliver"],"co-worker":["http://example.com/profile/emily"],"colleague":["http://example.com/profile/jack"],"neighbor":["http://example.com/profile/isabella"],"child":["http://example.com/profile/harry"],"parent":["http://example.com/profile/sophia"],"sibling":["http://example.com/profile/charlie"],"spouse":["http://example.com/profile/olivia"],"kin":["http://example.com/profile/james"],"muse":["http://example.com/profile/ava"],"crush":["http://example.com/profile/joshua"],"date":["http://example.com/profile/chloe"],"sweetheart":["http://example.com/profile/alfie"],"me":["http://example.com/profile/isla"]},"rel-urls":{"http://example.com/profile/jane":{"text":"jane","rels":["friend"]},"http://example.com/profile/jeo":{"text":"jeo","rels":["acquaintance"]},"http://example.com/profile/lily":{"text":"lily","rels":["contact"]},"http://example.com/profile/oliver":{"text":"oliver","rels":["met"]},"http://example.com/profile/emily":{"text":"emily","rels":["co-worker"]},"http://example.com/profile/jack":{"text":"jack","rels":["colleague"]},"http://example.com/profile/isabella":{"text":"isabella","rels":["neighbor"]},"http://example.com/profile/harry":{"text":"harry","rels":["child"]},"http://example.com/profile/sophia":{"text":"sophia","rels":["parent"]},"http://example.com/profile/charlie":{"text":"charlie","rels":["sibling"]},"http://example.com/profile/olivia":{"text":"olivia","rels":["spouse"]},"http://example.com/profile/james":{"text":"james","rels":["kin"]},"http://example.com/profile/ava":{"text":"ava","rels":["muse"]},"http://example.com/profile/joshua":{"text":"joshua","rels":["crush"]},"http://example.com/profile/chloe":{"text":"chloe","rels":["date"]},"http://example.com/profile/alfie":{"text":"alfie","rels":["sweetheart"]},"http://example.com/profile/isla":{"text":"isla","rels":["me"]}}}; - - it('xfn-all', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/rel/xfn-all +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('rel', function() { + var htmlFragment = ""; + var expected = {"items":[],"rels":{"friend":["http://example.com/profile/jane"],"acquaintance":["http://example.com/profile/jeo"],"contact":["http://example.com/profile/lily"],"met":["http://example.com/profile/oliver"],"co-worker":["http://example.com/profile/emily"],"colleague":["http://example.com/profile/jack"],"neighbor":["http://example.com/profile/isabella"],"child":["http://example.com/profile/harry"],"parent":["http://example.com/profile/sophia"],"sibling":["http://example.com/profile/charlie"],"spouse":["http://example.com/profile/olivia"],"kin":["http://example.com/profile/james"],"muse":["http://example.com/profile/ava"],"crush":["http://example.com/profile/joshua"],"date":["http://example.com/profile/chloe"],"sweetheart":["http://example.com/profile/alfie"],"me":["http://example.com/profile/isla"]},"rel-urls":{"http://example.com/profile/jane":{"text":"jane","rels":["friend"]},"http://example.com/profile/jeo":{"text":"jeo","rels":["acquaintance"]},"http://example.com/profile/lily":{"text":"lily","rels":["contact"]},"http://example.com/profile/oliver":{"text":"oliver","rels":["met"]},"http://example.com/profile/emily":{"text":"emily","rels":["co-worker"]},"http://example.com/profile/jack":{"text":"jack","rels":["colleague"]},"http://example.com/profile/isabella":{"text":"isabella","rels":["neighbor"]},"http://example.com/profile/harry":{"text":"harry","rels":["child"]},"http://example.com/profile/sophia":{"text":"sophia","rels":["parent"]},"http://example.com/profile/charlie":{"text":"charlie","rels":["sibling"]},"http://example.com/profile/olivia":{"text":"olivia","rels":["spouse"]},"http://example.com/profile/james":{"text":"james","rels":["kin"]},"http://example.com/profile/ava":{"text":"ava","rels":["muse"]},"http://example.com/profile/joshua":{"text":"joshua","rels":["crush"]},"http://example.com/profile/chloe":{"text":"chloe","rels":["date"]},"http://example.com/profile/alfie":{"text":"alfie","rels":["sweetheart"]},"http://example.com/profile/isla":{"text":"isla","rels":["me"]}}}; + + it('xfn-all', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-xfn-elsewhere.js b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-xfn-elsewhere.js index 4db25660e7..d982af5cc2 100644 --- a/toolkit/components/microformats/test/standards-tests/mf-v2-rel-xfn-elsewhere.js +++ b/toolkit/components/microformats/test/standards-tests/mf-v2-rel-xfn-elsewhere.js @@ -1,27 +1,27 @@ -/* +/* Microformats Test Suite - Downloaded from github repo: microformats/tests version v0.1.24 -Mocha integration test from: microformats-v2/rel/xfn-elsewhere -The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) -*/ - -assert = chai.assert; - - -describe('rel', function() { - var htmlFragment = ""; - var expected = {"items":[],"rels":{"me":["http://twitter.com/glennjones","http://delicious.com/glennjonesnet/","https://plus.google.com/u/0/105161464208920272734/about","http://lanyrd.com/people/glennjones/","http://github.com/glennjones","http://www.flickr.com/photos/glennjonesnet/","http://www.linkedin.com/in/glennjones","http://www.slideshare.net/glennjones/presentations"]},"rel-urls":{"http://twitter.com/glennjones":{"text":"twitter","rels":["me"]},"http://delicious.com/glennjonesnet/":{"text":"delicious","rels":["me"]},"https://plus.google.com/u/0/105161464208920272734/about":{"text":"google+","rels":["me"]},"http://lanyrd.com/people/glennjones/":{"text":"lanyrd","rels":["me"]},"http://github.com/glennjones":{"text":"github","rels":["me"]},"http://www.flickr.com/photos/glennjonesnet/":{"text":"flickr","rels":["me"]},"http://www.linkedin.com/in/glennjones":{"text":"linkedin","rels":["me"]},"http://www.slideshare.net/glennjones/presentations":{"text":"slideshare","rels":["me"]}}}; - - it('xfn-elsewhere', function(){ - var doc, dom, node, options, parser, found; - dom = new DOMParser(); - doc = dom.parseFromString( htmlFragment, 'text/html' ); - options ={ - 'document': doc, - 'node': doc, - 'baseUrl': 'http://example.com', - 'dateFormat': 'html5' - }; - found = Microformats.get( options ); - assert.deepEqual(found, expected); - }); -}); +Mocha integration test from: microformats-v2/rel/xfn-elsewhere +The test was built on Fri Sep 25 2015 13:26:26 GMT+0100 (BST) +*/ + +assert = chai.assert; + + +describe('rel', function() { + var htmlFragment = ""; + var expected = {"items":[],"rels":{"me":["http://twitter.com/glennjones","http://delicious.com/glennjonesnet/","https://plus.google.com/u/0/105161464208920272734/about","http://lanyrd.com/people/glennjones/","http://github.com/glennjones","http://www.flickr.com/photos/glennjonesnet/","http://www.linkedin.com/in/glennjones","http://www.slideshare.net/glennjones/presentations"]},"rel-urls":{"http://twitter.com/glennjones":{"text":"twitter","rels":["me"]},"http://delicious.com/glennjonesnet/":{"text":"delicious","rels":["me"]},"https://plus.google.com/u/0/105161464208920272734/about":{"text":"google+","rels":["me"]},"http://lanyrd.com/people/glennjones/":{"text":"lanyrd","rels":["me"]},"http://github.com/glennjones":{"text":"github","rels":["me"]},"http://www.flickr.com/photos/glennjonesnet/":{"text":"flickr","rels":["me"]},"http://www.linkedin.com/in/glennjones":{"text":"linkedin","rels":["me"]},"http://www.slideshare.net/glennjones/presentations":{"text":"slideshare","rels":["me"]}}}; + + it('xfn-elsewhere', function(){ + var doc, dom, node, options, parser, found; + dom = new DOMParser(); + doc = dom.parseFromString( htmlFragment, 'text/html' ); + options ={ + 'document': doc, + 'node': doc, + 'baseUrl': 'http://example.com', + 'dateFormat': 'html5' + }; + found = Microformats.get( options ); + assert.deepEqual(found, expected); + }); +}); diff --git a/toolkit/components/microformats/test/static/javascript/count.js b/toolkit/components/microformats/test/static/javascript/count.js index 3dcf56974a..56a64c05ea 100644 --- a/toolkit/components/microformats/test/static/javascript/count.js +++ b/toolkit/components/microformats/test/static/javascript/count.js @@ -1,8 +1,8 @@ /*! - parse - Used by http://localhost:3000/ - Copyright (C) 2010 - 2015 Glenn Jones. All Rights Reserved. - MIT License: https://raw.github.com/glennjones/microformat-shiv/master/license.txt + parse + Used by http://localhost:3000/ + Copyright (C) 2010 - 2015 Glenn Jones. All Rights Reserved. + MIT License: https://raw.github.com/glennjones/microformat-shiv/master/license.txt */ window.onload = function() { @@ -25,10 +25,10 @@ window.onload = function() { parserJSONElt = document.querySelector('#parser-json pre code') // createHTMLDocument is not well support below ie9 - doc = document.implementation.createHTMLDocument("New Document"); - node = document.createElement('div'); - node.innerHTML = html; - doc.body.appendChild(node); + doc = document.implementation.createHTMLDocument("New Document"); + node = document.createElement('div'); + node.innerHTML = html; + doc.body.appendChild(node); options ={ 'node': node diff --git a/toolkit/components/microformats/test/static/javascript/testrunner.js b/toolkit/components/microformats/test/static/javascript/testrunner.js index ea97fa511b..db8db492e4 100644 --- a/toolkit/components/microformats/test/static/javascript/testrunner.js +++ b/toolkit/components/microformats/test/static/javascript/testrunner.js @@ -1,8 +1,8 @@ /*! - testrunner - Used by http://localhost:3000/testrunner.html - Copyright (C) 2010 - 2015 Glenn Jones. All Rights Reserved. - MIT License: https://raw.github.com/glennjones/microformat-shiv/master/license.txt + testrunner + Used by http://localhost:3000/testrunner.html + Copyright (C) 2010 - 2015 Glenn Jones. All Rights Reserved. + MIT License: https://raw.github.com/glennjones/microformat-shiv/master/license.txt */ var options = { @@ -12,7 +12,7 @@ var options = { }; window.onload = function() { - var test = testData.data[0], + var test = testData.data[0], versionElt = document.querySelector('#version'); versionElt.innerHTML = 'v' + testData.version; @@ -35,23 +35,23 @@ function displayTest(e){ function buildTest( test ){ - var testDetailElt = document.querySelector('.test-detail'), + var testDetailElt = document.querySelector('.test-detail'), nameElt = document.querySelector('#test-name'), - htmlElt = document.querySelector('#test-html pre code'), - jsonElt = document.querySelector('#test-json pre code'), - parserElt = document.querySelector('#parser-json pre code'), - diffElt = document.querySelector('#test-diff pre code'); + htmlElt = document.querySelector('#test-html pre code'), + jsonElt = document.querySelector('#test-json pre code'), + parserElt = document.querySelector('#parser-json pre code'), + diffElt = document.querySelector('#test-diff pre code'); - nameElt.innerHTML = test.name; - htmlElt.innerHTML = htmlEscape( test.html ); - jsonElt.innerHTML = htmlEscape( test.json ); + nameElt.innerHTML = test.name; + htmlElt.innerHTML = htmlEscape( test.html ); + jsonElt.innerHTML = htmlEscape( test.json ); var dom = new DOMParser(); doc = dom.parseFromString( test.html, 'text/html' ); options.node = doc; var mfJSON = Microformats.get( options ); - parserElt.innerHTML = htmlEscape( js_beautify( JSON.stringify(mfJSON) ) ); + parserElt.innerHTML = htmlEscape( js_beautify( JSON.stringify(mfJSON) ) ); // diff json var diff = DeepDiff(JSON.parse(test.json), mfJSON); diff --git a/toolkit/components/nsDefaultCLH.js b/toolkit/components/nsDefaultCLH.js index fec3652f5c..a081bae498 100644 --- a/toolkit/components/nsDefaultCLH.js +++ b/toolkit/components/nsDefaultCLH.js @@ -95,8 +95,8 @@ nsDefaultCLH.prototype = { var win = windowMediator.getMostRecentWindow(singletonWindowType); if (win) { win.focus(); - cmdLine.preventDefault = true; - return; + cmdLine.preventDefault = true; + return; } } catch (e) { } diff --git a/toolkit/components/places/tests/unifiedcomplete/test_416214.js b/toolkit/components/places/tests/unifiedcomplete/test_416214.js index 9b217d5e30..7618e3048f 100644 --- a/toolkit/components/places/tests/unifiedcomplete/test_416214.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_416214.js @@ -33,7 +33,7 @@ add_task(function* test_tag_match_url() { yield check_autocomplete({ search: "superTag", matches: [ { uri: uri1, title: "title", tags: [ "superTag" ], style: [ "bookmark-tag" ] }, - { uri: uri2, title: "title", tags: [ "superTag" ], style: [ "bookmark-tag" ] } ] + { uri: uri2, title: "title", tags: [ "superTag" ], style: [ "bookmark-tag" ] } ] }); yield cleanup(); }); diff --git a/toolkit/components/satchel/test/browser/browser.ini b/toolkit/components/satchel/test/browser/browser.ini index 1890f2d011..57fd1fd422 100644 --- a/toolkit/components/satchel/test/browser/browser.ini +++ b/toolkit/components/satchel/test/browser/browser.ini @@ -1,6 +1,6 @@ [DEFAULT] support-files = - ../subtst_privbrowsing.html + !/toolkit/components/satchel/test/subtst_privbrowsing.html head.js [browser_privbrowsing_perwindowpb.js] diff --git a/toolkit/components/thumbnails/test/authenticate.sjs b/toolkit/components/thumbnails/test/authenticate.sjs new file mode 100644 index 0000000000..58da655cf9 --- /dev/null +++ b/toolkit/components/thumbnails/test/authenticate.sjs @@ -0,0 +1,220 @@ +function handleRequest(request, response) +{ + try { + reallyHandleRequest(request, response); + } catch (e) { + response.setStatusLine("1.0", 200, "AlmostOK"); + response.write("Error handling request: " + e); + } +} + + +function reallyHandleRequest(request, response) { + var match; + var requestAuth = true, requestProxyAuth = true; + + // Allow the caller to drive how authentication is processed via the query. + // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar + // The extra ? allows the user/pass/realm checks to succeed if the name is + // at the beginning of the query string. + var query = "?" + request.queryString; + + var expected_user = "", expected_pass = "", realm = "mochitest"; + var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; + var huge = false, plugin = false, anonymous = false; + var authHeaderCount = 1; + // user=xxx + match = /[^_]user=([^&]*)/.exec(query); + if (match) + expected_user = match[1]; + + // pass=xxx + match = /[^_]pass=([^&]*)/.exec(query); + if (match) + expected_pass = match[1]; + + // realm=xxx + match = /[^_]realm=([^&]*)/.exec(query); + if (match) + realm = match[1]; + + // proxy_user=xxx + match = /proxy_user=([^&]*)/.exec(query); + if (match) + proxy_expected_user = match[1]; + + // proxy_pass=xxx + match = /proxy_pass=([^&]*)/.exec(query); + if (match) + proxy_expected_pass = match[1]; + + // proxy_realm=xxx + match = /proxy_realm=([^&]*)/.exec(query); + if (match) + proxy_realm = match[1]; + + // huge=1 + match = /huge=1/.exec(query); + if (match) + huge = true; + + // plugin=1 + match = /plugin=1/.exec(query); + if (match) + plugin = true; + + // multiple=1 + match = /multiple=([^&]*)/.exec(query); + if (match) + authHeaderCount = match[1]+0; + + // anonymous=1 + match = /anonymous=1/.exec(query); + if (match) + anonymous = true; + + // Look for an authentication header, if any, in the request. + // + // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + // + // This test only supports Basic auth. The value sent by the client is + // "username:password", obscured with base64 encoding. + + var actual_user = "", actual_pass = "", authHeader, authPresent = false; + if (request.hasHeader("Authorization")) { + authPresent = true; + authHeader = request.getHeader("Authorization"); + match = /Basic (.+)/.exec(authHeader); + if (match.length != 2) + throw "Couldn't parse auth header: " + authHeader; + + var userpass = base64ToString(match[1]); // no atob() :-( + match = /(.*):(.*)/.exec(userpass); + if (match.length != 3) + throw "Couldn't decode auth header: " + userpass; + actual_user = match[1]; + actual_pass = match[2]; + } + + var proxy_actual_user = "", proxy_actual_pass = ""; + if (request.hasHeader("Proxy-Authorization")) { + authHeader = request.getHeader("Proxy-Authorization"); + match = /Basic (.+)/.exec(authHeader); + if (match.length != 2) + throw "Couldn't parse auth header: " + authHeader; + + var userpass = base64ToString(match[1]); // no atob() :-( + match = /(.*):(.*)/.exec(userpass); + if (match.length != 3) + throw "Couldn't decode auth header: " + userpass; + proxy_actual_user = match[1]; + proxy_actual_pass = match[2]; + } + + // Don't request authentication if the credentials we got were what we + // expected. + if (expected_user == actual_user && + expected_pass == actual_pass) { + requestAuth = false; + } + if (proxy_expected_user == proxy_actual_user && + proxy_expected_pass == proxy_actual_pass) { + requestProxyAuth = false; + } + + if (anonymous) { + if (authPresent) { + response.setStatusLine("1.0", 400, "Unexpected authorization header found"); + } else { + response.setStatusLine("1.0", 200, "Authorization header not found"); + } + } else { + if (requestProxyAuth) { + response.setStatusLine("1.0", 407, "Proxy authentication required"); + for (i = 0; i < authHeaderCount; ++i) + response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); + } else if (requestAuth) { + response.setStatusLine("1.0", 401, "Authentication required"); + for (i = 0; i < authHeaderCount; ++i) + response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); + } else { + response.setStatusLine("1.0", 200, "OK"); + } + } + + response.setHeader("Content-Type", "application/xhtml+xml", false); + response.write(""); + response.write("

    Login: " + (requestAuth ? "FAIL" : "PASS") + "

    \n"); + response.write("

    Proxy: " + (requestProxyAuth ? "FAIL" : "PASS") + "

    \n"); + response.write("

    Auth: " + authHeader + "

    \n"); + response.write("

    User: " + actual_user + "

    \n"); + response.write("

    Pass: " + actual_pass + "

    \n"); + + if (huge) { + response.write("
    "); + for (i = 0; i < 100000; i++) { + response.write("123456789\n"); + } + response.write("
    "); + response.write("This is a footnote after the huge content fill"); + } + + if (plugin) { + response.write("\n"); + } + + response.write(""); +} + + +// base64 decoder +// +// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() +// doesn't seem to exist. :-( +/* Convert Base64 data to a string */ +const toBinaryTable = [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +]; +const base64Pad = '='; + +function base64ToString(data) { + + var result = ''; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = (data[i] == base64Pad); + // Skip illegal characters and whitespace + if (c == -1) continue; + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) + throw Components.Exception('Corrupted base64 string'); + + return result; +} diff --git a/toolkit/components/thumbnails/test/browser.ini b/toolkit/components/thumbnails/test/browser.ini index 69d95f163e..3d94863dee 100644 --- a/toolkit/components/thumbnails/test/browser.ini +++ b/toolkit/components/thumbnails/test/browser.ini @@ -1,5 +1,6 @@ [DEFAULT] support-files = + authenticate.sjs background_red.html background_red_redirect.sjs background_red_scroll.html diff --git a/toolkit/content/tests/widgets/head.js b/toolkit/content/tests/widgets/head.js index 969da98472..c2ae0c7ae1 100644 --- a/toolkit/content/tests/widgets/head.js +++ b/toolkit/content/tests/widgets/head.js @@ -1,23 +1,23 @@ -"use strict"; - -function waitForCondition(condition, nextTest, errorMsg) { - var tries = 0; - var interval = setInterval(function() { - if (tries >= 30) { - ok(false, errorMsg); - moveOn(); - } - var conditionPassed; - try { - conditionPassed = condition(); - } catch (e) { - ok(false, e + "\n" + e.stack); - conditionPassed = false; - } - if (conditionPassed) { - moveOn(); - } - tries++; - }, 100); - var moveOn = function() { clearInterval(interval); nextTest(); }; -} +"use strict"; + +function waitForCondition(condition, nextTest, errorMsg) { + var tries = 0; + var interval = setInterval(function() { + if (tries >= 30) { + ok(false, errorMsg); + moveOn(); + } + var conditionPassed; + try { + conditionPassed = condition(); + } catch (e) { + ok(false, e + "\n" + e.stack); + conditionPassed = false; + } + if (conditionPassed) { + moveOn(); + } + tries++; + }, 100); + var moveOn = function() { clearInterval(interval); nextTest(); }; +} diff --git a/toolkit/crashreporter/test/unit_ipc/xpcshell.ini b/toolkit/crashreporter/test/unit_ipc/xpcshell.ini index 1f37c69c69..185445370b 100644 --- a/toolkit/crashreporter/test/unit_ipc/xpcshell.ini +++ b/toolkit/crashreporter/test/unit_ipc/xpcshell.ini @@ -2,5 +2,9 @@ head = tail = skip-if = toolkit == 'android' || toolkit == 'gonk' +support-files = + !/toolkit/crashreporter/test/unit/crasher_subprocess_head.js + !/toolkit/crashreporter/test/unit/crasher_subprocess_tail.js + !/toolkit/crashreporter/test/unit/head_crashreporter.js [test_content_annotation.js] diff --git a/toolkit/forgetaboutsite/test/unit/xpcshell.ini b/toolkit/forgetaboutsite/test/unit/xpcshell.ini index 0d1abb25f4..b5f3268f18 100644 --- a/toolkit/forgetaboutsite/test/unit/xpcshell.ini +++ b/toolkit/forgetaboutsite/test/unit/xpcshell.ini @@ -2,5 +2,7 @@ head = head_forgetaboutsite.js ../../../../dom/push/test/xpcshell/head.js tail = skip-if = toolkit == 'android' || toolkit == 'gonk' +support-files = + !/dom/push/test/xpcshell/head.js [test_removeDataFromDomain.js] diff --git a/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js b/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js index a3bf2fa4af..48769f8312 100644 --- a/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js +++ b/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js @@ -23,22 +23,36 @@ function test_importfromPrefs() { // Create own preferences to test Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY", ""); Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY2", ","); - Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST", "whitelist.example.com"); - Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST2", "whitelist2-1.example.com,whitelist2-2.example.com,about:home"); + Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST", "http://whitelist.example.com"); + Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST2", "https://whitelist2-1.example.com,http://whitelist2-2.example.com:8080,about:home"); + Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST3", "whitelist3-1.example.com,about:config"); // legacy style - host only Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.EMPTY", ""); - Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST", "blacklist.example.com,"); - Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST2", ",blacklist2-1.example.com,blacklist2-2.example.com,about:mozilla"); + Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST", "http://blacklist.example.com,"); + Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST2", ",https://blacklist2-1.example.com,http://blacklist2-2.example.com:8080,about:mozilla"); + Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST3", "blacklist3-1.example.com,about:preferences"); // legacy style - host only // Check they are unknown in the permission manager prior to importing. let whitelisted = ["http://whitelist.example.com", - "http://whitelist2-1.example.com", - "http://whitelist2-2.example.com", + "https://whitelist2-1.example.com", + "http://whitelist2-2.example.com:8080", + "http://whitelist3-1.example.com", + "https://whitelist3-1.example.com", + "about:config", "about:home"]; let blacklisted = ["http://blacklist.example.com", - "http://blacklist2-1.example.com", - "http://blacklist2-2.example.com", + "https://blacklist2-1.example.com", + "http://blacklist2-2.example.com:8080", + "http://blacklist3-1.example.com", + "https://blacklist3-1.example.com", + "about:preferences", "about:mozilla"]; - let unknown = whitelisted.concat(blacklisted); + let untouched = ["https://whitelist.example.com", + "https://blacklist.example.com", + "http://whitelist2-1.example.com", + "http://blacklist2-1.example.com", + "https://whitelist2-2.example.com:8080", + "https://blacklist2-2.example.com:8080"]; + let unknown = whitelisted.concat(blacklisted).concat(untouched); for (let url of unknown) { let uri = Services.io.newURI(url, null, null); do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.UNKNOWN_ACTION); @@ -64,4 +78,8 @@ function test_importfromPrefs() { let uri = Services.io.newURI(url, null, null); do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.DENY_ACTION); } + for (let url of untouched) { + let uri = Services.io.newURI(url, null, null); + do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.UNKNOWN_ACTION); + } } diff --git a/toolkit/mozapps/extensions/test/browser/browser.ini b/toolkit/mozapps/extensions/test/browser/browser.ini index dc6f86bd66..ea2e5ad817 100644 --- a/toolkit/mozapps/extensions/test/browser/browser.ini +++ b/toolkit/mozapps/extensions/test/browser/browser.ini @@ -31,6 +31,13 @@ support-files = browser_install1_3.xpi browser_eula.xml browser_purchase.xml + !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi + !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi + !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html + !/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi + !/toolkit/mozapps/extensions/test/xpinstall/theme.xpi + !/toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi + !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi [browser_addonrepository_performance.js] [browser_bug557956.js] diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js b/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js index d95db94ab9..605c4224b3 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js @@ -1,108 +1,108 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// Tests ChromeManifestParser.js - -Components.utils.import("resource://gre/modules/ChromeManifestParser.jsm"); - - -function run_test() { - do_test_pending(); - createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2"); - - startupManager(); +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ - installAllFiles([do_get_addon("test_chromemanifest_1"), - do_get_addon("test_chromemanifest_2"), - do_get_addon("test_chromemanifest_3"), - do_get_addon("test_chromemanifest_4")], - function() { - - restartManager(); - run_test_1(); - }); -} - -function run_test_1() { - AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", - "addon2@tests.mozilla.org", - "addon3@tests.mozilla.org", - "addon4@tests.mozilla.org"], - function([a1, a2, a3, a4]) { - // addon1 - let a1Uri = a1.getResourceURI("/").spec; - let expected = [ - {type: "content", baseURI: a1Uri, args: ["test-addon-1", "chrome/content"]}, - {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "en-US", "locale/en-US"]}, - {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]}, - {type: "overlay", baseURI: a1Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]} - ]; - let manifestURI = a1.getResourceURI("chrome.manifest"); - let manifest = ChromeManifestParser.parseSync(manifestURI); - - do_check_true(Array.isArray(manifest)); - do_check_eq(manifest.length, expected.length); - for (let i = 0; i < manifest.length; i++) { - do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i])); - } - - // addon2 - let a2Uri = a2.getResourceURI("/").spec; - expected = [ - {type: "content", baseURI: a2Uri, args: ["test-addon-1", "chrome/content"]}, - {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "en-US", "locale/en-US"]}, - {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]}, - {type: "overlay", baseURI: a2Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}, - {type: "binary-component", baseURI: a2Uri, args: ["components/something.so"]} - ]; - manifestURI = a2.getResourceURI("chrome.manifest"); - manifest = ChromeManifestParser.parseSync(manifestURI); - - do_check_true(Array.isArray(manifest)); - do_check_eq(manifest.length, expected.length); - for (let i = 0; i < manifest.length; i++) { - do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i])); - } - - // addon3 - let a3Uri = a3.getResourceURI("/").spec; - expected = [ - {type: "content", baseURI: a3Uri, args: ["test-addon-1", "chrome/content"]}, - {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "en-US", "locale/en-US"]}, - {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]}, - {type: "overlay", baseURI: a3Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}, - {type: "binary-component", baseURI: a3Uri, args: ["components/something.so"]}, - {type: "locale", baseURI: "jar:" + a3.getResourceURI("/inner.jar").spec + "!/", args: ["test-addon-1", "en-NZ", "locale/en-NZ"]}, - ]; - manifestURI = a3.getResourceURI("chrome.manifest"); - manifest = ChromeManifestParser.parseSync(manifestURI); - - do_check_true(Array.isArray(manifest)); - do_check_eq(manifest.length, expected.length); - for (let i = 0; i < manifest.length; i++) { - do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i])); - } - - // addon4 - let a4Uri = a4.getResourceURI("/").spec; - expected = [ - {type: "content", baseURI: a4Uri, args: ["test-addon-1", "chrome/content"]}, - {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "en-US", "locale/en-US"]}, - {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]}, - {type: "overlay", baseURI: a4Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}, - {type: "binary-component", baseURI: a4.getResourceURI("components/").spec, args: ["mycomponent.dll"]}, - {type: "binary-component", baseURI: a4.getResourceURI("components/other/").spec, args: ["thermalnuclearwar.dll"]} - ]; - manifestURI = a4.getResourceURI("chrome.manifest"); - manifest = ChromeManifestParser.parseSync(manifestURI); - - do_check_true(Array.isArray(manifest)); - do_check_eq(manifest.length, expected.length); - for (let i = 0; i < manifest.length; i++) { - do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i])); - } - - do_execute_soon(do_test_finished); - }); -} +// Tests ChromeManifestParser.js + +Components.utils.import("resource://gre/modules/ChromeManifestParser.jsm"); + + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2"); + + startupManager(); + + installAllFiles([do_get_addon("test_chromemanifest_1"), + do_get_addon("test_chromemanifest_2"), + do_get_addon("test_chromemanifest_3"), + do_get_addon("test_chromemanifest_4")], + function() { + + restartManager(); + run_test_1(); + }); +} + +function run_test_1() { + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org", + "addon3@tests.mozilla.org", + "addon4@tests.mozilla.org"], + function([a1, a2, a3, a4]) { + // addon1 + let a1Uri = a1.getResourceURI("/").spec; + let expected = [ + {type: "content", baseURI: a1Uri, args: ["test-addon-1", "chrome/content"]}, + {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "en-US", "locale/en-US"]}, + {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]}, + {type: "overlay", baseURI: a1Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]} + ]; + let manifestURI = a1.getResourceURI("chrome.manifest"); + let manifest = ChromeManifestParser.parseSync(manifestURI); + + do_check_true(Array.isArray(manifest)); + do_check_eq(manifest.length, expected.length); + for (let i = 0; i < manifest.length; i++) { + do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i])); + } + + // addon2 + let a2Uri = a2.getResourceURI("/").spec; + expected = [ + {type: "content", baseURI: a2Uri, args: ["test-addon-1", "chrome/content"]}, + {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "en-US", "locale/en-US"]}, + {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]}, + {type: "overlay", baseURI: a2Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}, + {type: "binary-component", baseURI: a2Uri, args: ["components/something.so"]} + ]; + manifestURI = a2.getResourceURI("chrome.manifest"); + manifest = ChromeManifestParser.parseSync(manifestURI); + + do_check_true(Array.isArray(manifest)); + do_check_eq(manifest.length, expected.length); + for (let i = 0; i < manifest.length; i++) { + do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i])); + } + + // addon3 + let a3Uri = a3.getResourceURI("/").spec; + expected = [ + {type: "content", baseURI: a3Uri, args: ["test-addon-1", "chrome/content"]}, + {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "en-US", "locale/en-US"]}, + {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]}, + {type: "overlay", baseURI: a3Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}, + {type: "binary-component", baseURI: a3Uri, args: ["components/something.so"]}, + {type: "locale", baseURI: "jar:" + a3.getResourceURI("/inner.jar").spec + "!/", args: ["test-addon-1", "en-NZ", "locale/en-NZ"]}, + ]; + manifestURI = a3.getResourceURI("chrome.manifest"); + manifest = ChromeManifestParser.parseSync(manifestURI); + + do_check_true(Array.isArray(manifest)); + do_check_eq(manifest.length, expected.length); + for (let i = 0; i < manifest.length; i++) { + do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i])); + } + + // addon4 + let a4Uri = a4.getResourceURI("/").spec; + expected = [ + {type: "content", baseURI: a4Uri, args: ["test-addon-1", "chrome/content"]}, + {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "en-US", "locale/en-US"]}, + {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]}, + {type: "overlay", baseURI: a4Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}, + {type: "binary-component", baseURI: a4.getResourceURI("components/").spec, args: ["mycomponent.dll"]}, + {type: "binary-component", baseURI: a4.getResourceURI("components/other/").spec, args: ["thermalnuclearwar.dll"]} + ]; + manifestURI = a4.getResourceURI("chrome.manifest"); + manifest = ChromeManifestParser.parseSync(manifestURI); + + do_check_true(Array.isArray(manifest)); + do_check_eq(manifest.length, expected.length); + for (let i = 0; i < manifest.length; i++) { + do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i])); + } + + do_execute_soon(do_test_finished); + }); +} diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug757663.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug757663.js index 648c7acc3b..54cee08393 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_bug757663.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug757663.js @@ -1,112 +1,112 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This test verifies that removing a listener during a callback for that type -// of listener still results in all listeners being called. - -var addon1 = { - id: "addon1@tests.mozilla.org", - version: "2.0", - name: "Test 1", - bootstrap: "true", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "1", - maxVersion: "1" - }] -}; - -var listener1 = { - sawEvent: false, - onDisabling: function() { - this.sawEvent = true; - AddonManager.removeAddonListener(this); - }, - onNewInstall: function() { - this.sawEvent = true; - AddonManager.removeInstallListener(this); - } -}; -var listener2 = { - sawEvent: false, - onDisabling: function() { - this.sawEvent = true; - }, - onNewInstall: function() { - this.sawEvent = true; - } -}; -var listener3 = { - sawEvent: false, - onDisabling: function() { - this.sawEvent = true; - }, - onNewInstall: function() { - this.sawEvent = true; - } -}; - -const profileDir = gProfD.clone(); -profileDir.append("extensions"); - - -function run_test() { - do_test_pending(); - createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - - writeInstallRDFForExtension(addon1, profileDir); - startupManager(); - - run_test_1(); -} - -function run_test_1() { - AddonManager.addAddonListener(listener1); - AddonManager.addAddonListener(listener2); - AddonManager.addAddonListener(listener3); - - AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org"], function([a1]) { - do_check_neq(a1, null); - do_check_false(a1.userDisabled); - do_check_true(a1.isActive); - - a1.userDisabled = true; - - do_check_true(listener1.sawEvent); - listener1.sawEvent = false; - do_check_true(listener2.sawEvent); - listener2.sawEvent = false; - do_check_true(listener3.sawEvent); - listener3.sawEvent = false; - - AddonManager.removeAddonListener(listener1); - AddonManager.removeAddonListener(listener2); - AddonManager.removeAddonListener(listener3); - - a1.uninstall(); - run_test_2(); - }); -} - -function run_test_2() { - AddonManager.addInstallListener(listener1); - AddonManager.addInstallListener(listener2); - AddonManager.addInstallListener(listener3); - - AddonManager.getInstallForFile(do_get_addon("test_bug757663"), function(aInstall) { - - do_check_true(listener1.sawEvent); - listener1.sawEvent = false; - do_check_true(listener2.sawEvent); - listener2.sawEvent = false; - do_check_true(listener3.sawEvent); - listener3.sawEvent = false; - - AddonManager.removeInstallListener(listener1); - AddonManager.removeInstallListener(listener2); - AddonManager.removeInstallListener(listener3); - - do_execute_soon(do_test_finished); - }); -} +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This test verifies that removing a listener during a callback for that type +// of listener still results in all listeners being called. + +var addon1 = { + id: "addon1@tests.mozilla.org", + version: "2.0", + name: "Test 1", + bootstrap: "true", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "1", + maxVersion: "1" + }] +}; + +var listener1 = { + sawEvent: false, + onDisabling: function() { + this.sawEvent = true; + AddonManager.removeAddonListener(this); + }, + onNewInstall: function() { + this.sawEvent = true; + AddonManager.removeInstallListener(this); + } +}; +var listener2 = { + sawEvent: false, + onDisabling: function() { + this.sawEvent = true; + }, + onNewInstall: function() { + this.sawEvent = true; + } +}; +var listener3 = { + sawEvent: false, + onDisabling: function() { + this.sawEvent = true; + }, + onNewInstall: function() { + this.sawEvent = true; + } +}; + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + + writeInstallRDFForExtension(addon1, profileDir); + startupManager(); + + run_test_1(); +} + +function run_test_1() { + AddonManager.addAddonListener(listener1); + AddonManager.addAddonListener(listener2); + AddonManager.addAddonListener(listener3); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org"], function([a1]) { + do_check_neq(a1, null); + do_check_false(a1.userDisabled); + do_check_true(a1.isActive); + + a1.userDisabled = true; + + do_check_true(listener1.sawEvent); + listener1.sawEvent = false; + do_check_true(listener2.sawEvent); + listener2.sawEvent = false; + do_check_true(listener3.sawEvent); + listener3.sawEvent = false; + + AddonManager.removeAddonListener(listener1); + AddonManager.removeAddonListener(listener2); + AddonManager.removeAddonListener(listener3); + + a1.uninstall(); + run_test_2(); + }); +} + +function run_test_2() { + AddonManager.addInstallListener(listener1); + AddonManager.addInstallListener(listener2); + AddonManager.addInstallListener(listener3); + + AddonManager.getInstallForFile(do_get_addon("test_bug757663"), function(aInstall) { + + do_check_true(listener1.sawEvent); + listener1.sawEvent = false; + do_check_true(listener2.sawEvent); + listener2.sawEvent = false; + do_check_true(listener3.sawEvent); + listener3.sawEvent = false; + + AddonManager.removeInstallListener(listener1); + AddonManager.removeInstallListener(listener2); + AddonManager.removeInstallListener(listener3); + + do_execute_soon(do_test_finished); + }); +} diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_checkCompatibility_themeOverride.js b/toolkit/mozapps/extensions/test/xpcshell/test_checkCompatibility_themeOverride.js index c872d75c30..b6cb13e087 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_checkCompatibility_themeOverride.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_checkCompatibility_themeOverride.js @@ -1,93 +1,93 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This verifies that the (temporary) -// extensions.checkCompatibility.temporaryThemeOverride_minAppVersion -// preference works. - -var ADDONS = [{ - id: "addon1@tests.mozilla.org", - type: 4, - internalName: "theme1/1.0", - version: "1.0", - name: "Test 1", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "1.0", - maxVersion: "1.0" - }] -}, { - id: "addon2@tests.mozilla.org", - type: 4, - internalName: "theme2/1.0", - version: "1.0", - name: "Test 2", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "2.0", - maxVersion: "2.0" - }] -}]; - -const profileDir = gProfD.clone(); -profileDir.append("extensions"); - - -function run_test() { - do_test_pending(); - createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3.0", "1"); - - for (let a of ADDONS) { - writeInstallRDFForExtension(a, profileDir); - } - - startupManager(); - - run_test_1(); -} - -function run_test_1() { - AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", - "addon2@tests.mozilla.org"], - function([a1, a2]) { - - do_check_neq(a1, null); - do_check_false(a1.isActive); - do_check_false(a1.isCompatible); - do_check_true(a1.appDisabled); - - do_check_neq(a2, null); - do_check_false(a2.isActive); - do_check_false(a2.isCompatible); - do_check_true(a1.appDisabled); - - do_execute_soon(run_test_2); - }); -} - -function run_test_2() { - Services.prefs.setCharPref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "2.0"); - if (isNightlyChannel()) - Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", false); - else - Services.prefs.setBoolPref("extensions.checkCompatibility.3.0", false); - restartManager(); - - AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", - "addon2@tests.mozilla.org"], - function([a1, a2]) { - - do_check_neq(a1, null); - do_check_false(a1.isActive); - do_check_false(a1.isCompatible); - do_check_true(a1.appDisabled); - - do_check_neq(a2, null); - do_check_false(a2.isActive); - do_check_false(a2.isCompatible); - do_check_false(a2.appDisabled); - - do_execute_soon(do_test_finished); - }); -} +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This verifies that the (temporary) +// extensions.checkCompatibility.temporaryThemeOverride_minAppVersion +// preference works. + +var ADDONS = [{ + id: "addon1@tests.mozilla.org", + type: 4, + internalName: "theme1/1.0", + version: "1.0", + name: "Test 1", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "1.0", + maxVersion: "1.0" + }] +}, { + id: "addon2@tests.mozilla.org", + type: 4, + internalName: "theme2/1.0", + version: "1.0", + name: "Test 2", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2.0", + maxVersion: "2.0" + }] +}]; + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3.0", "1"); + + for (let a of ADDONS) { + writeInstallRDFForExtension(a, profileDir); + } + + startupManager(); + + run_test_1(); +} + +function run_test_1() { + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org"], + function([a1, a2]) { + + do_check_neq(a1, null); + do_check_false(a1.isActive); + do_check_false(a1.isCompatible); + do_check_true(a1.appDisabled); + + do_check_neq(a2, null); + do_check_false(a2.isActive); + do_check_false(a2.isCompatible); + do_check_true(a1.appDisabled); + + do_execute_soon(run_test_2); + }); +} + +function run_test_2() { + Services.prefs.setCharPref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "2.0"); + if (isNightlyChannel()) + Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", false); + else + Services.prefs.setBoolPref("extensions.checkCompatibility.3.0", false); + restartManager(); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org"], + function([a1, a2]) { + + do_check_neq(a1, null); + do_check_false(a1.isActive); + do_check_false(a1.isCompatible); + do_check_true(a1.appDisabled); + + do_check_neq(a2, null); + do_check_false(a2.isActive); + do_check_false(a2.isCompatible); + do_check_false(a2.appDisabled); + + do_execute_soon(do_test_finished); + }); +} diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate5.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate5.js index 0109dcf927..885cd5a7c1 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate5.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate5.js @@ -1,139 +1,139 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// Checks that we fail to migrate but still start up ok when there is a SQLITE database -// with no useful data in it. - -const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin"; - -var addon1 = { - id: "addon1@tests.mozilla.org", - version: "1.0", - name: "Test 1", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "1", - maxVersion: "1" - }] -}; - -var addon2 = { - id: "addon2@tests.mozilla.org", - version: "2.0", - name: "Test 5", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "0", - maxVersion: "0" - }] -}; - -var defaultTheme = { - id: "default@tests.mozilla.org", - version: "2.0", - name: "Default theme", - internalName: "classic/1.0", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "1", - maxVersion: "1" - }] -}; - -var theme1 = { - id: "theme1@tests.mozilla.org", - version: "2.0", - name: "Test theme", - internalName: "theme1/1.0", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "1", - maxVersion: "1" - }] -}; - -const profileDir = gProfD.clone(); -profileDir.append("extensions"); - -function run_test() { - do_test_pending(); - createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - - writeInstallRDFForExtension(addon1, profileDir); - writeInstallRDFForExtension(addon2, profileDir); - writeInstallRDFForExtension(defaultTheme, profileDir); - writeInstallRDFForExtension(theme1, profileDir); - - Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0"); - - // Write out a broken database (no userDisabled field) - let dbfile = gProfD.clone(); - dbfile.append("extensions.sqlite"); - let db = AM_Cc["@mozilla.org/storage/service;1"]. - getService(AM_Ci.mozIStorageService). - openDatabase(dbfile); - db.createTable("addon", "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "id TEXT, location TEXT, version TEXT, active INTEGER, " + - "installDate INTEGER"); - db.createTable("targetApplication", "addon_internal_id INTEGER, " + - "id TEXT, minVersion TEXT, maxVersion TEXT"); - let stmt = db.createStatement("INSERT INTO addon VALUES (NULL, :id, :location, " + - ":version, :active, :installDate)"); - - let internal_ids = {}; - - [["addon1@tests.mozilla.org", "app-profile", "1.0", "1", "0"], - ["addon2@tests.mozilla.org", "app-profile", "2.0", "0", "0"], - ["default@tests.mozilla.org", "app-profile", "2.0", "1", "0"], - ["theme1@tests.mozilla.org", "app-profile", "2.0", "0", "0"]].forEach(function(a) { - stmt.params.id = a[0]; - stmt.params.location = a[1]; - stmt.params.version = a[2]; - stmt.params.active = a[3]; - stmt.params.installDate = a[4]; - stmt.execute(); - internal_ids[a[0]] = db.lastInsertRowID; - }); - stmt.finalize(); - - db.schemaVersion = 100; - Services.prefs.setIntPref("extensions.databaseSchema", 100); - db.close(); - - startupManager(); - check_startup_changes("installed", []); - check_startup_changes("updated", []); - check_startup_changes("uninstalled", []); - check_startup_changes("disabled", []); - check_startup_changes("enabled", []); - - AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", - "addon2@tests.mozilla.org", - "default@tests.mozilla.org", - "theme1@tests.mozilla.org"], - function([a1, a2, d, t1]) { - do_check_neq(a1, null); - do_check_false(a1.userDisabled); - do_check_false(a1.appDisabled); - do_check_true(a1.isActive); - - do_check_neq(a2, null); - do_check_false(a2.userDisabled); - do_check_true(a2.appDisabled); - do_check_false(a2.isActive); - - // Should have enabled the selected theme - do_check_neq(t1, null); - do_check_false(t1.userDisabled); - do_check_false(t1.appDisabled); - do_check_true(t1.isActive); - - do_check_neq(d, null); - do_check_true(d.userDisabled); - do_check_false(d.appDisabled); - do_check_false(d.isActive); - - do_execute_soon(do_test_finished); - }); -} +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Checks that we fail to migrate but still start up ok when there is a SQLITE database +// with no useful data in it. + +const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin"; + +var addon1 = { + id: "addon1@tests.mozilla.org", + version: "1.0", + name: "Test 1", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "1", + maxVersion: "1" + }] +}; + +var addon2 = { + id: "addon2@tests.mozilla.org", + version: "2.0", + name: "Test 5", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "0", + maxVersion: "0" + }] +}; + +var defaultTheme = { + id: "default@tests.mozilla.org", + version: "2.0", + name: "Default theme", + internalName: "classic/1.0", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "1", + maxVersion: "1" + }] +}; + +var theme1 = { + id: "theme1@tests.mozilla.org", + version: "2.0", + name: "Test theme", + internalName: "theme1/1.0", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "1", + maxVersion: "1" + }] +}; + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + + writeInstallRDFForExtension(addon1, profileDir); + writeInstallRDFForExtension(addon2, profileDir); + writeInstallRDFForExtension(defaultTheme, profileDir); + writeInstallRDFForExtension(theme1, profileDir); + + Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0"); + + // Write out a broken database (no userDisabled field) + let dbfile = gProfD.clone(); + dbfile.append("extensions.sqlite"); + let db = AM_Cc["@mozilla.org/storage/service;1"]. + getService(AM_Ci.mozIStorageService). + openDatabase(dbfile); + db.createTable("addon", "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "id TEXT, location TEXT, version TEXT, active INTEGER, " + + "installDate INTEGER"); + db.createTable("targetApplication", "addon_internal_id INTEGER, " + + "id TEXT, minVersion TEXT, maxVersion TEXT"); + let stmt = db.createStatement("INSERT INTO addon VALUES (NULL, :id, :location, " + + ":version, :active, :installDate)"); + + let internal_ids = {}; + + [["addon1@tests.mozilla.org", "app-profile", "1.0", "1", "0"], + ["addon2@tests.mozilla.org", "app-profile", "2.0", "0", "0"], + ["default@tests.mozilla.org", "app-profile", "2.0", "1", "0"], + ["theme1@tests.mozilla.org", "app-profile", "2.0", "0", "0"]].forEach(function(a) { + stmt.params.id = a[0]; + stmt.params.location = a[1]; + stmt.params.version = a[2]; + stmt.params.active = a[3]; + stmt.params.installDate = a[4]; + stmt.execute(); + internal_ids[a[0]] = db.lastInsertRowID; + }); + stmt.finalize(); + + db.schemaVersion = 100; + Services.prefs.setIntPref("extensions.databaseSchema", 100); + db.close(); + + startupManager(); + check_startup_changes("installed", []); + check_startup_changes("updated", []); + check_startup_changes("uninstalled", []); + check_startup_changes("disabled", []); + check_startup_changes("enabled", []); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org", + "default@tests.mozilla.org", + "theme1@tests.mozilla.org"], + function([a1, a2, d, t1]) { + do_check_neq(a1, null); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_true(a1.isActive); + + do_check_neq(a2, null); + do_check_false(a2.userDisabled); + do_check_true(a2.appDisabled); + do_check_false(a2.isActive); + + // Should have enabled the selected theme + do_check_neq(t1, null); + do_check_false(t1.userDisabled); + do_check_false(t1.appDisabled); + do_check_true(t1.isActive); + + do_check_neq(d, null); + do_check_true(d.userDisabled); + do_check_false(d.appDisabled); + do_check_false(d.isActive); + + do_execute_soon(do_test_finished); + }); +} diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js b/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js index fc947a18ec..cb6704936c 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js @@ -1,203 +1,203 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -// Tests AddonManager.strictCompatibility and it's related preference, -// extensions.strictCompatibility, and the strictCompatibility option in -// install.rdf - - -// Always compatible -var addon1 = { - id: "addon1@tests.mozilla.org", - version: "1.0", - name: "Test 1", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "1", - maxVersion: "1" - }] -}; - -// Incompatible in strict compatibility mode -var addon2 = { - id: "addon2@tests.mozilla.org", - version: "1.0", - name: "Test 2", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "0.7", - maxVersion: "0.8" - }] -}; - -// Theme - always uses strict compatibility, so is always incompatible -var addon3 = { - id: "addon3@tests.mozilla.org", - version: "1.0", - name: "Test 3", - internalName: "test-theme-3", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "0.8", - maxVersion: "0.9" - }] -}; - -// Opt-in to strict compatibility - always incompatible -var addon4 = { - id: "addon4@tests.mozilla.org", - version: "1.0", - name: "Test 4", - strictCompatibility: true, - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "0.8", - maxVersion: "0.9" - }] -}; - -// Addon from the future - would be marked as compatibile-by-default, -// but minVersion is higher than the app version -var addon5 = { - id: "addon5@tests.mozilla.org", - version: "1.0", - name: "Test 5", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "3", - maxVersion: "5" - }] -}; - -// Extremely old addon - maxVersion is less than the mimimum compat version -// set in extensions.minCompatibleVersion -var addon6 = { - id: "addon6@tests.mozilla.org", - version: "1.0", - name: "Test 6", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "0.1", - maxVersion: "0.2" - }] -}; - -// Dictionary - incompatible in strict compatibility mode -var addon7= { - id: "addon7@tests.mozilla.org", - version: "1.0", - name: "Test 7", - type: "64", - targetApplications: [{ - id: "xpcshell@tests.mozilla.org", - minVersion: "0.8", - maxVersion: "0.9" - }] -}; - - - -const profileDir = gProfD.clone(); -profileDir.append("extensions"); - - -function do_check_compat_status(aStrict, aAddonCompat, aCallback) { - do_check_eq(AddonManager.strictCompatibility, aStrict); - AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", - "addon2@tests.mozilla.org", - "addon3@tests.mozilla.org", - "addon4@tests.mozilla.org", - "addon5@tests.mozilla.org", - "addon6@tests.mozilla.org", - "addon7@tests.mozilla.org"], - function([a1, a2, a3, a4, a5, a6, a7]) { - do_check_neq(a1, null); - do_check_eq(a1.isCompatible, aAddonCompat[0]); - do_check_eq(a1.appDisabled, !aAddonCompat[0]); - do_check_false(a1.strictCompatibility); - - do_check_neq(a2, null); - do_check_eq(a2.isCompatible, aAddonCompat[1]); - do_check_eq(a2.appDisabled, !aAddonCompat[1]); - do_check_false(a2.strictCompatibility); - - do_check_neq(a3, null); - do_check_eq(a3.isCompatible, aAddonCompat[2]); - do_check_eq(a3.appDisabled, !aAddonCompat[2]); - do_check_true(a3.strictCompatibility); - - do_check_neq(a4, null); - do_check_eq(a4.isCompatible, aAddonCompat[3]); - do_check_eq(a4.appDisabled, !aAddonCompat[3]); - do_check_true(a4.strictCompatibility); - - do_check_neq(a5, null); - do_check_eq(a5.isCompatible, aAddonCompat[4]); - do_check_eq(a5.appDisabled, !aAddonCompat[4]); - do_check_false(a5.strictCompatibility); - - do_check_neq(a6, null); - do_check_eq(a6.isCompatible, aAddonCompat[5]); - do_check_eq(a6.appDisabled, !aAddonCompat[5]); - do_check_false(a6.strictCompatibility); - - do_check_neq(a7, null); - do_check_eq(a7.isCompatible, aAddonCompat[6]); - do_check_eq(a7.appDisabled, !aAddonCompat[6]); - do_check_false(a7.strictCompatibility); - - do_execute_soon(aCallback); - }); -} - - -function run_test() { - do_test_pending(); - createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ - writeInstallRDFForExtension(addon1, profileDir); - writeInstallRDFForExtension(addon2, profileDir); - writeInstallRDFForExtension(addon3, profileDir); - writeInstallRDFForExtension(addon4, profileDir); - writeInstallRDFForExtension(addon5, profileDir); - writeInstallRDFForExtension(addon6, profileDir); - writeInstallRDFForExtension(addon7, profileDir); - - Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.1"); - - startupManager(); - - // Should default to enabling strict compat. - do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_1); -} - -function run_test_1() { - do_print("Test 1"); - Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false); - do_check_compat_status(false, [true, true, false, false, false, true, true], run_test_2); -} - -function run_test_2() { - do_print("Test 2"); - restartManager(); - do_check_compat_status(false, [true, true, false, false, false, true, true], run_test_3); -} - -function run_test_3() { - do_print("Test 3"); - Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true); - do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_4); -} - -function run_test_4() { - do_print("Test 4"); - restartManager(); - do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_5); -} - -function run_test_5() { - do_print("Test 5"); - Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false); - Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.4"); - do_check_compat_status(false, [true, true, false, false, false, false, true], do_test_finished); -} +// Tests AddonManager.strictCompatibility and it's related preference, +// extensions.strictCompatibility, and the strictCompatibility option in +// install.rdf + + +// Always compatible +var addon1 = { + id: "addon1@tests.mozilla.org", + version: "1.0", + name: "Test 1", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "1", + maxVersion: "1" + }] +}; + +// Incompatible in strict compatibility mode +var addon2 = { + id: "addon2@tests.mozilla.org", + version: "1.0", + name: "Test 2", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "0.7", + maxVersion: "0.8" + }] +}; + +// Theme - always uses strict compatibility, so is always incompatible +var addon3 = { + id: "addon3@tests.mozilla.org", + version: "1.0", + name: "Test 3", + internalName: "test-theme-3", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "0.8", + maxVersion: "0.9" + }] +}; + +// Opt-in to strict compatibility - always incompatible +var addon4 = { + id: "addon4@tests.mozilla.org", + version: "1.0", + name: "Test 4", + strictCompatibility: true, + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "0.8", + maxVersion: "0.9" + }] +}; + +// Addon from the future - would be marked as compatibile-by-default, +// but minVersion is higher than the app version +var addon5 = { + id: "addon5@tests.mozilla.org", + version: "1.0", + name: "Test 5", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "3", + maxVersion: "5" + }] +}; + +// Extremely old addon - maxVersion is less than the mimimum compat version +// set in extensions.minCompatibleVersion +var addon6 = { + id: "addon6@tests.mozilla.org", + version: "1.0", + name: "Test 6", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "0.1", + maxVersion: "0.2" + }] +}; + +// Dictionary - incompatible in strict compatibility mode +var addon7= { + id: "addon7@tests.mozilla.org", + version: "1.0", + name: "Test 7", + type: "64", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "0.8", + maxVersion: "0.9" + }] +}; + + + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + + +function do_check_compat_status(aStrict, aAddonCompat, aCallback) { + do_check_eq(AddonManager.strictCompatibility, aStrict); + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org", + "addon3@tests.mozilla.org", + "addon4@tests.mozilla.org", + "addon5@tests.mozilla.org", + "addon6@tests.mozilla.org", + "addon7@tests.mozilla.org"], + function([a1, a2, a3, a4, a5, a6, a7]) { + do_check_neq(a1, null); + do_check_eq(a1.isCompatible, aAddonCompat[0]); + do_check_eq(a1.appDisabled, !aAddonCompat[0]); + do_check_false(a1.strictCompatibility); + + do_check_neq(a2, null); + do_check_eq(a2.isCompatible, aAddonCompat[1]); + do_check_eq(a2.appDisabled, !aAddonCompat[1]); + do_check_false(a2.strictCompatibility); + + do_check_neq(a3, null); + do_check_eq(a3.isCompatible, aAddonCompat[2]); + do_check_eq(a3.appDisabled, !aAddonCompat[2]); + do_check_true(a3.strictCompatibility); + + do_check_neq(a4, null); + do_check_eq(a4.isCompatible, aAddonCompat[3]); + do_check_eq(a4.appDisabled, !aAddonCompat[3]); + do_check_true(a4.strictCompatibility); + + do_check_neq(a5, null); + do_check_eq(a5.isCompatible, aAddonCompat[4]); + do_check_eq(a5.appDisabled, !aAddonCompat[4]); + do_check_false(a5.strictCompatibility); + + do_check_neq(a6, null); + do_check_eq(a6.isCompatible, aAddonCompat[5]); + do_check_eq(a6.appDisabled, !aAddonCompat[5]); + do_check_false(a6.strictCompatibility); + + do_check_neq(a7, null); + do_check_eq(a7.isCompatible, aAddonCompat[6]); + do_check_eq(a7.appDisabled, !aAddonCompat[6]); + do_check_false(a7.strictCompatibility); + + do_execute_soon(aCallback); + }); +} + + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + + writeInstallRDFForExtension(addon1, profileDir); + writeInstallRDFForExtension(addon2, profileDir); + writeInstallRDFForExtension(addon3, profileDir); + writeInstallRDFForExtension(addon4, profileDir); + writeInstallRDFForExtension(addon5, profileDir); + writeInstallRDFForExtension(addon6, profileDir); + writeInstallRDFForExtension(addon7, profileDir); + + Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.1"); + + startupManager(); + + // Should default to enabling strict compat. + do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_1); +} + +function run_test_1() { + do_print("Test 1"); + Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false); + do_check_compat_status(false, [true, true, false, false, false, true, true], run_test_2); +} + +function run_test_2() { + do_print("Test 2"); + restartManager(); + do_check_compat_status(false, [true, true, false, false, false, true, true], run_test_3); +} + +function run_test_3() { + do_print("Test 3"); + Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true); + do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_4); +} + +function run_test_4() { + do_print("Test 4"); + restartManager(); + do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_5); +} + +function run_test_5() { + do_print("Test 5"); + Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false); + Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.4"); + do_check_compat_status(false, [true, true, false, false, false, false, true], do_test_finished); +} diff --git a/toolkit/mozapps/installer/package-name.mk b/toolkit/mozapps/installer/package-name.mk index 35906d51a0..62a56ff549 100644 --- a/toolkit/mozapps/installer/package-name.mk +++ b/toolkit/mozapps/installer/package-name.mk @@ -166,7 +166,7 @@ INFO_PATH = $(PKG_PATH) MOZ_SOURCESTAMP_FILE = $(DIST)/$(INFO_PATH)/$(MOZ_INFO_BASENAME).txt MOZ_BUILDINFO_FILE = $(DIST)/$(INFO_PATH)/$(MOZ_INFO_BASENAME).json MOZ_MOZINFO_FILE = $(DIST)/$(INFO_PATH)/$(MOZ_INFO_BASENAME).mozinfo.json -MOZ_TEST_PACKAGES_FILE = $(DIST)/$(INFO_PATH)/test_packages.json +MOZ_TEST_PACKAGES_FILE = $(DIST)/$(PKG_PATH)/$(PKG_BASENAME).test_packages.json # JavaScript Shell ifdef MOZ_SIMPLE_PACKAGE_NAME diff --git a/toolkit/mozapps/installer/packager.py b/toolkit/mozapps/installer/packager.py index a7aa860d3b..f1c6908d08 100644 --- a/toolkit/mozapps/installer/packager.py +++ b/toolkit/mozapps/installer/packager.py @@ -358,7 +358,7 @@ def main(): # shlibsign libraries if launcher.can_launch(): - if not mozinfo.isMac: + if not mozinfo.isMac and buildconfig.substs.get('COMPILE_ENVIRONMENT'): for lib in SIGN_LIBS: libbase = mozpath.join(respath, '%s%s') \ % (buildconfig.substs['DLL_PREFIX'], lib) diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 2719f6be66..b95caf3585 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1244,7 +1244,7 @@ CycleCollectedJSRuntime::JSObjectsTenured() if (!JS::ObjectIsTenured(wrapper)) { MOZ_ASSERT(!cache->PreservingWrapper()); const JSClass* jsClass = js::GetObjectJSClass(wrapper); - jsClass->finalize(nullptr, wrapper); + jsClass->doFinalize(nullptr, wrapper); } } diff --git a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp index 56a6013d91..4bf4f09582 100644 --- a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp +++ b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp @@ -84,14 +84,18 @@ RunTest(JSRuntime* rt, JSContext* cx, ArrayT* array) static void CreateGlobalAndRunTest(JSRuntime* rt, JSContext* cx) { - static const JSClass GlobalClass = { - "global", JSCLASS_GLOBAL_FLAGS, + static const JSClassOps GlobalClassOps = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; + static const JSClass GlobalClass = { + "global", JSCLASS_GLOBAL_FLAGS, + &GlobalClassOps + }; + JS::CompartmentOptions options; options.behaviors().setVersion(JSVERSION_LATEST); JS::PersistentRootedObject global(cx);