author | David Anderson <danderson@mozilla.com> |
Tue, 31 Jan 2012 12:17:35 -0800 | |
changeset 105633 | ce25bca1ad68dd9e4b87644c001804071a4bf600 |
parent 105632 | 990ee31c370a209355cb2d85d07be97b085108b8 (current diff) |
parent 85842 | 29514d9b42165ca04c0d5a8d7dae9934c0dc1955 (diff) |
child 105634 | 4e487dfde168fd6bd9ba69aaf747e6d5e6cd2536 |
push id | 23447 |
push user | danderson@mozilla.com |
push date | Tue, 11 Sep 2012 17:34:27 +0000 |
treeherder | mozilla-central@fdfaef738a00 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 13.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/.hgtags +++ b/.hgtags @@ -68,8 +68,11 @@ 138f593553b66c9f815e8f57870c19d6347f7702 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705 41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816 c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15 54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108 a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16 +bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131 +bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131 +0000000000000000000000000000000000000000 AURORA_BASE_20120131
--- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -588,40 +588,23 @@ nsAccessible::TranslateString(const nsAS aStringOut.Assign(xsValue); } PRUint64 nsAccessible::VisibilityState() { PRUint64 vstates = states::INVISIBLE | states::OFFSCREEN; - // We need to check the parent chain for visibility. - nsAccessible* accessible = this; - do { - // We don't want background tab page content to be aggressively invisible. - // Otherwise this foils screen reader virtual buffer caches. - roles::Role role = accessible->Role(); - if (role == roles::PROPERTYPAGE || role == roles::PANE) - break; - - nsIFrame* frame = accessible->GetFrame(); - if (!frame) - return vstates; - - const nsIView* view = frame->GetView(); - if (view && view->GetVisibility() == nsViewVisibility_kHide) - return vstates; - - } while (accessible = accessible->Parent()); - nsIFrame* frame = GetFrame(); if (!frame) return vstates; const nsCOMPtr<nsIPresShell> shell(GetPresShell()); + if (!shell) + return vstates; // We need to know if at least a kMinPixels around the object is visible, // otherwise it will be marked states::OFFSCREEN. const PRUint16 kMinPixels = 12; const nsSize frameSize = frame->GetSize(); const nsRectVisibility rectVisibility = shell->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize), nsPresContext::CSSPixelsToAppUnits(kMinPixels)); @@ -639,16 +622,20 @@ nsAccessible::VisibilityState() frame->GetRect().IsEmpty()) { nsAutoString renderedText; frame->GetRenderedText(&renderedText, nsnull, nsnull, 0, 1); if (renderedText.IsEmpty()) return vstates; } + // XXX Do we really need to cross from content to chrome ancestor? + if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) + return vstates; + // Assume we are visible enough. return vstates &= ~states::INVISIBLE; } PRUint64 nsAccessible::NativeState() { PRUint64 state = 0;
--- a/accessible/src/base/nsRootAccessible.cpp +++ b/accessible/src/base/nsRootAccessible.cpp @@ -569,84 +569,42 @@ nsRootAccessible::Shutdown() { // Called manually or by nsAccessNode::LastRelease() if (!mWeakShell) return; // Already shutdown nsDocAccessibleWrap::Shutdown(); } -// nsRootAccessible protected member -already_AddRefed<nsIDocShellTreeItem> -nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart) -{ - if (!aStart) { - return nsnull; - } - - PRInt32 itemType; - aStart->GetItemType(&itemType); - if (itemType == nsIDocShellTreeItem::typeContent) { - nsDocAccessible *accDoc = nsAccUtils::GetDocAccessibleFor(aStart); - - // Hidden documents don't have accessibles (like SeaMonkey's sidebar), - // they are of no interest for a11y. - if (!accDoc) - return nsnull; - - // If ancestor chain of accessibles is not completely visible, - // don't use this one. This happens for example if it's inside - // a background tab (tabbed browsing) - nsAccessible* parent = accDoc->Parent(); - while (parent) { - if (parent->State() & states::INVISIBLE) - return nsnull; - - if (parent == this) - break; // Don't check past original root accessible we started with - - parent = parent->Parent(); - } - - NS_ADDREF(aStart); - return aStart; - } - nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aStart)); - if (treeNode) { - PRInt32 subDocuments; - treeNode->GetChildCount(&subDocuments); - for (PRInt32 count = 0; count < subDocuments; count ++) { - nsCOMPtr<nsIDocShellTreeItem> treeItemChild, contentTreeItem; - treeNode->GetChildAt(count, getter_AddRefs(treeItemChild)); - NS_ENSURE_TRUE(treeItemChild, nsnull); - contentTreeItem = GetContentDocShell(treeItemChild); - if (contentTreeItem) { - NS_ADDREF(aStart = contentTreeItem); - return aStart; - } - } - } - return nsnull; -} - // nsIAccessible method Relation nsRootAccessible::RelationByType(PRUint32 aType) { if (!mDocument || aType != nsIAccessibleRelation::RELATION_EMBEDS) return nsDocAccessibleWrap::RelationByType(aType); - nsCOMPtr<nsIDocShellTreeItem> treeItem = - nsCoreUtils::GetDocShellTreeItemFor(mDocument); - nsCOMPtr<nsIDocShellTreeItem> contentTreeItem = GetContentDocShell(treeItem); - // there may be no content area, so we need a null check - if (!contentTreeItem) - return Relation(); + nsIDOMWindow* rootWindow = mDocument->GetWindow(); + if (rootWindow) { + nsCOMPtr<nsIDOMWindow> contentWindow; + rootWindow->GetContent(getter_AddRefs(contentWindow)); + if (contentWindow) { + nsCOMPtr<nsIDOMDocument> contentDOMDocument; + contentWindow->GetDocument(getter_AddRefs(contentDOMDocument)); + nsCOMPtr<nsIDocument> contentDocumentNode = + do_QueryInterface(contentDOMDocument); + if (contentDocumentNode) { + nsDocAccessible* contentDocument = + GetAccService()->GetDocAccessible(contentDocumentNode); + if (contentDocument) + return Relation(contentDocument); + } + } + } - return Relation(nsAccUtils::GetDocAccessibleFor(contentTreeItem)); + return Relation(); } //////////////////////////////////////////////////////////////////////////////// // Protected members void nsRootAccessible::HandlePopupShownEvent(nsAccessible* aAccessible) {
--- a/accessible/src/base/nsRootAccessible.h +++ b/accessible/src/base/nsRootAccessible.h @@ -122,18 +122,17 @@ protected: #ifdef MOZ_XUL void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent, nsXULTreeAccessible* aAccessible); void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent, nsXULTreeAccessible* aAccessible); PRUint32 GetChromeFlags(); #endif - already_AddRefed<nsIDocShellTreeItem> - GetContentDocShell(nsIDocShellTreeItem *aStart); + nsRefPtr<nsCaretAccessible> mCaretAccessible; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsRootAccessible, NS_ROOTACCESSIBLE_IMPL_CID) inline nsRootAccessible* nsAccessible::AsRoot() {
--- a/accessible/tests/mochitest/relations/Makefile.in +++ b/accessible/tests/mochitest/relations/Makefile.in @@ -43,16 +43,17 @@ VPATH = @srcdir@ relativesrcdir = accessible/relations include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk # test_tabbrowser.xul disabled for misusing <tabbrowser> (bug 715857) _TEST_FILES =\ + test_embeds.xul \ test_general.html \ test_general.xul \ test_tree.xul \ test_update.html \ $(NULL) libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/relations/test_embeds.xul @@ -0,0 +1,152 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Embeds relation tests"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + <script type="application/javascript" + src="../events.js"></script> + <script type="application/javascript" + src="../relations.js"></script> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Helpers + + function tabBrowser() + { + return gBrowserWnd.gBrowser; + } + + function currentBrowser() + { + return tabBrowser().selectedBrowser; + } + + function currentTabDocument() + { + return currentBrowser().contentDocument; + } + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function loadURI(aURI) + { + this.invoke = function loadURI_invoke() + { + tabBrowser().loadURI(aURI); + } + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, currentBrowser) + ]; + + this.finalCheck = function loadURI_finalCheck() + { + testRelation(gBrowserWnd.document, RELATION_EMBEDS, + getAccessible(currentTabDocument())); + } + + this.getID = function loadURI_getID() + { + return "load uri " + aURI; + } + } + + function loadOneTab(aURI) + { + this.invoke = function loadOneTab_invoke() + { + tabBrowser().loadOneTab(aURI, null, null, null, false); + } + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, currentBrowser) + ]; + + this.finalCheck = function loadURI_finalCheck() + { + testRelation(gBrowserWnd.document, RELATION_EMBEDS, + getAccessible(currentTabDocument())); + } + + this.getID = function loadOneTab_getID() + { + return "load uri '" + aURI + "' in new tab"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Testing + + var gBrowserWnd = null; + function loadBrowser() + { + gBrowserWnd = window.openDialog("chrome://browser/content/", "_blank", + "chrome,all,dialog=no", "about:"); + + addA11yLoadEvent(startTests, gBrowserWnd); + } + + function startTests() + { + // Wait for tab load. + var browser = currentBrowser(); + addA11yLoadEvent(doTests, browser.contentWindow); + } + + //gA11yEventDumpToConsole = true; // debug + + var gQueue = null; + function doTests() + { + testRelation(gBrowserWnd.document, RELATION_EMBEDS, + getAccessible(currentTabDocument())); + + gQueue = new eventQueue(); + + gQueue.push(new loadURI("about:about")); + gQueue.push(new loadOneTab("about:mozilla")); + + gQueue.onFinish = function() + { + gBrowserWnd.close(); + } + gQueue.invoke(); + } + + SimpleTest.waitForExplicitFinish(); + addLoadEvent(loadBrowser); + ]]> + </script> + + <vbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=707654" + title="Embeds relation on root accessible can return not content document"> + Mozilla Bug 707654 + </a> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + </vbox> +</window>
--- a/accessible/tests/mochitest/relations/test_general.html +++ b/accessible/tests/mochitest/relations/test_general.html @@ -121,29 +121,16 @@ testRelation("caption", RELATION_LABEL_FOR, "table"); testRelation("table", RELATION_LABELLED_BY, "caption"); // 'labelled by'/'label for' relation for html:fieldset and // html:legend testRelation("legend", RELATION_LABEL_FOR, "fieldset"); testRelation("fieldset", RELATION_LABELLED_BY, "legend"); - // 'embeds' relation for root accessible - var docAcc = null; - var parentOfDocAcc = null; - var parentDocAcc = getAccessible(document); - do { - docAcc = parentDocAcc; - parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]); - parentDocAcc = getAccessible(parentOfDocAcc.document, - [nsIAccessible]); - } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW) - - testRelation(parentDocAcc, RELATION_EMBEDS, docAcc); - // finish test SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); addA11yLoadEvent(doTest); </script>
--- a/accessible/tests/mochitest/relations/test_general.xul +++ b/accessible/tests/mochitest/relations/test_general.xul @@ -98,29 +98,16 @@ // aria-flowto, multiple relations testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]); testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1"); testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1"); // 'default button' relation testRelation("textbox", RELATION_DEFAULT_BUTTON, "submit"); - // 'embeds' relation for root accessible - var docAcc = null; - var parentOfDocAcc = null; - var parentDocAcc = getAccessible(document); - do { - docAcc = parentDocAcc; - parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]); - parentDocAcc = getAccessible(parentOfDocAcc.document, - [nsIAccessible]); - } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW) - - testRelation(parentDocAcc, RELATION_EMBEDS, docAcc); - // 'labelled by'/'label for' relation for xul:goupbox and xul:label of // xul:caption var groupboxAcc = getAccessible("groupbox"); var labelAcc = groupboxAcc.firstChild; testRelation(labelAcc, RELATION_LABEL_FOR, groupboxAcc); testRelation(groupboxAcc, RELATION_LABELLED_BY, labelAcc); // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
--- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -42,16 +42,17 @@ MOZ_APP_VERSION=11.0a1 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official # MOZ_APP_DISPLAYNAME is set by branding/configure.sh MOZ_SAFE_BROWSING= MOZ_SERVICES_SYNC= +MOZ_WEBSMS_BACKEND=1 MOZ_DISABLE_DOMCRYPTO=1 MOZ_APP_STATIC_INI=1 if test "$OS_TARGET" = "Android"; then MOZ_CAPTURE=1 MOZ_RAW=1 fi
--- a/browser/app/Makefile.in +++ b/browser/app/Makefile.in @@ -103,21 +103,16 @@ include $(topsrcdir)/config/config.mk ifdef _MSC_VER # Always enter a Windows program through wmain, whether or not we're # a console application. WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup endif ifeq ($(OS_ARCH),WINNT) -OS_LIBS += $(call EXPAND_LIBNAME,comctl32 comdlg32 uuid shell32 ole32 oleaut32 version winspool) -OS_LIBS += $(call EXPAND_LIBNAME,usp10 msimg32) -endif - -ifeq ($(OS_ARCH),WINNT) RCINCLUDE = splash.rc ifndef GNU_CC RCFLAGS += -DMOZ_PHOENIX -I$(srcdir) else RCFLAGS += -DMOZ_PHOENIX --include-dir $(srcdir) endif ifdef DEBUG RCFLAGS += -DDEBUG
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -275,30 +275,30 @@ pref("browser.urlbar.clickSelectsAll", f #else pref("browser.urlbar.clickSelectsAll", true); #endif #ifdef UNIX_BUT_NOT_MAC pref("browser.urlbar.doubleClickSelectsAll", true); #else pref("browser.urlbar.doubleClickSelectsAll", false); #endif -pref("browser.urlbar.autoFill", true); +pref("browser.urlbar.autoFill", false); // 0: Match anywhere (e.g., middle of words) // 1: Match on word boundaries and then try matching anywhere // 2: Match only on word boundaries (e.g., after / or .) // 3: Match at the beginning of the url or title pref("browser.urlbar.matchBehavior", 1); pref("browser.urlbar.filter.javascript", true); // the maximum number of results to show in autocomplete when doing richResults pref("browser.urlbar.maxRichResults", 12); // The amount of time (ms) to wait after the user has stopped typing // before starting to perform autocomplete. 50 is the default set in // autocomplete.xml. -pref("browser.urlbar.delay", 0); +pref("browser.urlbar.delay", 50); // The special characters below can be typed into the urlbar to either restrict // the search to visited history, bookmarked, tagged pages; or force a match on // just the title text or url. pref("browser.urlbar.restrict.history", "^"); pref("browser.urlbar.restrict.bookmark", "*"); pref("browser.urlbar.restrict.tag", "+"); pref("browser.urlbar.restrict.openpage", "%");
--- a/browser/base/content/test/Makefile.in +++ b/browser/base/content/test/Makefile.in @@ -88,16 +88,17 @@ endif # # browser_sanitizeDialog_treeView.js is disabled until the tree view is added # back to the clear recent history dialog (sanitize.xul), if it ever is (bug # 480169) # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638. # browser_bug321000.js is disabled because newline handling is shaky (bug 592528) +# browser_urlbarAutoFillTrimURLs.js is disabled till bug 720792 is fixed _BROWSER_FILES = \ head.js \ browser_typeAheadFind.js \ browser_keywordSearch.js \ browser_allTabsPanel.js \ browser_alltabslistener.js \ browser_bug304198.js \ @@ -215,17 +216,16 @@ endif browser_scope.js \ browser_selectTabAtIndex.js \ browser_tab_dragdrop.js \ browser_tab_dragdrop2.js \ browser_tab_dragdrop2_frame1.xul \ browser_tabfocus.js \ browser_tabs_isActive.js \ browser_tabs_owner.js \ - browser_urlbarAutoFillTrimURLs.js \ browser_urlbarCopying.js \ browser_urlbarEnter.js \ browser_urlbarTrimURLs.js \ browser_urlHighlight.js \ browser_visibleFindSelection.js \ browser_visibleTabs.js \ browser_visibleTabs_contextMenu.js \ browser_visibleTabs_bookmarkAllPages.js \
--- a/browser/config/mozconfigs/linux32/release +++ b/browser/config/mozconfigs/linux32/release @@ -13,8 +13,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. ac_add_options --enable-warnings-as-errors + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/linux64/release +++ b/browser/config/mozconfigs/linux64/release @@ -13,8 +13,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. ac_add_options --enable-warnings-as-errors + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/macosx-universal/release +++ b/browser/config/mozconfigs/macosx-universal/release @@ -9,8 +9,11 @@ ac_add_options --enable-official-brandin # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. ac_add_options --enable-warnings-as-errors + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/win32/debug +++ b/browser/config/mozconfigs/win32/debug @@ -1,7 +1,9 @@ ac_add_options --enable-debug ac_add_options --enable-trace-malloc # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 mk_add_options MOZ_MAKE_FLAGS=-j1 + +. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/l10n-mozconfig +++ b/browser/config/mozconfigs/win32/l10n-mozconfig @@ -1,4 +1,6 @@ ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} ac_add_options --enable-update-packaging ac_add_options --enable-official-branding ac_add_options --with-l10n-base=../../l10n-central + +. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/nightly +++ b/browser/config/mozconfigs/win32/nightly @@ -9,8 +9,10 @@ ac_add_options --enable-jemalloc ac_add_options --enable-js-diagnostics # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 mk_add_options MOZ_MAKE_FLAGS=-j1 + +. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/release +++ b/browser/config/mozconfigs/win32/release @@ -6,8 +6,10 @@ ac_add_options --enable-update-channel=$ ac_add_options --enable-update-packaging ac_add_options --enable-jemalloc ac_add_options --enable-official-branding # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 + +. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/devtools/styleeditor/styleeditor.css +++ b/browser/devtools/styleeditor/styleeditor.css @@ -68,16 +68,24 @@ li.error > .stylesheet-info > .styleshee display: inline; cursor: pointer; } .splitview-nav > li > hgroup.stylesheet-info { -moz-box-pack: center; } +.stylesheet-name { + white-space: nowrap; +} + +li.unsaved > hgroup > h1 > .stylesheet-name:before { + content: "*"; +} + .stylesheet-enabled { display: -moz-box; } .stylesheet-saveButton { display: none; }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js @@ -29,16 +29,27 @@ function run(aChrome) is(aChrome.editors.length, 2, "there is 2 stylesheets initially"); } let gAddedCount = 0; // to add new stylesheet after the 2 initial stylesheets let gNewEditor; // to make sure only one new stylesheet got created let gUpdateCount = 0; // to make sure only one Update event is triggered let gCommitCount = 0; // to make sure only one Commit event is triggered +let gTransitionEndCount = 0; + +function finishOnTransitionEndAndCommit() { + if (gCommitCount && gTransitionEndCount) { + is(gUpdateCount, 1, "received one Update event"); + is(gCommitCount, 1, "received one Commit event"); + is(gTransitionEndCount, 1, "received one transitionend event"); + + finish(); + } +} function testEditorAdded(aChrome, aEditor) { gAddedCount++; if (gAddedCount == 2) { waitForFocus(function () { // create a new style sheet let newButton = gChromeWindow.document.querySelector(".style-editor-newButton"); EventUtils.synthesizeMouseAtCenter(newButton, {}, gChromeWindow); @@ -81,16 +92,26 @@ function testEditorAdded(aChrome, aEdito for each (let c in TESTCASE_CSS_SOURCE) { EventUtils.synthesizeKey(c, {}, gChromeWindow); } is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}", "rule bracket has been auto-closed"); + // we know that the testcase above will start a CSS transition + content.addEventListener("transitionend", function () { + gTransitionEndCount++; + + let computedStyle = content.getComputedStyle(content.document.body, null); + is(computedStyle.backgroundColor, "rgb(255, 0, 0)", + "content's background color has been updated to red"); + + executeSoon(finishOnTransitionEndAndCommit); + }, false); }, gChromeWindow) ; }, onUpdate: function (aEditor) { gUpdateCount++; ok(content.document.documentElement.classList.contains(TRANSITION_CLASS), "StyleEditor's transition class has been added to content"); @@ -104,32 +125,23 @@ function testEditorAdded(aChrome, aEdito ok(aEditor.hasFlag("unsaved"), "new editor has UNSAVED flag after modification"); let summary = aChrome.getSummaryElementForEditor(aEditor); let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; is(parseInt(ruleCount), 1, "new editor shows 1 rule after modification"); - let computedStyle = content.getComputedStyle(content.document.body, null); - is(computedStyle.backgroundColor, "rgb(255, 0, 0)", - "content's background color has been updated to red"); - ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS), "StyleEditor's transition class has been removed from content"); - executeSoon(function () { - is(gUpdateCount, 1, "received only one Update event (throttle)"); - is(gCommitCount, 1, "received only one Commit event (throttle)"); + aEditor.removeActionListener(listener); + gNewEditor = null; - aEditor.removeActionListener(listener); - - gNewEditor = null; - finish(); - }); + executeSoon(finishOnTransitionEndAndCommit); } }; aEditor.addActionListener(listener); if (aEditor.sourceEditor) { listener.onAttach(aEditor); } }
--- a/browser/devtools/tilt/Tilt.jsm +++ b/browser/devtools/tilt/Tilt.jsm @@ -225,35 +225,33 @@ Tilt.prototype = { this.tiltButton.checked = false; }, /** * Handles the event fired when a tab is selected. */ _onTabSelect: function T__onTabSelect() { - if (this.visualizers[this.currentWindowId]) { + if (this.currentInstance) { Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.SHOWN, null); } else { Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.HIDDEN, null); } }, /** * A node was selected in the Inspector. * Called from InspectorUI. * * @param {Element} aNode * the newly selected node */ update: function T_update(aNode) { - let id = this.currentWindowId; - - if (this.visualizers[id]) { - this.visualizers[id].presenter.highlightNode(aNode); + if (this.currentInstance) { + this.currentInstance.presenter.highlightNode(aNode); } }, /** * Add the browser event listeners to handle state changes. * Called from InspectorUI. */ setup: function T_setup() @@ -272,27 +270,28 @@ Tilt.prototype = { Services.obs.addObserver( this._whenInitializing.bind(this), TILT_NOTIFICATIONS.INITIALIZING, false); Services.obs.addObserver( this._whenDestroyed.bind(this), TILT_NOTIFICATIONS.DESTROYED, false); Services.obs.addObserver( this._whenShown.bind(this), TILT_NOTIFICATIONS.SHOWN, false); Services.obs.addObserver( this._whenHidden.bind(this), TILT_NOTIFICATIONS.HIDDEN, false); + Services.obs.addObserver(function(aSubject, aTopic, aWinId) { this.destroy(aWinId); }.bind(this), this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.DESTROYED, false); this.chromeWindow.gBrowser.tabContainer.addEventListener("TabSelect", this._onTabSelect.bind(this), false); // FIXME: this shouldn't be done here, see bug #705131 let onOpened = function() { - if (this.visualizers[this.currentWindowId]) { + if (this.currentInstance) { this.chromeWindow.InspectorUI.stopInspecting(); this.inspectButton.disabled = true; this.highlighterContainer.style.display = "none"; } }.bind(this); let onClosed = function() { this.inspectButton.disabled = false; @@ -321,18 +320,26 @@ Tilt.prototype = { (TiltGL.isWebGLForceEnabled() || TiltGL.isWebGLSupported())); }, /** * Gets the ID of the current window object to identify the visualizer. */ get currentWindowId() { - let gBrowser = this.chromeWindow.gBrowser; - return TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow); + return TiltUtils.getWindowId( + this.chromeWindow.gBrowser.selectedBrowser.contentWindow); + }, + + /** + * Gets the visualizer instance for the current tab. + */ + get currentInstance() + { + return this.visualizers[this.currentWindowId]; }, /** * Gets the Tilt button in the Inspector toolbar. */ get tiltButton() { return this.chromeWindow.document.getElementById(
--- a/browser/devtools/tilt/TiltVisualizer.jsm +++ b/browser/devtools/tilt/TiltVisualizer.jsm @@ -1047,16 +1047,17 @@ TiltVisualizer.Controller.prototype = { canvas.addEventListener("mousedown", this.onMouseDown, false); canvas.addEventListener("mouseup", this.onMouseUp, false); canvas.addEventListener("mousemove", this.onMouseMove, false); canvas.addEventListener("mouseover", this.onMouseOver, false); canvas.addEventListener("mouseout", this.onMouseOut, false); canvas.addEventListener("MozMousePixelScroll", this.onMozScroll, false); canvas.addEventListener("keydown", this.onKeyDown, false); canvas.addEventListener("keyup", this.onKeyUp, false); + canvas.addEventListener("keypress", this.onKeyPress, true); canvas.addEventListener("blur", this.onBlur, false); // handle resize events to change the arcball dimensions presenter.contentWindow.addEventListener("resize", this.onResize, false); }, /** * Removes all added events listeners required by this controller. @@ -1069,16 +1070,17 @@ TiltVisualizer.Controller.prototype = { canvas.removeEventListener("mousedown", this.onMouseDown, false); canvas.removeEventListener("mouseup", this.onMouseUp, false); canvas.removeEventListener("mousemove", this.onMouseMove, false); canvas.removeEventListener("mouseover", this.onMouseOver, false); canvas.removeEventListener("mouseout", this.onMouseOut, false); canvas.removeEventListener("MozMousePixelScroll", this.onMozScroll, false); canvas.removeEventListener("keydown", this.onKeyDown, false); canvas.removeEventListener("keyup", this.onKeyUp, false); + canvas.removeEventListener("keypress", this.onKeyPress, true); canvas.removeEventListener("blur", this.onBlur, false); presenter.contentWindow.removeEventListener("resize", this.onResize,false); }, /** * Function called each frame, updating the visualization camera transforms. * @@ -1212,34 +1214,42 @@ TiltVisualizer.Controller.prototype = { }, /** * Called when a key is released. */ onKeyUp: function TVC_onKeyUp(e) { let code = e.keyCode || e.which; - let tilt = this.presenter.chromeWindow.Tilt; - if (code === e.DOM_VK_ESCAPE) { - tilt.destroy(tilt.currentWindowId, true); - return; - } if (code === e.DOM_VK_X) { this.presenter.deleteNode(); } - if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) { e.preventDefault(); e.stopPropagation(); this.arcball.keyUp(code); } }, /** + * Called when a key is pressed. + */ + onKeyPress: function TVC_onKeyPress(e) + { + let tilt = this.presenter.chromeWindow.Tilt; + + if (e.keyCode === e.DOM_VK_ESCAPE) { + e.preventDefault(); + e.stopPropagation(); + tilt.destroy(tilt.currentWindowId, true); + } + }, + + /** * Called when the canvas looses focus. */ onBlur: function TVC_onBlur(e) { this.arcball.cancelKeyEvents(); }, /** * Called when the content window of the current browser is resized.
--- a/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js +++ b/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js @@ -26,12 +26,15 @@ function test() { } function cleanup() { let id = TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow); is(Tilt.visualizers[id], null, "The current instance of the visualizer wasn't destroyed properly."); + ok(InspectorUI.highlighter && InspectorUI.breadcrumbs, + "The Inspector should not close while Tilt is opened."); + Services.obs.removeObserver(cleanup, DESTROYED); gBrowser.removeCurrentTab(); finish(); }
--- a/browser/themes/gnomestripe/browser.css +++ b/browser/themes/gnomestripe/browser.css @@ -2101,16 +2101,21 @@ panel[dimmed="true"] { padding: 0 3px; /* A fake 1px-shadow is included in the border-images of the inspector-breadcrumbs-buttons, to match toolbar-buttons style. This negative margin compensate the extra row of pixels created by the shadow.*/ margin-bottom: -1px; } +#inspector-breadcrumbs > .scrollbutton-up, +#inspector-breadcrumbs > .scrollbutton-down { + -moz-appearance: none; +} + .inspector-breadcrumbs-button { -moz-appearance: none; background-color: transparent; border-style: solid; border-width: 1px 13px 2px 13px; color: hsl(210,30%,85%); max-width: 85px; /* The content of the button can be larger than the button */
--- a/browser/themes/gnomestripe/newtab/newTab.css +++ b/browser/themes/gnomestripe/newtab/newTab.css @@ -73,16 +73,22 @@ } /* GRID */ #grid { padding: 1px; margin: 0 auto; } +/* CELLS */ +.cell { + outline: 1px dashed #ccc; + outline-offset: -1px; +} + /* SITES */ .site { background-color: #ececec; -moz-transition: 200ms ease-out; -moz-transition-property: top, left, box-shadow, opacity; } .site[dragged] {
--- a/browser/themes/pinstripe/newtab/newTab.css +++ b/browser/themes/pinstripe/newtab/newTab.css @@ -73,16 +73,22 @@ } /* GRID */ #grid { padding: 1px; margin: 0 auto; } +/* CELLS */ +.cell { + outline: 1px dashed #ccc; + outline-offset: -1px; +} + /* SITES */ .site { background-color: #ececec; -moz-transition: 200ms ease-out; -moz-transition-property: top, left, box-shadow, opacity; } .site[dragged] {
--- a/browser/themes/winstripe/browser.css +++ b/browser/themes/winstripe/browser.css @@ -2781,16 +2781,21 @@ panel[dimmed="true"] { padding: 0 6px; /* A fake 1px-shadow is included in the border-images of the inspector-breadcrumbs-buttons, to match toolbar-buttons style. This negative margin compensate the extra row of pixels created by the shadow.*/ margin: -1px 0; } +#inspector-breadcrumbs > .scrollbutton-up, +#inspector-breadcrumbs > .scrollbutton-down { + -moz-appearance: none; +} + .inspector-breadcrumbs-button { -moz-appearance: none; background-color: transparent; border-style: solid; border-width: 2px 13px; outline: none; color: hsl(210,30%,85%); max-width: 85px;
--- a/browser/themes/winstripe/newtab/newTab.css +++ b/browser/themes/winstripe/newtab/newTab.css @@ -73,16 +73,22 @@ } /* GRID */ #grid { padding: 1px; margin: 0 auto; } +/* CELLS */ +.cell { + outline: 1px dashed #ccc; + outline-offset: -1px; +} + /* SITES */ .site { background-color: #ececec; -moz-transition: 200ms ease-out; -moz-transition-property: top, left, box-shadow, opacity; } .site[dragged] {
--- a/build/mobile/robocop/Actions.java.in +++ b/build/mobile/robocop/Actions.java.in @@ -55,15 +55,21 @@ public interface Actions { /** * Listens for a gecko event to be sent from the Gecko instance. * The returned object can be used to test if the event has been * received. Note that only one event is listened for. * * @param geckoEvent The geckoEvent JSONObject's type */ EventExpecter expectGeckoEvent(String geckoEvent); + + /** + * Listens for a paint event. + */ + EventExpecter expectPaint(); + // Send the string kewsToSend to the application void sendKeys(String keysToSend); //Send any of the above keys to the element void sendSpecialKey(SpecialKey button); void drag(int startingX, int endingX, int startingY, int endingY); }
--- a/build/mobile/robocop/Assert.java.in +++ b/build/mobile/robocop/Assert.java.in @@ -47,9 +47,12 @@ public interface Assert { void finalize(); void ok(boolean condition, String name, String diag); void is(Object a, Object b, String name); void isnot(Object a, Object b, String name); void todo(boolean condition, String name, String diag); void todo_is(Object a, Object b, String name); void todo_isnot(Object a, Object b, String name); void info(String name, String message); + + // robocop-specific asserts + void ispixel(int actual, int r, int g, int b, String name); }
--- a/build/mobile/robocop/Driver.java.in +++ b/build/mobile/robocop/Driver.java.in @@ -63,9 +63,16 @@ public interface Driver { int getHeight(); int getGeckoTop(); int getGeckoLeft(); int getGeckoWidth(); int getGeckoHeight(); void startFrameRecording(); int stopFrameRecording(); + + /** + * Get a copy of the painted content region. + * @return A 2-D array of pixels (indexed by y, then x). The pixels + * are in ARGB-8888 format. + */ + int[][] getPaintedSurface(); }
--- a/build/mobile/robocop/FennecNativeActions.java.in +++ b/build/mobile/robocop/FennecNativeActions.java.in @@ -68,44 +68,53 @@ import java.util.concurrent.SynchronousQ import org.json.*; import com.jayway.android.robotium.solo.Solo; public class FennecNativeActions implements Actions { // Map of IDs to element names. private Solo solo; private Instrumentation instr; + private Activity geckoApp; // Objects for reflexive access of fennec classes. private ClassLoader classLoader; private Class gel; private Class ge; private Class gas; + private Class drawListener; private Method registerGEL; private Method unregisterGEL; private Method sendGE; - + private Method getLayerClient; + private Method setDrawListener; public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){ this.solo = robocop; this.instr = instrumentation; + this.geckoApp = activity; // Set up reflexive access of java classes and methods. try { classLoader = activity.getClassLoader(); gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener"); ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent"); gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); Class [] parameters = new Class[2]; parameters[0] = String.class; parameters[1] = gel; registerGEL = gas.getMethod("registerGeckoEventListener", parameters); unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters); parameters = new Class[1]; parameters[0] = ge; sendGE = gas.getMethod("sendEventToGecko", parameters); + + getLayerClient = activity.getClass().getMethod("getSoftwareLayerClient"); + Class gslc = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient"); + drawListener = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener"); + setDrawListener = gslc.getDeclaredMethod("setDrawListener", drawListener); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); @@ -200,16 +209,85 @@ public class FennecNativeActions impleme } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } + class DrawListenerProxy implements InvocationHandler { + private final PaintExpecter mPaintExpecter; + + DrawListenerProxy(PaintExpecter paintExpecter) { + mPaintExpecter = paintExpecter; + } + + public Object invoke(Object proxy, Method method, Object[] args) { + String methodName = method.getName(); + if ("drawFinished".equals(methodName)) { + Log.i("Robocop", "Received drawFinished notification"); + mPaintExpecter.notifyOfEvent(); + } else if ("toString".equals(methodName)) { + return "DrawListenerProxy"; + } else if ("equals".equals(methodName)) { + return false; + } else if ("hashCode".equals(methodName)) { + return 0; + } + return null; + } + } + + class PaintExpecter implements EventExpecter { + private Object mLayerClient; + private boolean mPaintDone; + + PaintExpecter() throws IllegalAccessException, InvocationTargetException { + mLayerClient = getLayerClient.invoke(geckoApp); + setDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(classLoader, new Class[] { drawListener }, new DrawListenerProxy(this))); + } + + void notifyOfEvent() { + try { + setDrawListener.invoke(mLayerClient, (Object)null); + } catch (Exception e) { + e.printStackTrace(); + } + synchronized (this) { + mPaintDone = true; + this.notifyAll(); + } + } + + public synchronized void blockForEvent() { + while (! mPaintDone) { + try { + this.wait(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + break; + } + } + } + + public synchronized boolean eventReceived() { + return mPaintDone; + } + } + + public EventExpecter expectPaint() { + try { + return new PaintExpecter(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + public void sendSpecialKey(SpecialKey button) { switch( button) { case DOWN: instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN); break; case UP: instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP); break;
--- a/build/mobile/robocop/FennecNativeAssert.java.in +++ b/build/mobile/robocop/FennecNativeAssert.java.in @@ -238,16 +238,33 @@ public class FennecNativeAssert implemen boolean pass = !a.equals(b); String diag = "didn't expect " + a.toString() + ", but got it"; if(pass) { diag = a.toString() + " should not equal " + b.toString(); } ok(pass, name, diag); } + public void ispixel(int actual, int r, int g, int b, String name) { + // When we read GL pixels the GPU has already processed them and they + // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5 + // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare + // against the expected value, we use a little fuzz factor. For the alpha we just + // make sure it is always 0xFF. + int aAlpha = ((actual >> 24) & 0xFF); + int aR = ((actual >> 16) & 0xFF); + int aG = ((actual >> 8) & 0xFF); + int aB = (actual & 0xFF); + boolean pass = (aAlpha == 0xFF) /* alpha */ + && (Math.abs(aR - r) < 8) /* red */ + && (Math.abs(aG - g) < 8) /* green */ + && (Math.abs(aB - b) < 8); /* blue */ + ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")"); + } + public void todo(boolean condition, String name, String diag) { testInfo test = new testInfo(condition, name, diag, true); _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL"); testList.add(test); } public void todo_is(Object a, Object b, String name) { boolean pass = a.equals(b);
--- a/build/mobile/robocop/FennecNativeDriver.java.in +++ b/build/mobile/robocop/FennecNativeDriver.java.in @@ -40,29 +40,31 @@ package @ANDROID_PACKAGE_NAME@; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.LinkedList; import java.util.HashMap; import java.util.List; import java.lang.Class; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.InvocationHandler; import java.lang.Long; import android.app.Activity; +import android.opengl.GLSurfaceView; import android.util.Log; import android.view.View; import org.json.*; import com.jayway.android.robotium.solo.Solo; public class FennecNativeDriver implements Driver { @@ -76,16 +78,17 @@ public class FennecNativeDriver implemen private Class gel; private Class ge; private Class gas; private Method registerGEL; private Method unregisterGEL; private Method sendGE; private Method _startFrameRecording; private Method _stopFrameRecording; + private Method _getPixels; public FennecNativeDriver(Activity activity, Solo robocop){ this.activity = activity; this.solo = robocop; // Set up table of fennec_ids. locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt")); @@ -102,16 +105,19 @@ public class FennecNativeDriver implemen unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters); parameters = new Class[1]; parameters[0] = ge; sendGE = gas.getMethod("sendEventToGecko", parameters); Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI"); _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording"); _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording"); + + Class layerView = classLoader.loadClass("org.mozilla.gecko.gfx.LayerView"); + _getPixels = layerView.getDeclaredMethod("getPixels"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); @@ -207,16 +213,53 @@ public class FennecNativeDriver implemen e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return 0; } + private GLSurfaceView getSurfaceView() { + for (View v : solo.getCurrentViews()) { + if (v instanceof GLSurfaceView) { + return (GLSurfaceView)v; + } + } + return null; + } + + public int[][] getPaintedSurface() { + GLSurfaceView view = getSurfaceView(); + if (view == null) { + return null; + } + IntBuffer pixelBuffer; + try { + pixelBuffer = (IntBuffer)_getPixels.invoke(view); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + // now we need to (1) flip the image, because GL likes to do things up-side-down, + // and (2) rearrange the bits from AGBR-8888 to ARGB-8888. + int w = view.getWidth(); + int h = view.getHeight(); + pixelBuffer.position(0); + int[][] pixels = new int[h][w]; + for (int y = h - 1; y >= 0; y--) { + for (int x = 0; x < w; x++) { + int agbr = pixelBuffer.get(); + pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000); + } + } + return pixels; + } + class scrollHandler implements InvocationHandler { public scrollHandler(){}; public Object invoke(Object proxy, Method method, Object[] args) { try{ //Disect the JSON object into the appropriate variables JSONObject jo = ((JSONObject)args[1]); scrollHeight = jo.getInt("y"); height = jo.getInt("cheight");
--- a/build/mobile/robocop/Makefile.in +++ b/build/mobile/robocop/Makefile.in @@ -63,16 +63,17 @@ JAVAFILES = \ $(NULL) _JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in)) _TEST_FILES = \ $(TESTPATH)/robocop_blank_01.html \ $(TESTPATH)/robocop_blank_02.html \ $(TESTPATH)/robocop_blank_03.html \ + $(TESTPATH)/robocop_boxes.html \ $(NULL) _ROBOCOP_TOOLS = \ $(TESTPATH)/robocop.ini \ parse_ids.py \ $(NULL) GARBAGE += \
--- a/build/unix/build-toolchain/glibc-deterministic.patch +++ b/build/unix/build-toolchain/glibc-deterministic.patch @@ -17,16 +17,32 @@ diff -ru a/csu/Makefile b/csu/Makefile os=Linux; \ fi; \ - printf '"Compiled on a %s %s system on %s.\\n"\n' \ - "$$os" "$$version" "`date +%Y-%m-%d`";; \ + ;; \ *) ;; \ esac; \ files="$(all-Banner-files)"; \ +diff -ru a/Makerules b/Makerules +--- a/Makerules 2011-01-17 23:34:07.000000000 -0500 ++++ b/Makerules 2012-01-30 08:47:56.565068903 -0500 +@@ -992,9 +992,9 @@ + echo ' Use the shared library, but some functions are only in';\ + echo ' the static library, so try that secondarily. */';\ + cat $<; \ +- echo 'GROUP ( $(slibdir)/libc.so$(libc.so-version)' \ +- '$(libdir)/$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\ +- ' AS_NEEDED (' $(slibdir)/$(rtld-installed-name) ') )' \ ++ echo 'GROUP ( libc.so$(libc.so-version)' \ ++ '$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\ ++ ' AS_NEEDED (' $(rtld-installed-name) ') )' \ + ) > $@.new + mv -f $@.new $@ + diff -ru a/nscd/nscd_stat.c b/nscd/nscd_stat.c --- a/nscd/nscd_stat.c 2011-01-17 23:34:07.000000000 -0500 +++ b/nscd/nscd_stat.c 2012-01-23 15:54:45.231607606 -0500 @@ -38,7 +38,7 @@ /* We use this to make sure the receiver is the same. */ -static const char compilation[21] = __DATE__ " " __TIME__;
--- a/config/android-common.mk +++ b/config/android-common.mk @@ -65,11 +65,11 @@ ifndef JAVA_VERSION endif JAVAC_FLAGS = \ -target $(JAVA_VERSION) \ -source $(JAVA_VERSION) \ -classpath $(JAVA_CLASSPATH) \ -bootclasspath $(JAVA_BOOTCLASSPATH) \ -encoding UTF8 \ - -g \ + -g:source,lines \ -Werror \ $(NULL)
--- a/config/milestone.txt +++ b/config/milestone.txt @@ -5,9 +5,9 @@ # x.x.x.x # x.x.x+ # # Referenced by milestone.pl. # Hopefully I'll be able to automate replacement of *all* # hardcoded milestones in the tree from these two files. #-------------------------------------------------------- -12.0a1 +13.0a1
--- a/configure.in +++ b/configure.in @@ -4901,22 +4901,24 @@ cairo-android) if test "$MOZ_BUILD_APP" = "mobile/xul"; then MOZ_OLD_LINKER=1 fi MOZ_TOUCH=1 ;; cairo-gonk) AC_DEFINE(MOZ_WIDGET_GONK) + AC_DEFINE(MOZ_TOUCH) MOZ_WIDGET_TOOLKIT=gonk TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)' TK_LIBS='$(MOZ_CAIRO_LIBS)' MOZ_WEBGL=1 MOZ_PDF_PRINTING=1 MOZ_B2G_RIL=1 + MOZ_TOUCH=1 ;; esac AC_SUBST(MOZ_OLD_LINKER) AC_SUBST(MOZ_PDF_PRINTING) if test "$MOZ_PDF_PRINTING"; then PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1" @@ -5664,41 +5666,41 @@ if test -n "$MOZ_WEBM"; then [if test ! -f "${LIBVPX_DIR}/include/vpx/vpx_decoder.h"; then AC_MSG_ERROR([vpx/vpx_decoder.h found, but is not in ${LIBVPX_DIR}/include]) fi], AC_MSG_ERROR([--with-system-libvpx requested but vpx/vpx_decoder.h not found])) AC_CHECK_LIB(vpx, vpx_codec_dec_init_ver, [MOZ_NATIVE_LIBVPX_DEC_TEST=1], ([--with-system-libvpx requested but symbol vpx_codec_dec_init_ver not found])) if test -n "$MOZ_NATIVE_LIBVPX_DEC_TEST" ; then - AC_MSG_CHECKING([for libvpx version >= v0.9.7]) - dnl We need at least v0.9.7 to fix several crash bugs (for which we - dnl had local patches prior to v0.9.7). + AC_MSG_CHECKING([for libvpx version >= v1.0.0]) + dnl We need at least v1.0.0 to fix several crash bugs (for which we + dnl had local patches prior to v1.0.0). dnl dnl This is a terrible test for the library version, but we don't dnl have a good one. There is no version number in a public header, dnl and testing the headers still doesn't guarantee we link against dnl the right version. While we could call vpx_codec_version() at dnl run-time, that would break cross-compiling. There are no - dnl additional exported symbols between the v0.9.7 release and the - dnl v0.9.6 one to check for. + dnl additional exported decoder symbols between the v1.0.0 release + dnl and the v0.9.7 one to check for. AC_TRY_COMPILE([ #include <vpx/vpx_decoder.h> - #if !defined(VPX_CODEC_USE_INPUT_PARTITION) + #if !defined(VPX_CODEC_USE_INPUT_FRAGMENTS) #error "test failed." #endif ], [return 0;], [AC_MSG_RESULT([yes]) MOZ_NATIVE_LIBVPX=1 AC_DEFINE(MOZ_NATIVE_LIBVPX) MOZ_LIBVPX_INCLUDES="-I${LIBVPX_DIR}/include" MOZ_LIBVPX_LIBS="-L${LIBVPX_DIR}/lib -lvpx"], [AC_MSG_RESULT([no]) - AC_MSG_ERROR([--with-system-libvpx requested but it is not v0.9.7 or later])]) + AC_MSG_ERROR([--with-system-libvpx requested but it is not v1.0.0 or later])]) fi CFLAGS=$_SAVE_CFLAGS LDFLAGS=$_SAVE_LDFLAGS LIBS=$_SAVE_LIBS fi fi AC_SUBST(MOZ_NATIVE_LIBVPX)
--- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -173,16 +173,58 @@ enum EventNameType { EventNameType_SVGGraphic = 0x0004, // svg graphic elements EventNameType_SVGSVG = 0x0008, // the svg element EventNameType_SMIL = 0x0016, // smil elements EventNameType_HTMLXUL = 0x0003, EventNameType_All = 0xFFFF }; +/** + * Information retrieved from the <meta name="viewport"> tag. See + * GetViewportInfo for more information on this functionality. + */ +struct ViewportInfo +{ + // Default zoom indicates the level at which the display is 'zoomed in' + // initially for the user, upon loading of the page. + double defaultZoom; + + // The minimum zoom level permitted by the page. + double minZoom; + + // The maximum zoom level permitted by the page. + double maxZoom; + + // The width of the viewport, specified by the <meta name="viewport"> tag, + // in CSS pixels. + PRUint32 width; + + // The height of the viewport, specified by the <meta name="viewport"> tag, + // in CSS pixels. + PRUint32 height; + + // Whether or not we should automatically size the viewport to the device's + // width. This is true if the document has been optimized for mobile, and + // the width property of a specified <meta name="viewport"> tag is either + // not specified, or is set to the special value 'device-width'. + bool autoSize; + + // Whether or not the user can zoom in and out on the page. Default is true. + bool allowZoom; + + // This is a holdover from e10s fennec, and might be removed in the future. + // It's a hack to work around bugs that didn't allow zooming of documents + // from within the parent process. It is still used in native Fennec for XUL + // documents, but it should probably be removed. + // Currently, from, within GetViewportInfo(), This is only set to false + // if the document is a XUL document. + bool autoScale; +}; + struct EventNameMapping { nsIAtom* mAtom; PRUint32 mId; PRInt32 mType; PRUint32 mStructType; }; @@ -1484,16 +1526,28 @@ public: * * The only known case where this lies is mutation events. They run, and can * run anything else, when this function returns false, but this is ok. */ static bool IsSafeToRunScript() { return sScriptBlockerCount == 0; } + /** + * Retrieve information about the viewport as a data structure. + * This will return information in the viewport META data section + * of the document. This can be used in lieu of ProcessViewportInfo(), + * which places the viewport information in the document header instead + * of returning it directly. + * + * NOTE: If the site is optimized for mobile (via the doctype), this + * will return viewport information that specifies default information. + */ + static ViewportInfo GetViewportInfo(nsIDocument* aDocument); + /* Process viewport META data. This gives us information for the scale * and zoom of a page on mobile devices. We stick the information in * the document header and use it later on after rendering. * * See Bug #436083 */ static nsresult ProcessViewportInfo(nsIDocument *aDocument, const nsAString &viewportInfo);
--- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -73,18 +73,18 @@ enum nsLinkState { eLinkState_Unknown = 0, eLinkState_Unvisited = 1, eLinkState_Visited = 2, eLinkState_NotLink = 3 }; // IID for the nsIContent interface #define NS_ICONTENT_IID \ -{ 0xdc68f070, 0x226d, 0x11e1, \ - { 0xbf, 0xc2, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } } +{ 0x94671671, 0x9e1b, 0x447a, \ + { 0xad, 0xb7, 0xc3, 0x2e, 0x05, 0x6a, 0x96, 0xc9 } } /** * A node of content in a document's content model. This interface * is supported by all content objects. */ class nsIContent : public nsINode { public: typedef mozilla::widget::IMEState IMEState; @@ -943,16 +943,19 @@ public: } } // Overloaded from nsINode virtual already_AddRefed<nsIURI> GetBaseURI() const; virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor); + virtual bool IsPurple() = 0; + virtual void RemovePurple() = 0; + protected: /** * Hook for implementing GetID. This is guaranteed to only be * called if HasID() is true. */ virtual nsIAtom* DoGetID() const = 0; private:
--- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -283,18 +283,18 @@ private: // Categories of node properties // 0 is global. #define DOM_USER_DATA 1 #define DOM_USER_DATA_HANDLER 2 #define SMIL_MAPPED_ATTR_ANIMVAL 3 // IID for the nsINode interface #define NS_INODE_IID \ -{ 0xd026d280, 0x5b25, 0x41c0, \ - { 0x92, 0xcf, 0x6, 0xf6, 0xf, 0xb, 0x9a, 0xfe } } +{ 0xfcd3b0d1, 0x75db, 0x46c4, \ + { 0xa1, 0xf5, 0x07, 0xc2, 0x09, 0xf8, 0x1f, 0x44 } } /** * An internal interface that abstracts some DOMNode-related parts that both * nsIContent and nsIDocument share. An instance of this interface has a list * of nsIContent children and provides access to them. */ class nsINode : public nsIDOMEventTarget, public nsWrapperCache @@ -1218,16 +1218,23 @@ private: ElementHasName, // Set if the element might have a contenteditable attribute set. ElementMayHaveContentEditableAttr, // Set if the node is the common ancestor of the start/end nodes of a Range // that is in a Selection. NodeIsCommonAncestorForRangeInSelection, // Set if the node is a descendant of a node with the above bit set. NodeIsDescendantOfCommonAncestorForRangeInSelection, + // Set if CanSkipInCC check has been done for this subtree root. + NodeIsCCMarkedRoot, + // Maybe set if this node is in black subtree. + NodeIsCCBlackTree, + // Maybe set if the node is a root of a subtree + // which needs to be kept in the purple buffer. + NodeIsPurpleRoot, // Guard value BooleanFlagCount }; void SetBoolFlag(BooleanFlag name, bool value) { PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags)); mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name); } @@ -1265,16 +1272,26 @@ public: { ClearBoolFlag(NodeIsCommonAncestorForRangeInSelection); } bool IsDescendantOfCommonAncestorForRangeInSelection() const { return GetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); } void SetDescendantOfCommonAncestorForRangeInSelection() { SetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); } void ClearDescendantOfCommonAncestorForRangeInSelection() { ClearBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); } + void SetCCMarkedRoot(bool aValue) + { SetBoolFlag(NodeIsCCMarkedRoot, aValue); } + bool CCMarkedRoot() const { return GetBoolFlag(NodeIsCCMarkedRoot); } + void SetInCCBlackTree(bool aValue) + { SetBoolFlag(NodeIsCCBlackTree, aValue); } + bool InCCBlackTree() const { return GetBoolFlag(NodeIsCCBlackTree); } + void SetIsPurpleRoot(bool aValue) + { SetBoolFlag(NodeIsPurpleRoot, aValue); } + bool IsPurpleRoot() const { return GetBoolFlag(NodeIsPurpleRoot); } + protected: void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); } void SetInDocument() { SetBoolFlag(IsInDocument); } void ClearInDocument() { ClearBoolFlag(IsInDocument); } void SetIsElement() { SetBoolFlag(NodeIsElement); } void ClearIsElement() { ClearBoolFlag(NodeIsElement); } void SetHasID() { SetBoolFlag(ElementHasID); } void ClearHasID() { ClearBoolFlag(ElementHasID); }
--- a/content/base/src/nsCCUncollectableMarker.cpp +++ b/content/base/src/nsCCUncollectableMarker.cpp @@ -49,16 +49,23 @@ #include "nsISHistory.h" #include "nsISHEntry.h" #include "nsISHContainer.h" #include "nsIWindowWatcher.h" #include "mozilla/Services.h" #include "nsIXULWindow.h" #include "nsIAppShellService.h" #include "nsAppShellCID.h" +#include "nsEventListenerManager.h" +#include "nsContentUtils.h" +#include "nsGlobalWindow.h" +#include "nsJSEnvironment.h" +#include "nsInProcessTabChildGlobal.h" +#include "nsFrameLoader.h" +#include "nsGenericElement.h" static bool sInited = 0; PRUint32 nsCCUncollectableMarker::sGeneration = 0; #ifdef MOZ_XUL #include "nsXULPrototypeCache.h" #endif NS_IMPL_ISUPPORTS1(nsCCUncollectableMarker, nsIObserver) @@ -82,114 +89,208 @@ nsCCUncollectableMarker::Init() nsresult rv; // This makes the observer service hold an owning reference to the marker rv = obs->AddObserver(marker, "xpcom-shutdown", false); NS_ENSURE_SUCCESS(rv, rv); rv = obs->AddObserver(marker, "cycle-collector-begin", false); NS_ENSURE_SUCCESS(rv, rv); + rv = obs->AddObserver(marker, "cycle-collector-forget-skippable", false); + NS_ENSURE_SUCCESS(rv, rv); sInited = true; return NS_OK; } +static void +MarkUserData(void* aNode, nsIAtom* aKey, void* aValue, void* aData) +{ + nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc(); + if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) { + nsGenericElement::MarkUserData(aNode, aKey, aValue, aData); + } +} + +static void +MarkUserDataHandler(void* aNode, nsIAtom* aKey, void* aValue, void* aData) +{ + nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc(); + if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) { + nsGenericElement::MarkUserDataHandler(aNode, aKey, aValue, aData); + } +} + +static void +MarkMessageManagers() +{ + nsCOMPtr<nsIChromeFrameMessageManager> globalMM = + do_GetService("@mozilla.org/globalmessagemanager;1"); + if (!globalMM) { + return; + } + + globalMM->MarkForCC(); + PRUint32 childCount = 0; + globalMM->GetChildCount(&childCount); + for (PRUint32 i = 0; i < childCount; ++i) { + nsCOMPtr<nsITreeItemFrameMessageManager> windowMM; + globalMM->GetChildAt(i, getter_AddRefs(windowMM)); + if (!windowMM) { + continue; + } + windowMM->MarkForCC(); + PRUint32 tabChildCount = 0; + windowMM->GetChildCount(&tabChildCount); + for (PRUint32 j = 0; j < tabChildCount; ++j) { + nsCOMPtr<nsITreeItemFrameMessageManager> tabMM; + windowMM->GetChildAt(j, getter_AddRefs(tabMM)); + if (!tabMM) { + continue; + } + tabMM->MarkForCC(); + //XXX hack warning, but works, since we know that + // callback data is frameloader. + void* cb = static_cast<nsFrameMessageManager*>(tabMM.get())-> + GetCallbackData(); + nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb); + if (fl) { + nsIDOMEventTarget* et = fl->GetTabChildGlobalAsEventTarget(); + if (!et) { + continue; + } + static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC(); + nsEventListenerManager* elm = et->GetListenerManager(false); + if (elm) { + elm->UnmarkGrayJSListeners(); + } + } + } + } +} + void -MarkContentViewer(nsIContentViewer* aViewer) +MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS, + bool aPrepareForCC) { if (!aViewer) { return; } nsIDocument *doc = aViewer->GetDocument(); - if (doc) { + if (doc && + doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) { doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); + if (aCleanupJS) { + nsEventListenerManager* elm = doc->GetListenerManager(false); + if (elm) { + elm->UnmarkGrayJSListeners(); + } + nsCOMPtr<nsIDOMEventTarget> win = do_QueryInterface(doc->GetInnerWindow()); + if (win) { + elm = win->GetListenerManager(false); + if (elm) { + elm->UnmarkGrayJSListeners(); + } + static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers(); + } + + doc->PropertyTable(DOM_USER_DATA_HANDLER)-> + EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration); + } else if (aPrepareForCC) { + // Unfortunately we need to still mark user data just before running CC so + // that it has the right generation. + doc->PropertyTable(DOM_USER_DATA)-> + EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration); + } } } -void MarkDocShell(nsIDocShellTreeNode* aNode); +void MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS, + bool aPrepareForCC); void -MarkSHEntry(nsISHEntry* aSHEntry) +MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC) { if (!aSHEntry) { return; } nsCOMPtr<nsIContentViewer> cview; aSHEntry->GetContentViewer(getter_AddRefs(cview)); - MarkContentViewer(cview); + MarkContentViewer(cview, aCleanupJS, aPrepareForCC); nsCOMPtr<nsIDocShellTreeItem> child; PRInt32 i = 0; while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) && child) { - MarkDocShell(child); + MarkDocShell(child, aCleanupJS, aPrepareForCC); } nsCOMPtr<nsISHContainer> shCont = do_QueryInterface(aSHEntry); PRInt32 count; shCont->GetChildCount(&count); for (i = 0; i < count; ++i) { nsCOMPtr<nsISHEntry> childEntry; shCont->GetChildAt(i, getter_AddRefs(childEntry)); - MarkSHEntry(childEntry); + MarkSHEntry(childEntry, aCleanupJS, aPrepareForCC); } } void -MarkDocShell(nsIDocShellTreeNode* aNode) +MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS, bool aPrepareForCC) { nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aNode); if (!shell) { return; } nsCOMPtr<nsIContentViewer> cview; shell->GetContentViewer(getter_AddRefs(cview)); - MarkContentViewer(cview); + MarkContentViewer(cview, aCleanupJS, aPrepareForCC); nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell); nsCOMPtr<nsISHistory> history; webNav->GetSessionHistory(getter_AddRefs(history)); if (history) { PRInt32 i, historyCount; history->GetCount(&historyCount); for (i = 0; i < historyCount; ++i) { nsCOMPtr<nsIHistoryEntry> historyEntry; history->GetEntryAtIndex(i, false, getter_AddRefs(historyEntry)); nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(historyEntry); - MarkSHEntry(shEntry); + MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC); } } PRInt32 i, childCount; aNode->GetChildCount(&childCount); for (i = 0; i < childCount; ++i) { nsCOMPtr<nsIDocShellTreeItem> child; aNode->GetChildAt(i, getter_AddRefs(child)); - MarkDocShell(child); + MarkDocShell(child, aCleanupJS, aPrepareForCC); } } void -MarkWindowList(nsISimpleEnumerator* aWindowList) +MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS, + bool aPrepareForCC) { nsCOMPtr<nsISupports> iter; while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) && iter) { nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(iter); if (window) { nsCOMPtr<nsIDocShellTreeNode> rootDocShell = do_QueryInterface(window->GetDocShell()); - MarkDocShell(rootDocShell); + MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC); } } } nsresult nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { @@ -197,64 +298,79 @@ nsCCUncollectableMarker::Observe(nsISupp nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (!obs) return NS_ERROR_FAILURE; // No need for kungFuDeathGrip here, yay observerservice! obs->RemoveObserver(this, "xpcom-shutdown"); obs->RemoveObserver(this, "cycle-collector-begin"); + obs->RemoveObserver(this, "cycle-collector-forget-skippable"); sGeneration = 0; return NS_OK; } - NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin"), "wrong topic"); + NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") || + !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic"); + + // JS cleanup can be slow. Do it only if there has been a GC. + bool cleanupJS = + !nsJSContext::CleanupSinceLastGC() && + !strcmp(aTopic, "cycle-collector-forget-skippable"); + + bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin"); + // Increase generation to effectivly unmark all current objects if (!++sGeneration) { ++sGeneration; } nsresult rv; // Iterate all toplevel windows nsCOMPtr<nsISimpleEnumerator> windowList; nsCOMPtr<nsIWindowMediator> med = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); if (med) { rv = med->GetEnumerator(nsnull, getter_AddRefs(windowList)); NS_ENSURE_SUCCESS(rv, rv); - MarkWindowList(windowList); + MarkWindowList(windowList, cleanupJS, prepareForCC); } nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID); if (ww) { rv = ww->GetWindowEnumerator(getter_AddRefs(windowList)); NS_ENSURE_SUCCESS(rv, rv); - MarkWindowList(windowList); + MarkWindowList(windowList, cleanupJS, prepareForCC); } nsCOMPtr<nsIAppShellService> appShell = do_GetService(NS_APPSHELLSERVICE_CONTRACTID); if (appShell) { nsCOMPtr<nsIXULWindow> hw; appShell->GetHiddenWindow(getter_AddRefs(hw)); if (hw) { nsCOMPtr<nsIDocShell> shell; hw->GetDocShell(getter_AddRefs(shell)); nsCOMPtr<nsIDocShellTreeNode> shellTreeNode = do_QueryInterface(shell); - MarkDocShell(shellTreeNode); + MarkDocShell(shellTreeNode, cleanupJS, prepareForCC); } } + if (cleanupJS) { + nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration); + MarkMessageManagers(); + } + #ifdef MOZ_XUL nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance(); if (xulCache) { xulCache->MarkInCCGeneration(sGeneration); } #endif return NS_OK;
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -206,26 +206,39 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_ #include "nsIScriptElement.h" #include "nsIContentViewer.h" #include "nsIObjectLoadingContent.h" #include "nsCCUncollectableMarker.h" #include "mozilla/Base64.h" #include "mozilla/Preferences.h" #include "nsWrapperCacheInlines.h" +#include "nsIDOMDocumentType.h" +#include "nsIDOMWindowUtils.h" #include "nsCharSeparatedTokenizer.h" #include "nsUnicharUtils.h" using namespace mozilla::dom; using namespace mozilla::layers; using namespace mozilla::widget; using namespace mozilla; const char kLoadAsData[] = "loadAsData"; +/** + * Default values for the ViewportInfo structure. + */ +static const float kViewportMinScale = 0.0; +static const float kViewportMaxScale = 10.0; +static const PRUint32 kViewportMinWidth = 200; +static const PRUint32 kViewportMaxWidth = 10000; +static const PRUint32 kViewportMinHeight = 223; +static const PRUint32 kViewportMaxHeight = 10000; +static const PRInt32 kViewportDefaultScreenWidth = 980; + static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1"; static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID); static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID); nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull; nsIXPConnect *nsContentUtils::sXPConnect; nsIScriptSecurityManager *nsContentUtils::sSecurityManager; nsIThreadJSContextStack *nsContentUtils::sThreadJSContextStack; @@ -416,16 +429,18 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sFullScreenKeyInputRestricted, "full-screen-api.key-input-restricted"); Preferences::AddUintVarCache(&sHandlingInputTimeout, "dom.event.handling-user-input-time-limit", 1000); + nsGenericElement::InitCCCallbacks(); + sInitialized = true; return NS_OK; } void nsContentUtils::GetShiftText(nsAString& text) { @@ -4550,16 +4565,208 @@ static void ProcessViewportToken(nsIDocu else if (key_atom == nsGkAtoms::user_scalable) aDocument->SetHeaderData(nsGkAtoms::viewport_user_scalable, value); } #define IS_SEPARATOR(c) ((c == '=') || (c == ',') || (c == ';') || \ (c == '\t') || (c == '\n') || (c == '\r')) /* static */ +ViewportInfo +nsContentUtils::GetViewportInfo(nsIDocument *aDocument) +{ + ViewportInfo ret; + ret.defaultZoom = 1.0; + ret.autoSize = true; + ret.allowZoom = true; + ret.autoScale = true; + + // If the docType specifies that we are on a site optimized for mobile, + // then we want to return specially crafted defaults for the viewport info. + nsCOMPtr<nsIDOMDocument> + domDoc(do_QueryInterface(aDocument)); + + nsCOMPtr<nsIDOMDocumentType> docType; + nsresult rv = domDoc->GetDoctype(getter_AddRefs(docType)); + if (NS_SUCCEEDED(rv) && docType) { + nsAutoString docId; + rv = docType->GetPublicId(docId); + if (NS_SUCCEEDED(rv)) { + if ((docId.Find("WAP") != -1) || + (docId.Find("Mobile") != -1) || + (docId.Find("WML") != -1)) + { + return ret; + } + } + } + + if (aDocument->IsXUL()) { + ret.autoScale = false; + return ret; + } + + nsIDOMWindow* window = aDocument->GetWindow(); + nsCOMPtr<nsIDOMWindowUtils> windowUtils(do_GetInterface(window)); + + if (!windowUtils) { + return ret; + } + + nsAutoString handheldFriendly; + aDocument->GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly); + + if (handheldFriendly.EqualsLiteral("true")) { + return ret; + } + + PRInt32 errorCode; + + nsAutoString minScaleStr; + aDocument->GetHeaderData(nsGkAtoms::minimum_scale, minScaleStr); + + float scaleMinFloat = minScaleStr.ToFloat(&errorCode); + + if (errorCode) { + scaleMinFloat = kViewportMinScale; + } + + scaleMinFloat = NS_MIN(scaleMinFloat, kViewportMaxScale); + scaleMinFloat = NS_MAX(scaleMinFloat, kViewportMinScale); + + nsAutoString maxScaleStr; + aDocument->GetHeaderData(nsGkAtoms::maximum_scale, maxScaleStr); + + // We define a special error code variable for the scale and max scale, + // because they are used later (see the width calculations). + PRInt32 scaleMaxErrorCode; + float scaleMaxFloat = maxScaleStr.ToFloat(&scaleMaxErrorCode); + + if (scaleMaxErrorCode) { + scaleMaxFloat = kViewportMaxScale; + } + + scaleMaxFloat = NS_MIN(scaleMaxFloat, kViewportMaxScale); + scaleMaxFloat = NS_MAX(scaleMaxFloat, kViewportMinScale); + + nsAutoString scaleStr; + aDocument->GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr); + + PRInt32 scaleErrorCode; + float scaleFloat = scaleStr.ToFloat(&scaleErrorCode); + scaleFloat = NS_MIN(scaleFloat, scaleMaxFloat); + scaleFloat = NS_MAX(scaleFloat, scaleMinFloat); + + nsAutoString widthStr, heightStr; + + aDocument->GetHeaderData(nsGkAtoms::viewport_height, heightStr); + aDocument->GetHeaderData(nsGkAtoms::viewport_width, widthStr); + + bool autoSize = false; + + if (widthStr.EqualsLiteral("device-width")) { + autoSize = true; + } + + if (widthStr.IsEmpty() && + (heightStr.EqualsLiteral("device-height") || + scaleFloat == 1.0)) + { + autoSize = true; + } + + // XXXjwir3: + // See bug 706918, comment 23 for more information on this particular section + // of the code. We're using "screen size" in place of the size of the content + // area, because on mobile, these are close or equal. This will work for our + // purposes (bug 706198), but it will need to be changed in the future to be + // more correct when we bring the rest of the viewport code into platform. + // We actually want the size of the content area, in the event that we don't + // have any metadata about the width and/or height. On mobile, the screen size + // and the size of the content area are very close, or the same value. + // In XUL fennec, the content area is the size of the <browser> widget, but + // in native fennec, the content area is the size of the Gecko LayerView + // object. + + // TODO: + // Once bug 716575 has been resolved, this code should be changed so that it + // does the right thing on all platforms. + nsresult result; + PRInt32 screenLeft, screenTop, screenWidth, screenHeight; + nsCOMPtr<nsIScreenManager> screenMgr = + do_GetService("@mozilla.org/gfx/screenmanager;1", &result); + + nsCOMPtr<nsIScreen> screen; + screenMgr->GetPrimaryScreen(getter_AddRefs(screen)); + screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight); + + PRUint32 width = widthStr.ToInteger(&errorCode); + if (errorCode) { + if (autoSize) { + width = screenWidth; + } else { + width = Preferences::GetInt("browser.viewport.desktopWidth", 0); + } + } + + width = NS_MIN(width, kViewportMaxWidth); + width = NS_MAX(width, kViewportMinWidth); + + // Also recalculate the default zoom, if it wasn't specified in the metadata, + // and the width is specified. + if (scaleStr.IsEmpty() && !widthStr.IsEmpty()) { + scaleFloat = NS_MAX(scaleFloat, (float)(screenWidth/width)); + } + + PRUint32 height = heightStr.ToInteger(&errorCode); + + if (errorCode) { + height = width * ((float)screenHeight / screenWidth); + } + + // If height was provided by the user, but width wasn't, then we should + // calculate the width. + if (widthStr.IsEmpty() && !heightStr.IsEmpty()) { + width = (PRUint32) ((height * screenWidth) / screenHeight); + } + + height = NS_MIN(height, kViewportMaxHeight); + height = NS_MAX(height, kViewportMinHeight); + + // We need to perform a conversion, but only if the initial or maximum + // scale were set explicitly by the user. + if (!scaleStr.IsEmpty() && !scaleErrorCode) { + width = NS_MAX(width, (PRUint32)(screenWidth / scaleFloat)); + height = NS_MAX(height, (PRUint32)(screenHeight / scaleFloat)); + } else if (!maxScaleStr.IsEmpty() && !scaleMaxErrorCode) { + width = NS_MAX(width, (PRUint32)(screenWidth / scaleMaxFloat)); + height = NS_MAX(height, (PRUint32)(screenHeight / scaleMaxFloat)); + } + + bool allowZoom = true; + nsAutoString userScalable; + aDocument->GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable); + + if ((userScalable.EqualsLiteral("0")) || + (userScalable.EqualsLiteral("no")) || + (userScalable.EqualsLiteral("false"))) { + allowZoom = false; + } + + ret.allowZoom = allowZoom; + ret.width = width; + ret.height = height; + ret.defaultZoom = scaleFloat; + ret.minZoom = scaleMinFloat; + ret.maxZoom = scaleMaxFloat; + ret.autoSize = autoSize; + return ret; +} + +/* static */ nsresult nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument, const nsAString &viewportInfo) { /* We never fail. */ nsresult rv = NS_OK; /* Iterators. */
--- a/content/base/src/nsDOMBlobBuilder.cpp +++ b/content/base/src/nsDOMBlobBuilder.cpp @@ -30,61 +30,28 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ +#include "nsDOMBlobBuilder.h" #include "jstypedarray.h" #include "nsAutoPtr.h" #include "nsDOMClassInfoID.h" -#include "nsDOMFile.h" #include "nsIMultiplexInputStream.h" #include "nsStringStream.h" #include "nsTArray.h" #include "nsJSUtils.h" #include "nsContentUtils.h" -#include "CheckedInt.h" - -#include "mozilla/StdInt.h" using namespace mozilla; -class nsDOMMultipartFile : public nsDOMFileBase -{ -public: - // Create as a file - nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs, - const nsAString& aName, - const nsAString& aContentType) - : nsDOMFileBase(aName, aContentType, UINT64_MAX), - mBlobs(aBlobs) - { - } - - // Create as a blob - nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs, - const nsAString& aContentType) - : nsDOMFileBase(aContentType, UINT64_MAX), - mBlobs(aBlobs) - { - } - - already_AddRefed<nsIDOMBlob> - CreateSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType); - - NS_IMETHOD GetSize(PRUint64*); - NS_IMETHOD GetInternalStream(nsIInputStream**); - -protected: - nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs; -}; - NS_IMETHODIMP nsDOMMultipartFile::GetSize(PRUint64* aLength) { if (mLength == UINT64_MAX) { CheckedUint64 length = 0; PRUint32 i; PRUint32 len = mBlobs.Length(); @@ -194,89 +161,28 @@ nsDOMMultipartFile::CreateSlice(PRUint64 length -= NS_MIN<PRUint64>(l, length); } // we can create our blob now nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(blobs, aContentType); return blob.forget(); } -class nsDOMBlobBuilder : public nsIDOMMozBlobBuilder -{ -public: - nsDOMBlobBuilder() - : mData(nsnull), mDataLen(0), mDataBufferLen(0) - {} - - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMMOZBLOBBUILDER -protected: - nsresult AppendVoidPtr(void* aData, PRUint32 aLength); - nsresult AppendString(JSString* aString, JSContext* aCx); - nsresult AppendBlob(nsIDOMBlob* aBlob); - nsresult AppendArrayBuffer(JSObject* aBuffer); - - bool ExpandBufferSize(PRUint64 aSize) - { - if (mDataBufferLen >= mDataLen + aSize) { - mDataLen += aSize; - return true; - } - - // Start at 1 or we'll loop forever. - CheckedUint32 bufferLen = NS_MAX<PRUint32>(mDataBufferLen, 1); - while (bufferLen.valid() && bufferLen.value() < mDataLen + aSize) - bufferLen *= 2; - - if (!bufferLen.valid()) - return false; - - // PR_ memory functions are still fallible - void* data = PR_Realloc(mData, bufferLen.value()); - if (!data) - return false; - - mData = data; - mDataBufferLen = bufferLen.value(); - mDataLen += aSize; - return true; - } - - void Flush() { - if (mData) { - // If we have some data, create a blob for it - // and put it on the stack - - nsCOMPtr<nsIDOMBlob> blob = - new nsDOMMemoryFile(mData, mDataLen, EmptyString(), EmptyString()); - mBlobs.AppendElement(blob); - mData = nsnull; // The nsDOMMemoryFile takes ownership of the buffer - mDataLen = 0; - mDataBufferLen = 0; - } - } - - nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs; - void* mData; - PRUint64 mDataLen; - PRUint64 mDataBufferLen; -}; - DOMCI_DATA(MozBlobBuilder, nsDOMBlobBuilder) NS_IMPL_ADDREF(nsDOMBlobBuilder) NS_IMPL_RELEASE(nsDOMBlobBuilder) NS_INTERFACE_MAP_BEGIN(nsDOMBlobBuilder) NS_INTERFACE_MAP_ENTRY(nsIDOMMozBlobBuilder) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozBlobBuilder) NS_INTERFACE_MAP_END nsresult -nsDOMBlobBuilder::AppendVoidPtr(void* aData, PRUint32 aLength) +nsDOMBlobBuilder::AppendVoidPtr(const void* aData, PRUint32 aLength) { NS_ENSURE_ARG_POINTER(aData); PRUint64 offset = mDataLen; if (!ExpandBufferSize(aLength)) return NS_ERROR_OUT_OF_MEMORY; @@ -315,29 +221,39 @@ nsDOMBlobBuilder::AppendArrayBuffer(JSOb return AppendVoidPtr(JS_GetArrayBufferData(aBuffer), JS_GetArrayBufferByteLength(aBuffer)); } /* nsIDOMBlob getBlob ([optional] in DOMString contentType); */ NS_IMETHODIMP nsDOMBlobBuilder::GetBlob(const nsAString& aContentType, nsIDOMBlob** aBlob) { + return GetBlobInternal(aContentType, true, aBlob); +} + +nsresult +nsDOMBlobBuilder::GetBlobInternal(const nsAString& aContentType, + bool aClearBuffer, + nsIDOMBlob** aBlob) +{ NS_ENSURE_ARG(aBlob); Flush(); nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(mBlobs, aContentType); blob.forget(aBlob); // NB: This is a willful violation of the spec. The spec says that // the existing contents of the BlobBuilder should be included // in the next blob produced. This seems silly and has been raised // on the WHATWG listserv. - mBlobs.Clear(); + if (aClearBuffer) { + mBlobs.Clear(); + } return NS_OK; } /* nsIDOMBlob getFile (in DOMString name, [optional] in DOMString contentType); */ NS_IMETHODIMP nsDOMBlobBuilder::GetFile(const nsAString& aName, const nsAString& aContentType,
copy from content/base/src/nsDOMBlobBuilder.cpp copy to content/base/src/nsDOMBlobBuilder.h --- a/content/base/src/nsDOMBlobBuilder.cpp +++ b/content/base/src/nsDOMBlobBuilder.h @@ -30,25 +30,20 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -#include "jstypedarray.h" -#include "nsAutoPtr.h" -#include "nsDOMClassInfoID.h" +#ifndef nsDOMBlobBuilder_h +#define nsDOMBlobBuilder_h + #include "nsDOMFile.h" -#include "nsIMultiplexInputStream.h" -#include "nsStringStream.h" -#include "nsTArray.h" -#include "nsJSUtils.h" -#include "nsContentUtils.h" #include "CheckedInt.h" #include "mozilla/StdInt.h" using namespace mozilla; class nsDOMMultipartFile : public nsDOMFileBase { @@ -75,146 +70,31 @@ public: NS_IMETHOD GetSize(PRUint64*); NS_IMETHOD GetInternalStream(nsIInputStream**); protected: nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs; }; -NS_IMETHODIMP -nsDOMMultipartFile::GetSize(PRUint64* aLength) -{ - if (mLength == UINT64_MAX) { - CheckedUint64 length = 0; - - PRUint32 i; - PRUint32 len = mBlobs.Length(); - for (i = 0; i < len; i++) { - nsIDOMBlob* blob = mBlobs.ElementAt(i).get(); - PRUint64 l = 0; - - nsresult rv = blob->GetSize(&l); - NS_ENSURE_SUCCESS(rv, rv); - - length += l; - } - - NS_ENSURE_TRUE(length.valid(), NS_ERROR_FAILURE); - - mLength = length.value(); - } - - *aLength = mLength; - return NS_OK; -} - -NS_IMETHODIMP -nsDOMMultipartFile::GetInternalStream(nsIInputStream** aStream) -{ - nsresult rv; - *aStream = nsnull; - - nsCOMPtr<nsIMultiplexInputStream> stream = - do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); - NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE); - - PRUint32 i; - for (i = 0; i < mBlobs.Length(); i++) { - nsCOMPtr<nsIInputStream> scratchStream; - nsIDOMBlob* blob = mBlobs.ElementAt(i).get(); - - rv = blob->GetInternalStream(getter_AddRefs(scratchStream)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stream->AppendStream(scratchStream); - NS_ENSURE_SUCCESS(rv, rv); - } - - return CallQueryInterface(stream, aStream); -} - -already_AddRefed<nsIDOMBlob> -nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength, - const nsAString& aContentType) -{ - // If we clamped to nothing we create an empty blob - nsTArray<nsCOMPtr<nsIDOMBlob> > blobs; - - PRUint64 length = aLength; - PRUint64 skipStart = aStart; - - // Prune the list of blobs if we can - PRUint32 i; - for (i = 0; length && skipStart && i < mBlobs.Length(); i++) { - nsIDOMBlob* blob = mBlobs[i].get(); - - PRUint64 l; - nsresult rv = blob->GetSize(&l); - NS_ENSURE_SUCCESS(rv, nsnull); - - if (skipStart < l) { - PRUint64 upperBound = NS_MIN<PRUint64>(l - skipStart, length); - - nsCOMPtr<nsIDOMBlob> firstBlob; - rv = blob->MozSlice(skipStart, skipStart + upperBound, - aContentType, 3, - getter_AddRefs(firstBlob)); - NS_ENSURE_SUCCESS(rv, nsnull); - - // Avoid wrapping a single blob inside an nsDOMMultipartFile - if (length == upperBound) { - return firstBlob.forget(); - } - - blobs.AppendElement(firstBlob); - length -= upperBound; - i++; - break; - } - skipStart -= l; - } - - // Now append enough blobs until we're done - for (; length && i < mBlobs.Length(); i++) { - nsIDOMBlob* blob = mBlobs[i].get(); - - PRUint64 l; - nsresult rv = blob->GetSize(&l); - NS_ENSURE_SUCCESS(rv, nsnull); - - if (length < l) { - nsCOMPtr<nsIDOMBlob> lastBlob; - rv = blob->MozSlice(0, length, aContentType, 3, - getter_AddRefs(lastBlob)); - NS_ENSURE_SUCCESS(rv, nsnull); - - blobs.AppendElement(lastBlob); - } else { - blobs.AppendElement(blob); - } - length -= NS_MIN<PRUint64>(l, length); - } - - // we can create our blob now - nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(blobs, aContentType); - return blob.forget(); -} - class nsDOMBlobBuilder : public nsIDOMMozBlobBuilder { public: nsDOMBlobBuilder() : mData(nsnull), mDataLen(0), mDataBufferLen(0) {} NS_DECL_ISUPPORTS NS_DECL_NSIDOMMOZBLOBBUILDER + + nsresult GetBlobInternal(const nsAString& aContentType, + bool aClearBuffer, nsIDOMBlob** aBlob); + nsresult AppendVoidPtr(const void* aData, PRUint32 aLength); + protected: - nsresult AppendVoidPtr(void* aData, PRUint32 aLength); nsresult AppendString(JSString* aString, JSContext* aCx); nsresult AppendBlob(nsIDOMBlob* aBlob); nsresult AppendArrayBuffer(JSObject* aBuffer); bool ExpandBufferSize(PRUint64 aSize) { if (mDataBufferLen >= mDataLen + aSize) { mDataLen += aSize; @@ -255,150 +135,9 @@ protected: } nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs; void* mData; PRUint64 mDataLen; PRUint64 mDataBufferLen; }; -DOMCI_DATA(MozBlobBuilder, nsDOMBlobBuilder) - -NS_IMPL_ADDREF(nsDOMBlobBuilder) -NS_IMPL_RELEASE(nsDOMBlobBuilder) -NS_INTERFACE_MAP_BEGIN(nsDOMBlobBuilder) - NS_INTERFACE_MAP_ENTRY(nsIDOMMozBlobBuilder) - NS_INTERFACE_MAP_ENTRY(nsISupports) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozBlobBuilder) -NS_INTERFACE_MAP_END - -nsresult -nsDOMBlobBuilder::AppendVoidPtr(void* aData, PRUint32 aLength) -{ - NS_ENSURE_ARG_POINTER(aData); - - PRUint64 offset = mDataLen; - - if (!ExpandBufferSize(aLength)) - return NS_ERROR_OUT_OF_MEMORY; - - memcpy((char*)mData + offset, aData, aLength); - return NS_OK; -} - -nsresult -nsDOMBlobBuilder::AppendString(JSString* aString, JSContext* aCx) -{ - nsDependentJSString xpcomStr; - if (!xpcomStr.init(aCx, aString)) { - return NS_ERROR_XPC_BAD_CONVERT_JS; - } - - NS_ConvertUTF16toUTF8 utf8Str(xpcomStr); - - return AppendVoidPtr((void*)utf8Str.Data(), - utf8Str.Length()); -} - -nsresult -nsDOMBlobBuilder::AppendBlob(nsIDOMBlob* aBlob) -{ - NS_ENSURE_ARG_POINTER(aBlob); - - Flush(); - mBlobs.AppendElement(aBlob); - - return NS_OK; -} - -nsresult -nsDOMBlobBuilder::AppendArrayBuffer(JSObject* aBuffer) -{ - return AppendVoidPtr(JS_GetArrayBufferData(aBuffer), JS_GetArrayBufferByteLength(aBuffer)); -} - -/* nsIDOMBlob getBlob ([optional] in DOMString contentType); */ -NS_IMETHODIMP -nsDOMBlobBuilder::GetBlob(const nsAString& aContentType, - nsIDOMBlob** aBlob) -{ - NS_ENSURE_ARG(aBlob); - - Flush(); - - nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(mBlobs, - aContentType); - blob.forget(aBlob); - - // NB: This is a willful violation of the spec. The spec says that - // the existing contents of the BlobBuilder should be included - // in the next blob produced. This seems silly and has been raised - // on the WHATWG listserv. - mBlobs.Clear(); - - return NS_OK; -} - -/* nsIDOMBlob getFile (in DOMString name, [optional] in DOMString contentType); */ -NS_IMETHODIMP -nsDOMBlobBuilder::GetFile(const nsAString& aName, - const nsAString& aContentType, - nsIDOMFile** aFile) -{ - NS_ENSURE_ARG(aFile); - - Flush(); - - nsCOMPtr<nsIDOMFile> file = new nsDOMMultipartFile(mBlobs, - aName, - aContentType); - file.forget(aFile); - - // NB: This is a willful violation of the spec. The spec says that - // the existing contents of the BlobBuilder should be included - // in the next blob produced. This seems silly and has been raised - // on the WHATWG listserv. - mBlobs.Clear(); - - return NS_OK; -} - -/* [implicit_jscontext] void append (in jsval data); */ -NS_IMETHODIMP -nsDOMBlobBuilder::Append(const jsval& aData, JSContext* aCx) -{ - // We need to figure out what our jsval is - - // Is it an object? - if (JSVAL_IS_OBJECT(aData)) { - JSObject* obj = JSVAL_TO_OBJECT(aData); - if (!obj) { - // We got passed null. Just do nothing. - return NS_OK; - } - - // Is it a Blob? - nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface( - nsContentUtils::XPConnect()-> - GetNativeOfWrapper(aCx, obj)); - if (blob) - return AppendBlob(blob); - - // Is it an array buffer? - if (js_IsArrayBuffer(obj)) { - JSObject* buffer = js::ArrayBuffer::getArrayBuffer(obj); - if (buffer) - return AppendArrayBuffer(buffer); - } - } - - // If it's not a Blob or an ArrayBuffer, coerce it to a string - JSString* str = JS_ValueToString(aCx, aData); - NS_ENSURE_TRUE(str, NS_ERROR_FAILURE); - - return AppendString(str, aCx); -} - -nsresult NS_NewBlobBuilder(nsISupports* *aSupports) -{ - nsDOMBlobBuilder* builder = new nsDOMBlobBuilder(); - return CallQueryInterface(builder, aSupports); -} +#endif
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1717,16 +1717,28 @@ NS_INTERFACE_TABLE_HEAD(nsDocument) else NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument) NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsDocument, nsNodeUtils::LastRelease(this)) +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument) + return nsGenericElement::CanSkip(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument) + return nsGenericElement::CanSkipInCC(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument) + return nsGenericElement::CanSkipThis(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + static PLDHashOperator SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) { SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); nsCycleCollectionTraversalCallback *cb = static_cast<nsCycleCollectionTraversalCallback*>(arg); @@ -5842,34 +5854,31 @@ NS_IMETHODIMP nsDocument::SetTextContent(const nsAString & aTextContent) { return nsINode::SetTextContent(aTextContent); } NS_IMETHODIMP nsDocument::LookupPrefix(const nsAString & namespaceURI, nsAString & aResult) { - SetDOMStringToNull(aResult); - return NS_OK; + return nsINode::LookupPrefix(namespaceURI, aResult); } NS_IMETHODIMP nsDocument::IsDefaultNamespace(const nsAString & namespaceURI, bool *aResult) { - *aResult = namespaceURI.IsEmpty(); - return NS_OK; + return nsINode::IsDefaultNamespace(namespaceURI, aResult); } NS_IMETHODIMP nsDocument::LookupNamespaceURI(const nsAString & prefix, nsAString & aResult) { - SetDOMStringToNull(aResult); - return NS_OK; + return nsINode::LookupNamespaceURI(prefix, aResult); } NS_IMETHODIMP nsDocument::SetUserData(const nsAString & key, nsIVariant *data, nsIDOMUserDataHandler *handler, nsIVariant **aResult) { return nsINode::SetUserData(key, data, handler, aResult);
--- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -875,18 +875,18 @@ public: virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents); void DecreaseEventSuppression() { --mEventsSuppressed; MaybeRescheduleAnimationFrameNotifications(); } - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument, - nsIDocument) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument, + nsIDocument) void DoNotifyPossibleTitleChange(); nsExternalResourceMap& ExternalResourceMap() { return mExternalResourceMap; }
--- a/content/base/src/nsGenericDOMDataNode.cpp +++ b/content/base/src/nsGenericDOMDataNode.cpp @@ -92,16 +92,28 @@ nsGenericDOMDataNode::~nsGenericDOMDataN } NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericDOMDataNode) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericDOMDataNode) nsINode::Trace(tmp, aCallback, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericDOMDataNode) + return nsGenericElement::CanSkip(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericDOMDataNode) + return nsGenericElement::CanSkipInCC(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericDOMDataNode) + return nsGenericElement::CanSkipThis(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode) // Always need to traverse script objects, so do that before we check // if we're uncollectable. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS if (!nsINode::Traverse(tmp, cb)) { return NS_SUCCESS_INTERRUPTED_TRAVERSE; }
--- a/content/base/src/nsGenericDOMDataNode.h +++ b/content/base/src/nsGenericDOMDataNode.h @@ -267,17 +267,17 @@ public: bool aCloneAfterOriginal = true); //---------------------------------------- #ifdef DEBUG void ToCString(nsAString& aBuf, PRInt32 aOffset, PRInt32 aLen) const; #endif - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericDOMDataNode) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsGenericDOMDataNode) protected: virtual mozilla::dom::Element* GetNameSpaceElement() { nsINode *parent = GetNodeParent(); return parent && parent->IsElement() ? parent->AsElement() : nsnull; } @@ -343,13 +343,23 @@ protected: * @param aCloneText if true the text content will be cloned too * @return the clone */ virtual nsGenericDOMDataNode *CloneDataNode(nsINodeInfo *aNodeInfo, bool aCloneText) const = 0; nsTextFragment mText; +public: + virtual bool IsPurple() + { + return mRefCnt.IsPurple(); + } + virtual void RemovePurple() + { + mRefCnt.RemovePurple(); + } + private: already_AddRefed<nsIAtom> GetCurrentValueAtom(); }; #endif /* nsGenericDOMDataNode_h___ */
--- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -148,17 +148,17 @@ #include "mozAutoDocUpdate.h" #include "nsCSSParser.h" #include "prprf.h" #include "nsSVGFeatures.h" #include "nsDOMMemoryReporter.h" #include "nsWrapperCacheInlines.h" - +#include "nsCycleCollector.h" #include "xpcpublic.h" #include "xpcprivate.h" using namespace mozilla; using namespace mozilla::dom; NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID); @@ -1203,55 +1203,57 @@ nsINode::GetContextForEventHandlers(nsre /* static */ void nsINode::Trace(nsINode *tmp, TraceCallback cb, void *closure) { nsContentUtils::TraceWrapper(tmp, cb, closure); } -static bool -IsXBL(nsINode* aNode) -{ - return aNode->IsElement() && - aNode->AsElement()->IsInNamespace(kNameSpaceID_XBL); + +static +bool UnoptimizableCCNode(nsINode* aNode) +{ + const PtrBits problematicFlags = (NODE_IS_ANONYMOUS | + NODE_IS_IN_ANONYMOUS_SUBTREE | + NODE_IS_NATIVE_ANONYMOUS_ROOT | + NODE_MAY_BE_IN_BINDING_MNGR | + NODE_IS_INSERTION_PARENT); + return aNode->HasFlag(problematicFlags) || + aNode->NodeType() == nsIDOMNode::ATTRIBUTE_NODE || + // For strange cases like xbl:content/xbl:children + (aNode->IsElement() && + aNode->AsElement()->IsInNamespace(kNameSpaceID_XBL)); } /* static */ bool nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb) { nsIDocument *currentDoc = tmp->GetCurrentDoc(); if (currentDoc && nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) { return false; } if (nsCCUncollectableMarker::sGeneration) { // If we're black no need to traverse. - if (tmp->IsBlack()) { + if (tmp->IsBlack() || tmp->InCCBlackTree()) { return false; } - const PtrBits problematicFlags = - (NODE_IS_ANONYMOUS | - NODE_IS_IN_ANONYMOUS_SUBTREE | - NODE_IS_NATIVE_ANONYMOUS_ROOT | - NODE_MAY_BE_IN_BINDING_MNGR | - NODE_IS_INSERTION_PARENT); - - if (!tmp->HasFlag(problematicFlags) && !IsXBL(tmp)) { + if (!UnoptimizableCCNode(tmp)) { // If we're in a black document, return early. if ((currentDoc && currentDoc->IsBlack())) { return false; } // If we're not in anonymous content and we have a black parent, // return early. nsIContent* parent = tmp->GetParent(); - if (parent && !IsXBL(parent) && parent->IsBlack()) { + if (parent && !UnoptimizableCCNode(parent) && parent->IsBlack()) { NS_ABORT_IF_FALSE(parent->IndexOf(tmp) >= 0, "Parent doesn't own us?"); return false; } } } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNodeInfo) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent()) @@ -4251,16 +4253,392 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns } } NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericElement) nsINode::Trace(tmp, aCallback, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END +static JSObject* +GetJSObjectChild(nsINode* aNode) +{ + if (aNode->PreservingWrapper()) { + return aNode->GetWrapperPreserveColor(); + } + return aNode->GetExpandoObjectPreserveColor(); +} + +static bool +NeedsScriptTraverse(nsINode* aNode) +{ + JSObject* o = GetJSObjectChild(aNode); + return o && xpc_IsGrayGCThing(o); +} + +void +nsGenericElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild, + void* aData) +{ + PRUint32* gen = static_cast<PRUint32*>(aData); + xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen); +} + +void +nsGenericElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey, + void* aChild, void* aData) +{ + nsCOMPtr<nsIXPConnectWrappedJS> wjs = + do_QueryInterface(static_cast<nsISupports*>(aChild)); + xpc_UnmarkGrayObject(wjs); +} + +static void +MarkNodeChildren(nsINode* aNode) +{ + JSObject* o = GetJSObjectChild(aNode); + xpc_UnmarkGrayObject(o); + + nsEventListenerManager* elm = aNode->GetListenerManager(false); + if (elm) { + elm->UnmarkGrayJSListeners(); + } + + if (aNode->HasProperties()) { + nsIDocument* ownerDoc = aNode->OwnerDoc(); + ownerDoc->PropertyTable(DOM_USER_DATA)-> + Enumerate(aNode, nsGenericElement::MarkUserData, + &nsCCUncollectableMarker::sGeneration); + ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)-> + Enumerate(aNode, nsGenericElement::MarkUserDataHandler, + &nsCCUncollectableMarker::sGeneration); + } +} + +nsINode* +FindOptimizableSubtreeRoot(nsINode* aNode) +{ + nsINode* p; + while ((p = aNode->GetNodeParent())) { + if (UnoptimizableCCNode(aNode)) { + return nsnull; + } + aNode = p; + } + + if (UnoptimizableCCNode(aNode)) { + return nsnull; + } + return aNode; +} + +nsAutoTArray<nsINode*, 1020>* gCCBlackMarkedNodes = nsnull; + +void +ClearBlackMarkedNodes() +{ + if (!gCCBlackMarkedNodes) { + return; + } + PRUint32 len = gCCBlackMarkedNodes->Length(); + for (PRUint32 i = 0; i < len; ++i) { + nsINode* n = gCCBlackMarkedNodes->ElementAt(i); + n->SetCCMarkedRoot(false); + n->SetInCCBlackTree(false); + } + delete gCCBlackMarkedNodes; + gCCBlackMarkedNodes = nsnull; +} + +// static +bool +nsGenericElement::CanSkipInCC(nsINode* aNode) +{ + // Don't try to optimize anything during shutdown. + if (nsCCUncollectableMarker::sGeneration == 0) { + return false; + } + + // Bail out early if aNode is somewhere in anonymous content, + // or otherwise unusual. + if (UnoptimizableCCNode(aNode)) { + return false; + } + + nsIDocument* currentDoc = aNode->GetCurrentDoc(); + if (currentDoc && + nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) { + return !NeedsScriptTraverse(aNode); + } + + nsINode* root = + currentDoc ? static_cast<nsINode*>(currentDoc) : + FindOptimizableSubtreeRoot(aNode); + if (!root) { + return false; + } + + // Subtree has been traversed already. + if (root->CCMarkedRoot()) { + return root->InCCBlackTree() && !NeedsScriptTraverse(aNode); + } + + if (!gCCBlackMarkedNodes) { + gCCBlackMarkedNodes = new nsAutoTArray<nsINode*, 1020>; + } + + // nodesToUnpurple contains nodes which will be removed + // from the purple buffer if the DOM tree is black. + nsAutoTArray<nsIContent*, 1020> nodesToUnpurple; + // grayNodes need script traverse, so they aren't removed from + // the purple buffer, but are marked to be in black subtree so that + // traverse is faster. + nsAutoTArray<nsINode*, 1020> grayNodes; + + bool foundBlack = root->IsBlack(); + if (root != currentDoc) { + currentDoc = nsnull; + if (NeedsScriptTraverse(root)) { + grayNodes.AppendElement(root); + } else if (static_cast<nsIContent*>(root)->IsPurple()) { + nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root)); + } + } + + // Traverse the subtree and check if we could know without CC + // that it is black. + // Note, this traverse is non-virtual and inline, so it should be a lot faster + // than CC's generic traverse. + for (nsIContent* node = root->GetFirstChild(); node; + node = node->GetNextNode(root)) { + foundBlack = foundBlack || node->IsBlack(); + if (foundBlack && currentDoc) { + // If we can mark the whole document black, no need to optimize + // so much, since when the next purple node in the document will be + // handled, it is fast to check that currentDoc is in CCGeneration. + break; + } + if (NeedsScriptTraverse(node)) { + // Gray nodes need real CC traverse. + grayNodes.AppendElement(node); + } else if (node->IsPurple()) { + nodesToUnpurple.AppendElement(node); + } + } + + root->SetCCMarkedRoot(true); + root->SetInCCBlackTree(foundBlack); + gCCBlackMarkedNodes->AppendElement(root); + + if (!foundBlack) { + return false; + } + + if (currentDoc) { + // Special case documents. If we know the document is black, + // we can mark the document to be in CCGeneration. + currentDoc-> + MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); + } else { + for (PRUint32 i = 0; i < grayNodes.Length(); ++i) { + nsINode* node = grayNodes[i]; + node->SetInCCBlackTree(true); + } + gCCBlackMarkedNodes->AppendElements(grayNodes); + } + + // Subtree is black, we can remove non-gray purple nodes from + // purple buffer. + for (PRUint32 i = 0; i < nodesToUnpurple.Length(); ++i) { + nsIContent* purple = nodesToUnpurple[i]; + // Can't remove currently handled purple node. + if (purple != aNode) { + purple->RemovePurple(); + } + } + return !NeedsScriptTraverse(aNode); +} + +nsAutoTArray<nsINode*, 1020>* gPurpleRoots = nsnull; + +void ClearPurpleRoots() +{ + if (!gPurpleRoots) { + return; + } + PRUint32 len = gPurpleRoots->Length(); + for (PRUint32 i = 0; i < len; ++i) { + nsINode* n = gPurpleRoots->ElementAt(i); + n->SetIsPurpleRoot(false); + } + delete gPurpleRoots; + gPurpleRoots = nsnull; +} + +static bool +ShouldClearPurple(nsIContent* aContent) +{ + if (aContent && aContent->IsPurple()) { + return true; + } + + JSObject* o = GetJSObjectChild(aContent); + if (o && xpc_IsGrayGCThing(o)) { + return true; + } + + if (aContent->GetListenerManager(false)) { + return true; + } + + return aContent->HasProperties(); +} + +// CanSkip checks if aNode is black, and if it is, returns +// true. If aNode is in a black DOM tree, CanSkip may also remove other objects +// from purple buffer and unmark event listeners and user data. +// If the root of the DOM tree is a document, less optimizations are done +// since checking the blackness of the current document is usually fast and we +// don't want slow down such common cases. +bool +nsGenericElement::CanSkip(nsINode* aNode) +{ + // Don't try to optimize anything during shutdown. + if (nsCCUncollectableMarker::sGeneration == 0) { + return false; + } + + // Bail out early if aNode is somewhere in anonymous content, + // or otherwise unusual. + if (UnoptimizableCCNode(aNode)) { + return false; + } + + nsIDocument* currentDoc = aNode->GetCurrentDoc(); + if (currentDoc && + nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) { + MarkNodeChildren(aNode); + return true; + } + + nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) : + FindOptimizableSubtreeRoot(aNode); + if (!root) { + return false; + } + + // Subtree has been traversed already, and aNode + // wasn't removed from purple buffer. No need to do more here. + if (root->IsPurpleRoot()) { + return false; + } + + // nodesToClear contains nodes which are either purple or + // gray. + nsAutoTArray<nsIContent*, 1020> nodesToClear; + + bool foundBlack = root->IsBlack(); + if (root != currentDoc) { + currentDoc = nsnull; + if (ShouldClearPurple(static_cast<nsIContent*>(root))) { + nodesToClear.AppendElement(static_cast<nsIContent*>(root)); + } + } + + // Traverse the subtree and check if we could know without CC + // that it is black. + // Note, this traverse is non-virtual and inline, so it should be a lot faster + // than CC's generic traverse. + for (nsIContent* node = root->GetFirstChild(); node; + node = node->GetNextNode(root)) { + foundBlack = foundBlack || node->IsBlack(); + if (foundBlack) { + if (currentDoc) { + // If we can mark the whole document black, no need to optimize + // so much, since when the next purple node in the document will be + // handled, it is fast to check that the currentDoc is in CCGeneration. + break; + } + // No need to put stuff to the nodesToClear array, if we can clear it + // already here. + if (node->IsPurple() && node != aNode) { + node->RemovePurple(); + } + MarkNodeChildren(node); + } else if (ShouldClearPurple(node)) { + // Collect interesting nodes which we can clear if we find that + // they are kept alive in a black tree. + nodesToClear.AppendElement(node); + } + } + + if (!foundBlack) { + if (!gPurpleRoots) { + gPurpleRoots = new nsAutoTArray<nsINode*, 1020>(); + } + root->SetIsPurpleRoot(true); + gPurpleRoots->AppendElement(root); + return false; + } + + if (currentDoc) { + // Special case documents. If we know the document is black, + // we can mark the document to be in CCGeneration. + currentDoc-> + MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); + MarkNodeChildren(currentDoc); + } + + // Subtree is black, so we can remove purple nodes from + // purple buffer and mark stuff that to be certainly alive. + for (PRUint32 i = 0; i < nodesToClear.Length(); ++i) { + nsIContent* n = nodesToClear[i]; + MarkNodeChildren(n); + // Can't remove currently handled purple node. + if (n != aNode && n->IsPurple()) { + n->RemovePurple(); + } + } + return true; +} + +bool +nsGenericElement::CanSkipThis(nsINode* aNode) +{ + if (nsCCUncollectableMarker::sGeneration == 0) { + return false; + } + if (aNode->IsBlack()) { + return true; + } + nsIDocument* c = aNode->GetCurrentDoc(); + return + ((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) || + aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode); +} + +void +nsGenericElement::InitCCCallbacks() +{ + nsCycleCollector_setForgetSkippableCallback(ClearPurpleRoots); + nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes); +} + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericElement) + return nsGenericElement::CanSkip(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericElement) + return nsGenericElement::CanSkipInCC(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericElement) + return nsGenericElement::CanSkipThis(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + static const char* kNSURIs[] = { " ([none])", " (xmlns)", " (xml)", " (xhtml)", " (XLink)", " (XSLT)", " (XBL)",
--- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -597,27 +597,45 @@ public: * namespace ID must not be kNameSpaceID_Unknown and the name must not be * null. Note that this can only return info on attributes that actually * live on this element (and is only virtual to handle XUL prototypes). That * is, this should only be called from methods that only care about attrs * that effectively live in mAttrsAndChildren. */ virtual nsAttrInfo GetAttrInfo(PRInt32 aNamespaceID, nsIAtom* aName) const; - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericElement) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsGenericElement) virtual void NodeInfoChanged(nsINodeInfo* aOldNodeInfo) { } /** * Fire a DOMNodeRemoved mutation event for all children of this node */ void FireNodeRemovedForChildren(); + virtual bool IsPurple() + { + return mRefCnt.IsPurple(); + } + + virtual void RemovePurple() + { + mRefCnt.RemovePurple(); + } + + static bool CanSkip(nsINode* aNode); + static bool CanSkipInCC(nsINode* aNode); + static bool CanSkipThis(nsINode* aNode); + static void InitCCCallbacks(); + static void MarkUserData(void* aObject, nsIAtom* aKey, void* aChild, + void *aData); + static void MarkUserDataHandler(void* aObject, nsIAtom* aKey, void* aChild, + void* aData); protected: /** * Set attribute and (if needed) notify documentobservers and fire off * mutation events. This will send the AttributeChanged notification. * Callers of this method are responsible for calling AttributeWillChange, * since that needs to happen before the new attr value has been set, and * in particular before it has been parsed. *
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -577,16 +577,18 @@ nsXMLHttpRequest::Initialize(nsISupports void nsXMLHttpRequest::ResetResponse() { mResponseXML = nsnull; mResponseBody.Truncate(); mResponseText.Truncate(); mResponseBlob = nsnull; + mDOMFile = nsnull; + mBuilder = nsnull; mResultArrayBuffer = nsnull; mResultJSON = JSVAL_VOID; mLoadTransferred = 0; mResponseBodyDecodedPos = 0; } void nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver) @@ -962,16 +964,38 @@ nsXMLHttpRequest::CreateResponseParsedJS (jschar*)mResponseText.get(), mResponseText.Length(), &mResultJSON)) { return NS_ERROR_FAILURE; } return NS_OK; } +nsresult +nsXMLHttpRequest::CreatePartialBlob() +{ + if (mDOMFile) { + if (mLoadTotal == mLoadTransferred) { + mResponseBlob = mDOMFile; + } else { + mResponseBlob = + mDOMFile->CreateSlice(0, mLoadTransferred, EmptyString()); + } + return NS_OK; + } + + nsCAutoString contentType; + if (mLoadTotal == mLoadTransferred) { + mChannel->GetContentType(contentType); + } + + return mBuilder->GetBlobInternal(NS_ConvertASCIItoUTF16(contentType), + false, getter_AddRefs(mResponseBlob)); +} + /* attribute AString responseType; */ NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType) { switch (mResponseType) { case XML_HTTP_RESPONSE_TYPE_DEFAULT: aResponseType.Truncate(); break; case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER: @@ -990,16 +1014,19 @@ NS_IMETHODIMP nsXMLHttpRequest::GetRespo aResponseType.AssignLiteral("json"); break; case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT: aResponseType.AssignLiteral("moz-chunked-text"); break; case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER: aResponseType.AssignLiteral("moz-chunked-arraybuffer"); break; + case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB: + aResponseType.AssignLiteral("moz-blob"); + break; default: NS_ERROR("Should not happen"); } return NS_OK; } /* attribute AString responseType; */ @@ -1036,29 +1063,32 @@ NS_IMETHODIMP nsXMLHttpRequest::SetRespo return NS_ERROR_DOM_INVALID_STATE_ERR; } mResponseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT; } else if (aResponseType.EqualsLiteral("moz-chunked-arraybuffer")) { if (!(mState & XML_HTTP_REQUEST_ASYNC)) { return NS_ERROR_DOM_INVALID_STATE_ERR; } mResponseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER; + } else if (aResponseType.EqualsLiteral("moz-blob")) { + mResponseType = XML_HTTP_RESPONSE_TYPE_MOZ_BLOB; } // If the given value is not the empty string, "arraybuffer", // "blob", "document", or "text" terminate these steps. // If the state is OPENED, SetCacheAsFile would have no effect here // because the channel hasn't initialized the cache entry yet. // SetCacheAsFile will be called from OnStartRequest. // If the state is HEADERS_RECEIVED, however, we need to call // it immediately because OnStartRequest is already dispatched. if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) { nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel)); if (cc) { - cc->SetCacheAsFile(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB); + cc->SetCacheAsFile(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB || + mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB); } } return NS_OK; } /* readonly attribute jsval response; */ NS_IMETHODIMP nsXMLHttpRequest::GetResponse(JSContext *aCx, jsval *aResult) @@ -1092,22 +1122,32 @@ NS_IMETHODIMP nsXMLHttpRequest::GetRespo } *aResult = OBJECT_TO_JSVAL(mResultArrayBuffer); } else { *aResult = JSVAL_NULL; } break; case XML_HTTP_RESPONSE_TYPE_BLOB: - if (mState & XML_HTTP_REQUEST_DONE && mResponseBlob) { + case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB: + *aResult = JSVAL_NULL; + if (mState & XML_HTTP_REQUEST_DONE) { + // do nothing here + } else if (mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) { + if (!mResponseBlob) { + rv = CreatePartialBlob(); + NS_ENSURE_SUCCESS(rv, rv); + } + } else { + return rv; + } + if (mResponseBlob) { JSObject* scope = JS_GetGlobalForScopeChain(aCx); rv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, aResult, nsnull, true); - } else { - *aResult = JSVAL_NULL; } break; case XML_HTTP_RESPONSE_TYPE_DOCUMENT: if (mState & XML_HTTP_REQUEST_DONE && mResponseXML) { JSObject* scope = JS_GetGlobalForScopeChain(aCx); rv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, aResult, nsnull, true); @@ -1707,44 +1747,55 @@ nsXMLHttpRequest::StreamReaderFunc(nsIIn PRUint32 *writeCount) { nsXMLHttpRequest* xmlHttpRequest = static_cast<nsXMLHttpRequest*>(closure); if (!xmlHttpRequest || !writeCount) { NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount"); return NS_ERROR_FAILURE; } - if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB && - xmlHttpRequest->mResponseBlob) { - *writeCount = count; - return NS_OK; + nsresult rv = NS_OK; + + if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB || + xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) { + if (!xmlHttpRequest->mDOMFile) { + if (!xmlHttpRequest->mBuilder) { + xmlHttpRequest->mBuilder = new nsDOMBlobBuilder(); + } + rv = xmlHttpRequest->mBuilder->AppendVoidPtr(fromRawSegment, count); + } + // Clear the cache so that the blob size is updated. + if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) { + xmlHttpRequest->mResponseBlob = nsnull; + } + if (NS_SUCCEEDED(rv)) { + *writeCount = count; + } + return rv; } if ((xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT && xmlHttpRequest->mResponseXML) || xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER || - xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB || xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) { // Copy for our own use PRUint32 previousLength = xmlHttpRequest->mResponseBody.Length(); xmlHttpRequest->mResponseBody.Append(fromRawSegment,count); if (count > 0 && xmlHttpRequest->mResponseBody.Length() == previousLength) { return NS_ERROR_OUT_OF_MEMORY; } } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT || xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_TEXT || xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_JSON || xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) { NS_ASSERTION(!xmlHttpRequest->mResponseXML, "We shouldn't be parsing a doc here"); xmlHttpRequest->AppendToResponseText(fromRawSegment, count); } - nsresult rv = NS_OK; - if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) { // Give the same data to the parser. // We need to wrap the data in a new lightweight stream and pass that // to the parser, because calling ReadSegments() recursively on the same // stream is not supported. nsCOMPtr<nsIInputStream> copyStream; rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count); @@ -1768,17 +1819,17 @@ nsXMLHttpRequest::StreamReaderFunc(nsIIn *writeCount = count; } else { *writeCount = 0; } return rv; } -bool nsXMLHttpRequest::CreateResponseBlob(nsIRequest *request) +bool nsXMLHttpRequest::CreateDOMFile(nsIRequest *request) { nsCOMPtr<nsIFile> file; nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(request)); if (cc) { cc->GetCacheFile(getter_AddRefs(file)); } else { nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(request); if (fc) { @@ -1796,19 +1847,20 @@ bool nsXMLHttpRequest::CreateResponseBlo // fully cached (i.e. whether we can skip reading the response). cc->IsFromCache(&fromFile); } else { // If the response is coming from the local resource, we can skip // reading the response unconditionally. fromFile = true; } - mResponseBlob = + mDOMFile = new nsDOMFileFile(file, NS_ConvertASCIItoUTF16(contentType), cacheToken); - mResponseBody.Truncate(); + mBuilder = nsnull; + NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty"); } return fromFile; } NS_IMETHODIMP nsXMLHttpRequest::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, @@ -1817,30 +1869,31 @@ nsXMLHttpRequest::OnDataAvailable(nsIReq { NS_ENSURE_ARG_POINTER(inStr); NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context"); mProgressSinceLastProgressEvent = true; bool cancelable = false; - if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB && !mResponseBlob) { - cancelable = CreateResponseBlob(request); + if ((mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB || + mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) && !mDOMFile) { + cancelable = CreateDOMFile(request); // The nsIStreamListener contract mandates us // to read from the stream before returning. } PRUint32 totalRead; nsresult rv = inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc, (void*)this, count, &totalRead); NS_ENSURE_SUCCESS(rv, rv); if (cancelable) { // We don't have to read from the local file for the blob response - mResponseBlob->GetSize(&mLoadTransferred); + mDOMFile->GetSize(&mLoadTransferred); ChangeState(XML_HTTP_REQUEST_LOADING); return request->Cancel(NS_OK); } mLoadTransferred += totalRead; ChangeState(XML_HTTP_REQUEST_LOADING); @@ -1931,17 +1984,18 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ } mReadRequest = request; mContext = ctxt; mState |= XML_HTTP_REQUEST_PARSEBODY; mState &= ~XML_HTTP_REQUEST_MPART_HEADERS; ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED); - if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) { + if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB || + mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) { nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel)); if (cc) { cc->SetCacheAsFile(true); } } ResetResponse(); @@ -2127,44 +2181,41 @@ nsXMLHttpRequest::OnStopRequest(nsIReque // If we're received data since the last progress event, make sure to fire // an event for it, except in the HTML case, defer the last progress event // until the parser is done. if (!mIsHtml) { MaybeDispatchProgressEvents(true); } - nsCOMPtr<nsIChannel> channel(do_QueryInterface(request)); - NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED); - - if (NS_SUCCEEDED(status) && mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) { - if (!mResponseBlob) { - CreateResponseBlob(request); + if (NS_SUCCEEDED(status) && + (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB || + mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB)) { + if (!mDOMFile) { + CreateDOMFile(request); } - if (!mResponseBlob) { + if (mDOMFile) { + mResponseBlob = mDOMFile; + mDOMFile = nsnull; + } else { // Smaller files may be written in cache map instead of separate files. // Also, no-store response cannot be written in persistent cache. nsCAutoString contentType; mChannel->GetContentType(contentType); - // XXX We should change mResponseBody to be a raw malloc'ed buffer - // to avoid copying the data. - PRUint32 blobLen = mResponseBody.Length(); - void *blobData = PR_Malloc(blobLen); - if (blobData) { - memcpy(blobData, mResponseBody.BeginReading(), blobLen); - - mResponseBlob = - new nsDOMMemoryFile(blobData, blobLen, - NS_ConvertASCIItoUTF16(contentType)); - mResponseBody.Truncate(); - } - NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty"); + mBuilder->GetBlobInternal(NS_ConvertASCIItoUTF16(contentType), + false, getter_AddRefs(mResponseBlob)); + mBuilder = nsnull; } + NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty"); + NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty"); } + nsCOMPtr<nsIChannel> channel(do_QueryInterface(request)); + NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED); + channel->SetNotificationCallbacks(nsnull); mNotificationCallbacks = nsnull; mChannelEventSink = nsnull; mProgressEventSink = nsnull; mState &= ~XML_HTTP_REQUEST_SYNCLOOPING; if (NS_FAILED(status)) {
--- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -60,16 +60,18 @@ #include "nsIJSNativeInitializer.h" #include "nsIDOMLSProgressEvent.h" #include "nsIDOMNSEvent.h" #include "nsITimer.h" #include "nsIPrivateDOMEvent.h" #include "nsDOMProgressEvent.h" #include "nsDOMEventTargetWrapperCache.h" #include "nsContentUtils.h" +#include "nsDOMFile.h" +#include "nsDOMBlobBuilder.h" class nsILoadGroup; class AsyncVerifyRedirectCallbackForwarder; class nsIUnicodeDecoder; class nsXHREventTarget : public nsDOMEventTargetWrapperCache, public nsIXMLHttpRequestEventTarget { @@ -212,17 +214,18 @@ protected: nsresult AppendToResponseText(const char * aBuffer, PRUint32 aBufferLen); static NS_METHOD StreamReaderFunc(nsIInputStream* in, void* closure, const char* fromRawSegment, PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount); nsresult CreateResponseParsedJSON(JSContext* aCx); - bool CreateResponseBlob(nsIRequest *request); + nsresult CreatePartialBlob(void); + bool CreateDOMFile(nsIRequest *request); // Change the state of the object with this. The broadcast argument // determines if the onreadystatechange listener should be called. nsresult ChangeState(PRUint32 aState, bool aBroadcast = true); already_AddRefed<nsILoadGroup> GetLoadGroup() const; nsIURI *GetBaseURI(); nsresult RemoveAddEventListener(const nsAString& aType, nsRefPtr<nsDOMEventListenerWrapper>& aCurrent, @@ -304,20 +307,30 @@ protected: enum { XML_HTTP_RESPONSE_TYPE_DEFAULT, XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER, XML_HTTP_RESPONSE_TYPE_BLOB, XML_HTTP_RESPONSE_TYPE_DOCUMENT, XML_HTTP_RESPONSE_TYPE_TEXT, XML_HTTP_RESPONSE_TYPE_JSON, XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT, - XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER + XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER, + XML_HTTP_RESPONSE_TYPE_MOZ_BLOB } mResponseType; + // It is either a cached blob-response from the last call to GetResponse, + // but is also explicitly set in OnStopRequest. nsCOMPtr<nsIDOMBlob> mResponseBlob; + // Non-null only when we are able to get a os-file representation of the + // response, i.e. when loading from a file, or when the http-stream + // caches into a file or is reading from a cached file. + nsRefPtr<nsDOMFileBase> mDOMFile; + // We stream data to mBuilder when response type is "blob" or "moz-blob" + // and mDOMFile is null. + nsRefPtr<nsDOMBlobBuilder> mBuilder; nsCString mOverrideMimeType; /** * The notification callbacks the channel had when Send() was * called. We want to forward things here as needed. */ nsCOMPtr<nsIInterfaceRequestor> mNotificationCallbacks;
--- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -524,16 +524,17 @@ include $(topsrcdir)/config/rules.mk test_bug684671.html \ test_bug685798.html \ test_bug686449.xhtml \ test_bug690056.html \ test_bug692434.html \ file_bug692434.xml \ test_bug693615.html \ test_bug693875.html \ + test_bug694754.xhtml \ test_bug698384.html \ test_nodelist_holes.html \ test_xhr_abort_after_load.html \ test_bug702439.html \ test_bug702439.html^headers^ \ file_bug702439.html \ test_bug707142.html \ file_bug707142_baseline.json \
--- a/content/base/test/test_XHR.html +++ b/content/base/test/test_XHR.html @@ -207,23 +207,26 @@ is(xhr.status, 200, "wrong status"); checkResponseTextAccessThrows(xhr); checkResponseXMLAccessThrows(xhr); is(xhr.response, null, "Bad JSON should result in null response."); is(xhr.response, null, "Bad JSON should result in null response even 2nd time."); // test response (responseType='blob') var onloadCount = 0; function checkOnloadCount() { - if (++onloadCount >= 3) SimpleTest.finish(); + if (++onloadCount >= 6) SimpleTest.finish(); }; +var responseTypes = ['blob', 'moz-blob']; +for (var i = 0; i < responseTypes.length; i++) { +var t = responseTypes[i]; // with a simple text file xhr = new XMLHttpRequest(); xhr.open("GET", 'file_XHR_pass2.txt'); -xhr.responseType = 'blob'; +xhr.responseType = t; xhr.onloadend = continueTest; xhr.send(null); yield; is(xhr.status, 200, "wrong status"); checkResponseTextAccessThrows(xhr); checkResponseXMLAccessThrows(xhr); b = xhr.response; ok(b, "should have a non-null blob"); @@ -240,17 +243,17 @@ fr.readAsBinaryString(b); // with a binary file (function(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { switch (xhr.readyState) { case 2: is(xhr.status, 200, "wrong status"); - xhr.responseType = 'blob'; + xhr.responseType = t; break; case 4: b = xhr.response; ok(b != null, "should have a non-null blob"); is(b.size, 12, "wrong blob size"); var fr = new FileReader(); fr.onload = function() { @@ -288,19 +291,20 @@ xhr.onreadystatechange = function() { checkOnloadCount(); }; xhr = null; // kill the XHR object SpecialPowers.gc(); fr.readAsArrayBuffer(b); } }; xhr.open("GET", 'file_XHR_binary2.bin', true); -xhr.responseType = 'blob'; +xhr.responseType = t; xhr.send(null); })(); +} var client = new XMLHttpRequest(); client.onreadystatechange = function() { if(client.readyState == 4) { try { is(client.responseXML, null, "responseXML should be null."); is(client.responseText, "", "responseText should be empty string."); is(client.response, "", "response should be empty string.");
new file mode 100644 --- /dev/null +++ b/content/base/test/test_bug694754.xhtml @@ -0,0 +1,70 @@ +<!DOCTYPE HTML> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:test="http://example.com/test"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=694754 +--> +<head> + <title>Test for Bug 694754</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=694754">Mozilla Bug 694754</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 694754 **/ +/* +The following code tests if calling the DOM methods Document::lookupNamespaceURI +and Document::lookupPrefix directly (with quickstubs) and through XPCOM leads +to the same result. + +This test makes use of the bug/feature that deleting a method from the +prototype forces the engine to go through XPCOM. +*/ + +// Document::lookupPrefix called directly (quickstubs) +var prefixDirect = document.lookupPrefix("http://example.com/test"); +is(prefixDirect, "test", + "calling Document::lookupPrefix through quickstubs works"); + +// Document::lookupPrefix called via XPCOM +var proto = Object.getPrototypeOf(document); +delete(proto.lookupPrefix); +var prefixThroughXPCOM = document.lookupPrefix("http://example.com/test"); +is(prefixThroughXPCOM, "test", + "calling Document::lookupPrefix through XPCOM works"); + + + +// Document::lookupNamespaceURI called directly (quickstubs) +var namespaceDirect = document.lookupNamespaceURI(null); +is(namespaceDirect, "http://www.w3.org/1999/xhtml", + "calling Document::lookupNamespaceURI through quickstubs works"); + +// Document::lookupNamespaceURI called via XPCOM +delete(proto.lookupNamespaceURI); +var namespaceThroughXPCOM = document.lookupNamespaceURI(null); +is(namespaceThroughXPCOM, "http://www.w3.org/1999/xhtml", + "calling Document::lookupNamespaceURI through XPCOM works"); + +// Document::isDefaultNamespace called directly (quickstubs) +var isDefaultNamespaceDirect = document.isDefaultNamespace("http://www.w3.org/1999/xhtml"); +is(isDefaultNamespaceDirect, true, + "Default namespace correctly detected through quickstubs"); + +// Document::isDefaultNamespace called via XPCOM +delete(proto.isDefaultNamespace); +var isDefaultNamespaceXPCOM = document.isDefaultNamespace("http://www.w3.org/1999/xhtml"); +is(isDefaultNamespaceXPCOM, true, + "Default namespace correctly detected through XPCOM"); + + +</script> +</pre> +</body> +</html>
--- a/content/base/test/test_xhr_progressevents.html +++ b/content/base/test/test_xhr_progressevents.html @@ -34,52 +34,60 @@ function updateProgress(e, data, testNam if (data.nodata) { is(e.target.response, null, "response should be null" + test); response = null; } else if (data.text) { is(typeof e.target.response, "string", "response should be a string" + test); response = e.target.response; } + else if (data.blob) { + ok(e.target.response instanceof Blob, "response should be a Blob" + test); + response = e.target.response; + } else { - ok(e.target.response instanceof ArrayBuffer, "response should be a ArrayBuffer" + test); + ok(e.target.response instanceof ArrayBuffer, "response should be an ArrayBuffer" + test); response = bufferToString(e.target.response); } + is(e.target.response, e.target.response, "reflexivity should hold" + test); if (!data.nodata && !data.encoded) { - if (!data.chunked) { + if (data.blob) { + is(e.loaded, response.size, "event.loaded matches response size" + test); + } + else if (!data.chunked) { is(e.loaded, response.length, "event.loaded matches response size" + test); } else { is(e.loaded - data.receivedBytes, response.length, "event.loaded grew by response size" + test); } } ok(e.loaded > data.receivedBytes, "event.loaded increased" + test); ok(e.loaded - data.receivedBytes <= data.pendingBytes, "event.loaded didn't increase too much" + test); - if (!data.nodata) { + if (!data.nodata && !data.blob) { var newData; ok(startsWith(response, data.receivedResult), "response strictly grew" + test); newData = response.substr(data.receivedResult.length); if (!data.encoded) { ok(newData.length > 0, "sanity check for progress" + test); } ok(startsWith(data.pendingResult, newData), "new data matches expected" + test); } is(e.lengthComputable, "total" in data, "lengthComputable" + test); if ("total" in data) { is(e.total, data.total, "total" + test); } - if (!data.nodata) { + if (!data.nodata && !data.blob) { data.pendingResult = data.pendingResult.substr(newData.length); } data.pendingBytes -= e.loaded - data.receivedBytes; data.receivedResult = response; data.receivedBytes = e.loaded; } function sendData(s) { @@ -108,17 +116,18 @@ function bufferToString(buffer) { } function runTests() { var xhr = new XMLHttpRequest(); xhr.onprogress = xhr.onload = xhr.onerror = xhr.onreadystatechange = xhr.onloadend = getEvent; var responseTypes = [{ type: "text", text: true }, { type: "arraybuffer", text: false, nodata: true }, - { type: "blob", text: false, nodata: true }, + { type: "blob", text: false, nodata: true, blob: true }, + { type: "moz-blob", text: false, nodata: false, blob: true }, { type: "document", text: true, nodata: true }, { type: "json", text: true, nodata: true }, { type: "", text: true }, { type: "moz-chunked-text", text: true, chunked: true }, { type: "moz-chunked-arraybuffer", text: false, chunked: true }, ]; var responseType; var fileExpectedResult = ""; @@ -148,17 +157,17 @@ function runTests() { { data: utf8encode("Å").substr(1), utf16: "Å" }, { data: utf8encode("aöb").substr(0,2), utf16: "a" }, { data: utf8encode("aöb").substr(2), utf16: "öb" }, { data: utf8encode("a\u867Eb").substr(0,3), utf16: "a" }, { data: utf8encode("a\u867Eb").substr(3,1), utf16: "\u867E" }, { data: utf8encode("a\u867Eb").substr(4), utf16: "b" }, { close: true }, ]; - if (responseType.type === "blob") { + if (responseType.blob) { tests.push({ file: "file_XHR_binary2.bin", name: "cacheable data", total: 65536 }, { close: true }, { file: "file_XHR_binary2.bin", name: "cached data", total: 65536 }, { close: true }); } let testState = { index: 0 }; for (let i = 0; i < tests.length; ++i) { @@ -172,16 +181,17 @@ function runTests() { pendingBytes: 5, receivedResult: "", receivedBytes: 0, total: test.total, encoded: test.encoded, nodata: responseType.nodata, chunked: responseType.chunked, text: responseType.text, + blob: responseType.blob, file: test.file }; xhr.onreadystatechange = null; if (testState.file) xhr.open("GET", test.file); else xhr.open("POST", "progressserver.sjs?open&" + test.open); xhr.responseType = responseType.type; @@ -230,25 +240,25 @@ function runTests() { is(e.type, "loadend", "should fire loadend closing " + testState.name); is(e.lengthComputable, true, "length should be computable during loadend closing " + testState.name); log("got loadend"); if (responseType.chunked) { is(xhr.response, null, "chunked data has null response for " + testState.name); } - if (!testState.nodata || responseType.chunked) { + if (!testState.nodata && !responseType.blob || responseType.chunked) { // This branch intentionally left blank // Under these conditions we check the response during updateProgress } else if (responseType.type === "arraybuffer") { is(bufferToString(xhr.response), testState.pendingResult, "full response for " + testState.name); } - else if (responseType.type === "blob") { + else if (responseType.blob) { let reader = new FileReader; reader.readAsBinaryString(xhr.response); reader.onloadend = getEvent; yield; is(reader.result, testState.pendingResult, "full response in blob for " + testState.name); } @@ -275,17 +285,17 @@ function runTests() { continue; updateProgress(e, testState, "data for " + testState.name + "[" + testState.index + "]"); if (responseType.chunked) { testState.receivedResult = ""; } } - if (!testState.nodata) { + if (!testState.nodata && !testState.blob) { is(testState.pendingResult, "", "should have consumed the expected result"); } log("done with this test"); } is(testState.name, "", "forgot to close last test");
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp @@ -990,17 +990,17 @@ NS_NewCanvasRenderingContext2DAzure(nsID { #ifdef XP_WIN if ((gfxWindowsPlatform::GetPlatform()->GetRenderMode() != gfxWindowsPlatform::RENDER_DIRECT2D || !gfxWindowsPlatform::GetPlatform()->DWriteEnabled()) && !Preferences::GetBool("gfx.canvas.azure.prefer-skia", false)) { return NS_ERROR_NOT_AVAILABLE; } -#elif !defined(XP_MACOSX) && !defined(ANDROID) && !defined(XP_LINUX) +#elif !defined(XP_MACOSX) && !defined(ANDROID) && !defined(LINUX) return NS_ERROR_NOT_AVAILABLE; #endif nsRefPtr<nsIDOMCanvasRenderingContext2D> ctx = new nsCanvasRenderingContext2DAzure(); if (!ctx) return NS_ERROR_OUT_OF_MEMORY; *aResult = ctx.forget().get();
--- a/content/canvas/test/webgl/README.mozilla +++ b/content/canvas/test/webgl/README.mozilla @@ -1,9 +1,9 @@ -This is a local copy of the WebGL conformance suite, SVN revision 16456 +This is a local copy of the WebGL conformance suite, SVN revision 16776 The canonical location for this testsuite is: https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/sdk/tests All files and directories in this directory, with the exceptions listed below, come from upstream and should not be modified without corresponding upstream fixes and/or a patch file in this directory. The exceptions (the Mozilla-specific files) are:
--- a/content/canvas/test/webgl/conformance/buffers/buffer-data-array-buffer.html +++ b/content/canvas/test/webgl/conformance/buffers/buffer-data-array-buffer.html @@ -22,17 +22,17 @@ debug('Regression test for <a href="http var gl = create3DContext(); shouldBeNonNull("gl"); var array = new ArrayBuffer(128); shouldBeNonNull("array"); var buf = gl.createBuffer(); -shouldBeNonNull(buf); +shouldBeNonNull("buf"); gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); glErrorShouldBe(gl, gl.INVALID_OPERATION); gl.bindBuffer(gl.ARRAY_BUFFER, buf); glErrorShouldBe(gl, gl.NO_ERROR); gl.bufferData(gl.ARRAY_BUFFER, -10, gl.STATIC_DRAW);
--- a/content/canvas/test/webgl/conformance/canvas/drawingbuffer-static-canvas-test.html +++ b/content/canvas/test/webgl/conformance/canvas/drawingbuffer-static-canvas-test.html @@ -97,17 +97,17 @@ var maxSize; var canvas = document.getElementById("canvas"); var gl = wtu.create3DContext(canvas); if (!gl) { testFailed("context does not exist"); } else { testPassed("context exists"); gl.program = createProgram(gl, "vshader", "fshader", ["vPosition"]); - shouldBeNonNull(gl.program); + shouldBeNonNull("gl.program"); gl.useProgram(gl.program); gl.enable(gl.DEPTH_TEST); gl.disable(gl.BLEND); gl.clearColor(0, 0, 0, 1); gl.clearDepth(1); shouldBe('gl.getError()', 'gl.NO_ERROR'); debug("");
--- a/content/canvas/test/webgl/conformance/canvas/drawingbuffer-test.html +++ b/content/canvas/test/webgl/conformance/canvas/drawingbuffer-test.html @@ -96,17 +96,17 @@ var maxSize; var canvas = document.createElement("canvas"); var gl = create3DContext(canvas); if (!gl) { testFailed("context does not exist"); } else { testPassed("context exists"); gl.program = createProgram(gl, "vshader", "fshader", ["vPosition"]); - shouldBeNonNull(gl.program); + shouldBeNonNull("gl.program"); gl.useProgram(gl.program); gl.enable(gl.DEPTH_TEST); gl.disable(gl.BLEND); gl.clearColor(0, 0, 0, 1); gl.clearDepth(1); shouldBe('gl.getError()', 'gl.NO_ERROR'); debug("");
--- a/content/canvas/test/webgl/conformance/context/context-lost-restored.html +++ b/content/canvas/test/webgl/conformance/context/context-lost-restored.html @@ -1,12 +1,11 @@ <!DOCTYPE html> <html> <head> -<meta charset="utf-8"> <link rel="stylesheet" href="../../resources/js-test-style.css"/> <script src="../../resources/js-test-pre.js"></script> <script src="../resources/webgl-test.js"></script> <script src="../resources/webgl-test-utils.js"></script> <script> var wtu = WebGLTestUtils; var canvas; var gl; @@ -63,17 +62,17 @@ function testLosingContext() canvas.addEventListener("webglcontextlost", function(e) { testLostContext(e); // restore the context after this event has exited. setTimeout(function() { // we didn't call prevent default so we should not be able to restore the context shouldGenerateGLError(gl, gl.INVALID_OPERATION, "extension.restoreContext()"); testLosingAndRestoringContext(); - }, 1); + }, 0); }); canvas.addEventListener("webglcontextrestored", testShouldNotRestoreContext); allowRestore = false; contextLostEventFired = false; contextRestoredEventFired = false; testOriginalContext(); extension.loseContext(); @@ -101,17 +100,17 @@ function testLosingAndRestoringContext() setTimeout(function() { shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()"); // The context should still be lost. It will not get restored until the // webglrestorecontext event is fired. shouldBeTrue("gl.isContextLost()"); shouldBe("gl.getError()", "gl.NO_ERROR"); // gl methods should still be no-ops shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)"); - }, 1); + }, 0); }); canvas.addEventListener("webglcontextrestored", function() { testRestoredContext(); finishTest(); }); allowRestore = true; contextLostEventFired = false; contextRestoredEventFired = false; @@ -159,17 +158,17 @@ function testOriginalContext() function testLostContext(e) { debug("Test lost context"); shouldBeFalse("contextLostEventFired"); contextLostEventFired = true; shouldBeTrue("gl.isContextLost()"); shouldBe("gl.getError()", "gl.NO_ERROR"); debug(""); - if (allowRestore) + if (allowRestore) e.preventDefault(); } function testShouldNotRestoreContext(e) { testFailed("Should not restore the context unless preventDefault is called on the context lost event"); debug(""); }
--- a/content/canvas/test/webgl/conformance/context/context-lost.html +++ b/content/canvas/test/webgl/conformance/context/context-lost.html @@ -1,12 +1,11 @@ <!DOCTYPE html> <html> <head> -<meta charset="utf-8"> <link rel="stylesheet" href="../../resources/js-test-style.css"/> <script src="../../resources/js-test-pre.js"></script> <script src="../resources/webgl-test.js"></script> <script src="../resources/webgl-test-utils.js"></script> <script> var wtu; var canvas; var gl;
--- a/content/canvas/test/webgl/conformance/extensions/00_test_list.txt +++ b/content/canvas/test/webgl/conformance/extensions/00_test_list.txt @@ -1,8 +1,7 @@ oes-standard-derivatives.html oes-texture-float.html oes-vertex-array-object.html webgl-debug-renderer-info.html webgl-debug-shaders.html -# commented out until 1.0.1 cut -# webgl-experimental-compressed-textures.html +--min-version 1.0.2 webgl-experimental-compressed-textures.html
--- a/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html +++ b/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html @@ -52,17 +52,25 @@ precision mediump float; varying vec2 texCoord; void main() { float dx = dFdx(texCoord.x); float dy = dFdy(texCoord.y); float w = fwidth(texCoord.x); gl_FragColor = vec4(dx, dy, w, 1.0); } </script> - +<!-- Shaders to link with test fragment shaders --> +<script id="goodVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +varying vec2 texCoord; +void main() { + texCoord = vPosition.xy; + gl_Position = vPosition; +} +</script> <!-- Shaders to test output --> <script id="outputVertexShader" type="x-shader/x-vertex"> attribute vec4 vPosition; varying vec4 position; void main() { position = vPosition; gl_Position = vPosition; } @@ -178,53 +186,54 @@ function runHintTestEnabled() { } } if (!anyFailed) { testPassed("Round-trip of hint()/getParameter() with all supported modes"); } } function runShaderTests(extensionEnabled) { + debug(""); debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled")); // Expect the macro shader to succeed ONLY if enabled - var macroFragmentShader = wtu.loadShaderFromScript(gl, "macroFragmentShader"); + var macroFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "macroFragmentShader"); if (extensionEnabled) { - if (macroFragmentShader) { + if (macroFragmentProgram) { // Expected result testPassed("GL_OES_standard_derivatives defined in shaders when extension is enabled"); } else { testFailed("GL_OES_standard_derivatives not defined in shaders when extension is enabled"); } } else { - if (macroFragmentShader) { + if (macroFragmentProgram) { testFailed("GL_OES_standard_derivatives defined in shaders when extension is disabled"); } else { testPassed("GL_OES_standard_derivatives not defined in shaders when extension disabled"); } } // Always expect the shader missing the #pragma to fail (whether enabled or not) - var missingPragmaFragmentShader = wtu.loadShaderFromScript(gl, "missingPragmaFragmentShader"); - if (missingPragmaFragmentShader) { + var missingPragmaFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "missingPragmaFragmentShader"); + if (missingPragmaFragmentProgram) { testFailed("Shader built-ins allowed without #extension pragma"); } else { testPassed("Shader built-ins disallowed without #extension pragma"); } // Try to compile a shader using the built-ins that should only succeed if enabled - var testFragmentShader = wtu.loadShaderFromScript(gl, "testFragmentShader"); + var testFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "testFragmentShader"); if (extensionEnabled) { - if (testFragmentShader) { + if (testFragmentProgram) { testPassed("Shader built-ins compiled successfully when extension enabled"); } else { testFailed("Shader built-ins failed to compile when extension enabled"); } } else { - if (testFragmentShader) { + if (testFragmentProgram) { testFailed("Shader built-ins compiled successfully when extension disabled"); } else { testPassed("Shader built-ins failed to compile when extension disabled"); } } } function runOutputTests() {
--- a/content/canvas/test/webgl/conformance/glsl/00_test_list.txt +++ b/content/canvas/test/webgl/conformance/glsl/00_test_list.txt @@ -1,10 +1,8 @@ functions/00_test_list.txt implicit/00_test_list.txt -# commented out for version 1.0.1 of the conforamnce tests. -#matrices/00_test_list.txt +--min-version 1.0.2 matrices/00_test_list.txt misc/00_test_list.txt reserved/00_test_list.txt -# commented out for version 1.0.1 of the conforamnce tests. -#samplers/00_test_list.txt +--min-version 1.0.2 samplers/00_test_list.txt variables/00_test_list.txt
--- a/content/canvas/test/webgl/conformance/glsl/misc/00_test_list.txt +++ b/content/canvas/test/webgl/conformance/glsl/misc/00_test_list.txt @@ -1,55 +1,67 @@ attrib-location-length-limits.html embedded-struct-definitions-forbidden.html -#glsl-2types-of-textures-on-same-unit.html +# this test is intentionally disabled as it is too strict and to hard to simulate +# glsl-2types-of-textures-on-same-unit.html glsl-function-nodes.html glsl-long-variable-names.html non-ascii-comments.vert.html non-ascii.vert.html + shader-with-256-character-identifier.frag.html shader-with-257-character-identifier.frag.html shader-with-_webgl-identifier.vert.html shader-with-arbitrary-indexing.frag.html shader-with-arbitrary-indexing.vert.html shader-with-attrib-array.vert.html shader-with-attrib-struct.vert.html shader-with-clipvertex.vert.html +--min-version 1.0.2 shader-with-conditional-scoping.html shader-with-default-precision.frag.html shader-with-default-precision.vert.html shader-with-define-line-continuation.frag.html shader-with-dfdx-no-ext.frag.html shader-with-dfdx.frag.html +--min-version 1.0.2 shader-with-do-scoping.html shader-with-error-directive.html shader-with-explicit-int-cast.vert.html shader-with-float-return-value.frag.html +--min-version 1.0.2 shader-with-for-scoping.html +--min-version 1.0.2 shader-with-for-loop.html shader-with-frag-depth.frag.html shader-with-function-recursion.frag.html +--min-version 1.0.2 shader-with-function-scoped-struct.html +--min-version 1.0.2 shader-with-functional-scoping.html shader-with-glcolor.vert.html shader-with-gles-1.frag.html shader-with-gles-symbol.frag.html shader-with-glprojectionmatrix.vert.html shader-with-implicit-vec3-to-vec4-cast.vert.html shader-with-include.vert.html shader-with-int-return-value.frag.html shader-with-invalid-identifier.frag.html shader-with-ivec2-return-value.frag.html shader-with-ivec3-return-value.frag.html shader-with-ivec4-return-value.frag.html shader-with-limited-indexing.frag.html -shader-with-line-directive.html +# we can not check line directives because GLSL 1.0.17 says error messages +# are implementation defined. +#shader-with-line-directive.html +--min-version 1.0.2 shader-with-hex-int-constant-macro.html shader-with-long-line.html shader-with-non-ascii-error.frag.html shader-with-precision.frag.html shader-with-quoted-error.frag.html shader-with-undefined-preprocessor-symbol.frag.html shader-with-uniform-in-loop-condition.vert.html shader-with-vec2-return-value.frag.html shader-with-vec3-return-value.frag.html shader-with-vec4-return-value.frag.html +--min-version 1.0.2 shader-with-vec4-vec3-vec4-conditional.html shader-with-version-100.frag.html shader-with-version-100.vert.html shader-with-version-120.vert.html shader-with-version-130.vert.html shader-with-webgl-identifier.vert.html shader-without-precision.frag.html shared.html struct-nesting-exceeds-maximum.html
--- a/content/canvas/test/webgl/conformance/glsl/misc/attrib-location-length-limits.html +++ b/content/canvas/test/webgl/conformance/glsl/misc/attrib-location-length-limits.html @@ -55,18 +55,18 @@ var attribLoc = gl.getAttribLocation(pro if (attribLoc == -1) { testFailed("attrib location was -1, should not be"); } else { testPassed("attrib location should not be -1"); } wtu.glErrorShouldBe(gl, gl.NONE); debug("Test attrib location over the length limit"); -debug("Shader compilation should fail"); -shouldBe('wtu.loadShaderFromScript(gl, "badVertexShader", gl.VERTEX_SHADER, function (err) {})', 'null'); +debug("Shader compilation or link should fail"); +shouldBe('wtu.loadProgramFromScriptExpectError(gl, "badVertexShader", "fragmentShader")', 'null'); wtu.glErrorShouldBe(gl, gl.NONE); debug("Attempt to bind too-long attrib location should produce error"); program = gl.createProgram(); gl.bindAttribLocation(program, 0, "vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567"); wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); debug("Attempt to fetch too-long attrib location should produce error");
--- a/content/canvas/test/webgl/conformance/glsl/misc/glsl-long-variable-names.html +++ b/content/canvas/test/webgl/conformance/glsl/misc/glsl-long-variable-names.html @@ -8,125 +8,242 @@ <script src="../../resources/webgl-test.js"> </script> </head> <body> <canvas id="example" width="50" height="50"> There is supposed to be an example drawing here, but it's not important. </canvas> <div id="description"></div> <div id="console"></div> - <script id="vshader" type="x-shader/x-vertex"> - attribute vec4 vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456; - varying float alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890; + <script id="vshader_shared_uniform" type="x-shader/x-vertex"> + attribute vec3 vPosition; + uniform float value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890; void main() { - alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = 1.0; - gl_Position = vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456; + gl_Position = vec4(vPosition, value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890); + } + </script> + + <script id="fshader_shared_uniform" type="x-shader/x-fragment"> + precision mediump float; + uniform float value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890; + void main() + { + gl_FragColor = vec4(1.0, 0.0, value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, 1.0); + } + </script> + + <script id="vshader_uniform_array" type="x-shader/x-vertex"> + attribute vec3 vPosition; + void main() + { + gl_Position = vec4(vPosition, 1.0); } </script> - <script id="fshader" type="x-shader/x-fragment"> + <script id="fshader_uniform_array" type="x-shader/x-fragment"> + precision mediump float; + uniform float color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[2]; + void main() + { + gl_FragColor = vec4(color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0], color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1], 1.0, 1.0); + } + </script> + + <script id="vshader_varying" type="x-shader/x-vertex"> + attribute vec3 vPosition; + varying float value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890; + void main() + { + value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = 1.0; + gl_Position = vec4(vPosition, 1.0); + } + </script> + + <script id="fshader_varying" type="x-shader/x-fragment"> precision mediump float; - varying float alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890; - uniform float color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[3]; + varying float value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890; + void main() + { + gl_FragColor = vec4(value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, 0.0, 1.0, 1.0); + } + </script> + + <script id="vshader_local" type="x-shader/x-vertex"> + attribute vec3 vPosition; + void main() + { + for (int i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 = 0; i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 < 1; ++i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234) + { + gl_Position = vec4(vPosition, 1.0); + } + } + </script> + + <script id="fshader_local" type="x-shader/x-fragment"> + precision mediump float; void main() { for (int i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 = 0; i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 < 1; ++i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234) { - gl_FragColor = vec4(color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0], color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1], color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[2], alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890); + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); } } </script> + <script id="vshader_attrib" type="x-shader/x-vertex"> + attribute vec3 vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456; + void main() + { + gl_Position = vec4(vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456, 1.0); + } + </script> + + <script id="fshader_attrib" type="x-shader/x-fragment"> + precision mediump float; + void main() + { + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); + } + </script> + <script> - function fail(x,y, buf, shouldBe) + if (window.initNonKhronosFramework) { + window.initNonKhronosFramework(false); + } + + description("Verify that shader long variable names works fine if they are within 256 characters."); + + debug("Test same long uniform name in both vertex shader and fragment shader"); + var gl = initWebGL("example", "vshader_shared_uniform", "fshader_shared_uniform", [ "vPosition"], [ 0, 0, 0, 1 ], 1); + shouldBeNonNull("gl"); + shouldBe("gl.getError()", "gl.NO_ERROR"); + var prog = gl.getParameter(gl.CURRENT_PROGRAM); + shouldBeNonNull("prog"); + var valueLoc = gl.getUniformLocation(prog, "value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + shouldBeNonNull("valueLoc"); + shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_UNIFORMS)", "1"); + var activeUniform = gl.getActiveUniform(prog, 0); + shouldBeNonNull("activeUniform"); + shouldBe("activeUniform.type", "gl.FLOAT"); + shouldBe("activeUniform.size", "1"); + shouldBe("activeUniform.name", "'value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'"); + gl.uniform1f(valueLoc, 1.0); + drawAndCheckPixels(gl); + shouldBe("gl.getError()", "gl.NO_ERROR"); + debug(""); + + debug("Test long uniform array name"); + var gl = initWebGL("example", "vshader_uniform_array", "fshader_uniform_array", [ "vPosition"], [ 0, 0, 0, 1 ], 1); + shouldBeNonNull("gl"); + shouldBe("gl.getError()", "gl.NO_ERROR"); + var prog = gl.getParameter(gl.CURRENT_PROGRAM); + shouldBeNonNull("prog"); + var redLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]"); + shouldBeNonNull("redLoc"); + var greenLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1]"); + shouldBeNonNull("greenLoc"); + shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_UNIFORMS)", "1"); + var activeUniform = gl.getActiveUniform(prog, 0); + shouldBeNonNull("activeUniform"); + shouldBe("activeUniform.type", "gl.FLOAT"); + shouldBe("activeUniform.size", "2"); + shouldBe("activeUniform.name", "'color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]'"); + gl.uniform1f(redLoc, 1.0); + gl.uniform1f(greenLoc, 0.0); + drawAndCheckPixels(gl); + shouldBe("gl.getError()", "gl.NO_ERROR"); + debug(""); + + debug("Test long varying name"); + var gl = initWebGL("example", "vshader_varying", "fshader_varying", [ "vPosition"], [ 0, 0, 0, 1 ], 1); + shouldBeNonNull("gl"); + shouldBe("gl.getError()", "gl.NO_ERROR"); + var prog = gl.getParameter(gl.CURRENT_PROGRAM); + shouldBeNonNull("prog"); + drawAndCheckPixels(gl); + shouldBe("gl.getError()", "gl.NO_ERROR"); + debug(""); + + debug("Test long local variable name"); + var gl = initWebGL("example", "vshader_varying", "fshader_varying", [ "vPosition"], [ 0, 0, 0, 1 ], 1); + shouldBeNonNull("gl"); + shouldBe("gl.getError()", "gl.NO_ERROR"); + var prog = gl.getParameter(gl.CURRENT_PROGRAM); + shouldBeNonNull("prog"); + drawAndCheckPixels(gl); + shouldBe("gl.getError()", "gl.NO_ERROR"); + debug(""); + + debug("Test long attribute name"); + var gl = initWebGL("example", "vshader_attrib", "fshader_attrib", [ "vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"], [ 0, 0, 0, 1 ], 1); + shouldBeNonNull("gl"); + shouldBe("gl.getError()", "gl.NO_ERROR"); + var prog = gl.getParameter(gl.CURRENT_PROGRAM); + shouldBeNonNull("prog"); + shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_ATTRIBUTES)", "1"); + var activeAttrib = gl.getActiveAttrib(prog, 0); + shouldBeNonNull("activeAttrib"); + shouldBe("activeAttrib.size", "1"); + shouldBe("activeAttrib.type", "gl.FLOAT_VEC3"); + shouldBe("activeAttrib.name", "'vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456'"); + drawAndCheckPixels(gl); + shouldBe("gl.getError()", "gl.NO_ERROR"); + debug(""); + + + function fail(x, y, buf, shouldBe) { var i = (y*50+x) * 4; var reason = "pixel at ("+x+","+y+") is ("+buf[i]+","+buf[i+1]+","+buf[i+2]+","+buf[i+3]+"), should be "+shouldBe; testFailed(reason); } function pass() { testPassed("drawing is correct"); } - if (window.initNonKhronosFramework) { - window.initNonKhronosFramework(false); - } - - description("Verify that shader long variable names works fine if they are within 256 characters."); - - gl = initWebGL("example", "vshader", "fshader", [ "vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"], [ 0, 0, 0, 1 ], 1); - - var prog = gl.getParameter(gl.CURRENT_PROGRAM); - shouldBeNonNull(prog); - var redLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]"); - shouldBeNonNull(redLoc); - var greenLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1]"); - shouldBeNonNull(greenLoc); - var blueLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[2]"); - shouldBeNonNull(blueLoc); - - shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_ATTRIBUTES)", "1"); - var activeAttrib = gl.getActiveAttrib(prog, 0); - shouldBeNonNull(activeAttrib); - shouldBe("activeAttrib.size", "1"); - shouldBe("activeAttrib.type", "gl.FLOAT_VEC4"); - shouldBe("activeAttrib.name", "'vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456'"); + function drawAndCheckPixels(gl) + { + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); - shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_UNIFORMS)", "1"); - var activeUniform = gl.getActiveUniform(prog, 0); - shouldBeNonNull(activeUniform); - shouldBe("activeUniform.size", "3"); - shouldBe("activeUniform.type", "gl.FLOAT"); - shouldBe("activeUniform.name", "'color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]'"); - - gl.uniform1f(redLoc, 1.0); - gl.uniform1f(greenLoc, 0.0); - gl.uniform1f(blueLoc, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 3); - var vertexObject = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW); - gl.enableVertexAttribArray(0); - gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + var buf = new Uint8Array(50 * 50 * 4); + gl.readPixels(0, 0, 50, 50, gl.RGBA, gl.UNSIGNED_BYTE, buf); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.drawArrays(gl.TRIANGLES, 0, 3); - - var buf = new Uint8Array(50 * 50 * 4); - gl.readPixels(0, 0, 50, 50, gl.RGBA, gl.UNSIGNED_BYTE, buf); - shouldBe("gl.getError()", "gl.NO_ERROR"); - - function checkPixels() - { // Test several locations // First line should be all black for (var i = 0; i < 50; ++i) if (buf[i*4] != 0 || buf[i*4+1] != 0 || buf[i*4+2] != 0 || buf[i*4+3] != 255) { fail(i, 0, buf, "(0,0,0,255)"); return; } - // Line 15 should be red for at least 10 red pixels starting 20 pixels in + // Line 15 should be magenta for at least 10 pixels starting 20 pixels in var offset = (15*50+20) * 4; for (var i = 0; i < 10; ++i) if (buf[offset+i*4] != 255 || buf[offset+i*4+1] != 0 || buf[offset+i*4+2] != 255 || buf[offset+i*4+3] != 255) { fail(20 + i, 15, buf, "(255,0,255,255)"); return; } // Last line should be all black offset = (49*50) * 4; for (var i = 0; i < 50; ++i) if (buf[offset+i*4] != 0 || buf[offset+i*4+1] != 0 || buf[offset+i*4+2] != 0 || buf[offset+i*4+3] != 255) { fail(i, 49, buf, "(0,0,0,255)"); return; } pass(); } - checkPixels(); + successfullyParsed = true; </script> <script src="../../../resources/js-test-post.js"></script> </body> </html>
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-conditional-scoping.html @@ -0,0 +1,47 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL GLSL Conformance Tests</title> +<link rel="stylesheet" href="../../../resources/js-test-style.css"/> +<script src="../../../resources/js-test-pre.js"></script> +<script src="../../resources/webgl-test-utils.js"></script> +<script src="../../resources/glsl-conformance-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script id="fragmentShader" type="text/something-not-javascript"> +// fragment shader with conditional scoping should succeed +precision mediump float; +void main() { + int k = 3; + + if (true) int g = k = 4; + else int q = k = 5; + + g = 3; + q = 4; + + if (true) int g = 4; + else int k = 10; + + if (true) { int g = 10; } + else { int k = 20; } + + gl_FragColor = vec4(1.); +} +</script> +<script> +GLSLConformanceTester.runTest(); +successfullyParsed = true; +</script> +</body> +</html> + +
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-do-scoping.html @@ -0,0 +1,35 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL GLSL Conformance Tests</title> +<link rel="stylesheet" href="../../../resources/js-test-style.css"/> +<script src="../../../resources/js-test-pre.js"></script> +<script src="../../resources/webgl-test-utils.js"></script> +<script src="../../resources/glsl-conformance-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script id="fragmentShader" type="text/something-not-javascript"> +// fragment shader with do scoping should succeed +precision mediump float; +void main() { + int k = 0; + do int k = 1; while (k != 0); // ok, do always introduces scope + gl_FragColor = vec4(float(k)); +} +</script> +<script> +GLSLConformanceTester.runTest(); +successfullyParsed = true; +</script> +</body> +</html> + +
--- a/content/canvas/test/webgl/conformance/glsl/misc/shader-with-error-directive.html +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-error-directive.html @@ -28,20 +28,22 @@ void main() description("Checks shader with error directive"); var wtu = WebGLTestUtils; GLSLConformanceTester.runTests([ { vShaderId: undefined, vShaderSuccess: true, fShaderId: 'fshaderWithErrorDirective', fShaderSuccess: false, - fShaderTest: (function() { - return wtu.getLastError().indexOf("testing123 testing123") >= 0; }), + // We can't test for the actual error message as + // GLSL 1.0.17 11 says the messages are implementation dependant. + //fShaderTest: (function() { + // return wtu.getLastError().indexOf("testing123 testing123") >= 0; }), linkSuccess: false, - passMsg: "error directive returns error user's error message", + passMsg: "error directive causes error", }, ]); debug(""); successfullyParsed = true; </script> </body> </html>
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-for-loop.html @@ -0,0 +1,81 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL GLSL Conformance Tests</title> +<link rel="stylesheet" href="../../../resources/js-test-style.css"/> +<script src="../../../resources/js-test-pre.js"></script> +<script src="../../resources/webgl-test-utils.js"></script> +<script src="../../resources/glsl-conformance-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script id="fragmentShader" type="text/something-not-javascript"> +// fragment shader with for loop should succeed + +// TODO(gman): trim to min size to test bug. +precision highp float; +uniform float time; +uniform vec2 resolution; + +// Saw-tooth function that is synced with the demo music (128bpm) +float gBeat=fract(time*3.2/3.); + +// Calculate the surface color +vec3 surfColor(vec2 p) +{ + vec2 q=vec2(sin(.08*p.x),4.*p.y); + vec3 c=vec3(0); + for(float i=0.;i<15.;i++) + c+=(1.+sin(i*sin(time)+vec3(0.,1.3,2.2)))*.2/length(q-vec2(sin(i),12.*sin(.3*time+i))); + return c+vec3(mix(mod(floor(p.x*.2)+floor(p.y*2.2),2.),.2,gBeat)); +} + +// Ray trace (cylinder) +vec3 trace(vec3 o,vec3 d) +{ + d.y*=.65+.1*sin(.5*time); + float D=1./(d.y*d.y+d.z*d.z), + a=(o.y*d.y+o.z*d.z)*D, + b=(o.y*o.y+o.z*o.z-36.)*D, + t=-a-sqrt(a*a-b); + o+=t*d; + return surfColor(vec2(o.x,atan(o.y,o.z)))*(1.+.01*t); +} + +void main() +{ + // Screen setup + vec2 p=(2.*gl_FragCoord.xy-resolution)/resolution.y, + q=2.*gl_FragCoord.xy/resolution-1.; + + // Camera setup + vec3 cp=vec3(-time*20.+1.,1.6*sin(time*1.2),2.+2.*cos(time*.3)), + ct=cp+vec3(1.,.3*cos(time),-.2), + cd=normalize(ct-cp), + cr=normalize(cross(cd,vec3(.5*cos(.3*time),0.,1.))), + cu=cross(cr,cd), + rd=normalize(2.*cd+cr*p.x+cu*p.y); + + // Trace! (+some funky lens/raster effects) + vec3 c=trace(cp,rd)* + min(1.,1.8-dot(q,q))* + (.9+.1*sin(3.*sin(gBeat)*gl_FragCoord.y)); + + gl_FragColor=vec4(c,1); +} +</script> +<script> +GLSLConformanceTester.runTest(); +successfullyParsed = true; +</script> +</body> +</html> + +
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-for-scoping.html @@ -0,0 +1,35 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL GLSL Conformance Tests</title> +<link rel="stylesheet" href="../../../resources/js-test-style.css"/> +<script src="../../../resources/js-test-pre.js"></script> +<script src="../../resources/webgl-test-utils.js"></script> +<script src="../../resources/glsl-conformance-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script id="fragmentShader" type="text/something-not-javascript"> +// fragment shader with for scoping should succeed +precision mediump float; +void main() { + int k = 0; + for (int i = 0; i < 10; i++) { int i = k+i; } // ok, compound nests + gl_FragColor = vec4(float(k)); +} +</script> +<script> +GLSLConformanceTester.runTest(); +successfullyParsed = true; +</script> +</body> +</html> + +
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-function-scoped-struct.html @@ -0,0 +1,39 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL GLSL Conformance Tests</title> +<link rel="stylesheet" href="../../../resources/js-test-style.css"/> +<script src="../../../resources/js-test-pre.js"></script> +<script src="../../resources/webgl-test-utils.js"></script> +<script src="../../resources/glsl-conformance-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script id="fragmentShader" type="text/something-not-javascript"> +// fragment shader with private function scoped struct should fail. +precision mediump float; +int fun2(struct s { int m; } g) { return g.m; } + +s a; + +void main() { + int e = fun2(s(3)); + + gl_FragColor = vec4(1.0); +} +</script> +<script> +GLSLConformanceTester.runTest(); +successfullyParsed = true; +</script> +</body> +</html> + +
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-functional-scoping.html @@ -0,0 +1,38 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL GLSL Conformance Tests</title> +<link rel="stylesheet" href="../../../resources/js-test-style.css"/> +<script src="../../../resources/js-test-pre.js"></script> +<script src="../../resources/webgl-test-utils.js"></script> +<script src="../../resources/glsl-conformance-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script id="fragmentShader" type="text/something-not-javascript"> +// fragment shader with functional scoping should succeed +precision mediump float; +int f(int k) { + int k = k + 3; + return k; +} + +void main() { + gl_FragColor = vec4(f(100)); +} +</script> +<script> +GLSLConformanceTester.runTest(); +successfullyParsed = true; +</script> +</body> +</html> + +
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-hex-int-constant-macro.html @@ -0,0 +1,37 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL GLSL Conformance Tests</title> +<link rel="stylesheet" href="../../../resources/js-test-style.css"/> +<script src="../../../resources/js-test-pre.js"></script> +<script src="../../resources/webgl-test-utils.js"></script> +<script src="../../resources/glsl-conformance-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script id="vertexShader" type="text/something-not-javascript"> +// vertex shader uses the long integer constant should succeed +attribute vec4 vPosition; +void main() +{ + #define TEST 0x1F + int a = TEST; + + gl_Position = vPosition; +} +</script> +<script> +GLSLConformanceTester.runTest(); +successfullyParsed = true; +</script> +</body> +</html> + +
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-vec4-vec3-vec4-conditional.html @@ -0,0 +1,35 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL GLSL Conformance Tests</title> +<link rel="stylesheet" href="../../../resources/js-test-style.css"/> +<script src="../../../resources/js-test-pre.js"></script> +<script src="../../resources/webgl-test-utils.js"></script> +<script src="../../resources/glsl-conformance-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script id="fragmentShader" type="text/something-not-javascript"> +// fragment shader that vec4->vec3->vec4 conditional should succeed +precision mediump float; +uniform float x; +void main() +{ + gl_FragColor = vec4((x > 0.0 ? vec4(1.0, 1.0, 1.0, 0.0) : vec4(0.1, 0.1, 0.1, 0.0)).xyz, 1.0); +} +</script> +<script> +GLSLConformanceTester.runTest(); +successfullyParsed = true; +</script> +</body> +</html> + +
--- a/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-exceeds-maximum.html +++ b/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-exceeds-maximum.html @@ -13,18 +13,22 @@ found in the LICENSE file. <script src="../../resources/webgl-test-utils.js"></script> <script src="../../resources/glsl-conformance-test.js"></script> </head> <body> <div id="description"></div> <div id="console"></div> <script id="vertexShader" type="text/something-not-javascript"> // shader with too-deep struct nesting should fail per WebGL spec +struct nesting5 { + vec4 vector; +}; + struct nesting4 { - vec4 vector; + nesting5 field5; }; struct nesting3 { nesting4 field4; }; struct nesting2 { nesting3 field3; @@ -32,17 +36,17 @@ struct nesting2 { struct nesting1 { nesting2 field2; }; uniform nesting1 uniform1; void main() { - gl_Position = uniform1.field2.field3.field4.vector; + gl_Position = uniform1.field2.field3.field4.field5.vector; } </script> <script> GLSLConformanceTester.runTest(); successfullyParsed = true; </script> </body> </html>
--- a/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-under-maximum.html +++ b/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-under-maximum.html @@ -13,32 +13,36 @@ found in the LICENSE file. <script src="../../resources/webgl-test-utils.js"></script> <script src="../../resources/glsl-conformance-test.js"></script> </head> <body> <div id="description"></div> <div id="console"></div> <script id="vertexShader" type="text/something-not-javascript"> // shader with struct nesting less than maximum in WebGL spec should succeed +struct nesting4 { + vec4 vector; +}; + struct nesting3 { - vec4 vector; + nesting4 field4; }; struct nesting2 { nesting3 field3; }; struct nesting1 { nesting2 field2; }; uniform nesting1 uniform1; void main() { - gl_Position = uniform1.field2.field3.vector; + gl_Position = uniform1.field2.field3.field4.vector; } </script> <script> GLSLConformanceTester.runTest(); successfullyParsed = true; </script> </body> </html>
--- a/content/canvas/test/webgl/conformance/glsl/variables/gl-pointcoord.html +++ b/content/canvas/test/webgl/conformance/glsl/variables/gl-pointcoord.html @@ -36,106 +36,106 @@ found in the LICENSE file. gl_PointCoord.x, gl_PointCoord.y, 0, 1); } </script> <script> - function init() - { - if (window.initNonKhronosFramework) { - window.initNonKhronosFramework(false); - } + if (window.initNonKhronosFramework) { + window.initNonKhronosFramework(false); + } + + description("Checks gl_PointCoord and gl_PointSize"); + debug(""); - description("Checks gl_PointCoord and gl_PointSize"); - debug(""); + // NOTE: I'm not 100% confident in this test. I think it is correct. - // NOTE: I'm not 100% confident in this test. I think it is correct. - - wtu = WebGLTestUtils; - gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1); + wtu = WebGLTestUtils; + var gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1); + shouldBeNonNull("gl"); + shouldBe("gl.getError()", "gl.NO_ERROR"); - canvas = gl.canvas; - width = canvas.width; - height = canvas.height; - shouldBeTrue("width == height"); + var canvas = gl.canvas; + var width = canvas.width; + var height = canvas.height; + shouldBe("width", "height"); - maxPointSize = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)[1]; - shouldBeTrue("maxPointSize >= 1"); - shouldBeTrue("maxPointSize % 1 == 0"); + var maxPointSize = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)[1]; + shouldBeTrue("maxPointSize >= 1"); + shouldBeTrue("maxPointSize % 1 == 0"); - maxPointSize = Math.min(maxPointSize, 64); - pointWidth = maxPointSize / width; - pointStep = Math.floor(maxPointSize / 4); - pointStep = Math.max(1, pointStep); + maxPointSize = Math.min(maxPointSize, 64); + var pointWidth = maxPointSize / width; + var pointStep = Math.floor(maxPointSize / 4); + var pointStep = Math.max(1, pointStep); - program = gl.program; - pointSizeLoc = gl.getUniformLocation(program, "uPointSize"); - gl.uniform1f(pointSizeLoc, maxPointSize); + var program = gl.program; + var pointSizeLoc = gl.getUniformLocation(program, "uPointSize"); + gl.uniform1f(pointSizeLoc, maxPointSize); - var pixelOffset = (maxPointSize % 2) ? (1 / width) : 0; - var vertexObject = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); - gl.bufferData( - gl.ARRAY_BUFFER, - new Float32Array( - [-0.5 + pixelOffset, -0.5 + pixelOffset, - 0.5 + pixelOffset, -0.5 + pixelOffset, - -0.5 + pixelOffset, 0.5 + pixelOffset, - 0.5 + pixelOffset, 0.5 + pixelOffset]), - gl.STATIC_DRAW); - gl.enableVertexAttribArray(0); - gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + var pixelOffset = (maxPointSize % 2) ? (1 / width) : 0; + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array( + [-0.5 + pixelOffset, -0.5 + pixelOffset, + 0.5 + pixelOffset, -0.5 + pixelOffset, + -0.5 + pixelOffset, 0.5 + pixelOffset, + 0.5 + pixelOffset, 0.5 + pixelOffset]), + gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.drawArrays(gl.POINTS, 0, 4); - function s2p(s) { - return (s + 1.0) * 0.5 * width; - } + gl.drawArrays(gl.POINTS, 0, 4); + shouldBe("gl.getError()", "gl.NO_ERROR"); + + function s2p(s) { + return (s + 1.0) * 0.5 * width; + } - //function print(x, y) { - // var b = new Uint8Array(4); - // gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, b); - // debug("" + x + "," + y + ": " + b[0] + "," + b[1] + "," + b[2]); - //} - // - //for (var ii = 0; ii < 100; ++ii) { - // print(ii, ii); - //} + //function print(x, y) { + // var b = new Uint8Array(4); + // gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, b); + // debug("" + x + "," + y + ": " + b[0] + "," + b[1] + "," + b[2]); + //} + // + //for (var ii = 0; ii < 100; ++ii) { + // print(ii, ii); + //} - for (var py = 0; py < 2; ++py) { - for (var px = 0; px < 2; ++px) { - debug(""); - var pointX = -0.5 + px + pixelOffset; - var pointY = -0.5 + py + pixelOffset; - for (var yy = 0; yy < maxPointSize; yy += pointStep) { - for (var xx = 0; xx < maxPointSize; xx += pointStep) { - // formula for s and t from OpenGL ES 2.0 spec section 3.3 - var xw = s2p(pointX); - var yw = s2p(pointY); - //debug("xw: " + xw + " yw: " + yw); - var u = xx / maxPointSize * 2 - 1; - var v = yy / maxPointSize * 2 - 1; - var xf = Math.floor(s2p(pointX + u * pointWidth)); - var yf = Math.floor(s2p(pointY + v * pointWidth)); - //debug("xf: " + xf + " yf: " + yf); - var s = 0.5 + (xf + 0.5 - xw) / maxPointSize; - var t = 0.5 + (yf + 0.5 - yw) / maxPointSize; - //debug("s: " + s + " t: " + t); - var color = [Math.floor(s * 255), Math.floor((1 - t) * 255), 0]; - var msg = "pixel " + xf + "," + yf + " should be " + color; - wtu.checkCanvasRect(gl, xf, yf, 1, 1, color, msg, 4); - } + for (var py = 0; py < 2; ++py) { + for (var px = 0; px < 2; ++px) { + debug(""); + var pointX = -0.5 + px + pixelOffset; + var pointY = -0.5 + py + pixelOffset; + for (var yy = 0; yy < maxPointSize; yy += pointStep) { + for (var xx = 0; xx < maxPointSize; xx += pointStep) { + // formula for s and t from OpenGL ES 2.0 spec section 3.3 + var xw = s2p(pointX); + var yw = s2p(pointY); + //debug("xw: " + xw + " yw: " + yw); + var u = xx / maxPointSize * 2 - 1; + var v = yy / maxPointSize * 2 - 1; + var xf = Math.floor(s2p(pointX + u * pointWidth)); + var yf = Math.floor(s2p(pointY + v * pointWidth)); + //debug("xf: " + xf + " yf: " + yf); + var s = 0.5 + (xf + 0.5 - xw) / maxPointSize; + var t = 0.5 + (yf + 0.5 - yw) / maxPointSize; + //debug("s: " + s + " t: " + t); + var color = [Math.floor(s * 255), Math.floor((1 - t) * 255), 0]; + var msg = "pixel " + xf + "," + yf + " should be " + color; + wtu.checkCanvasRect(gl, xf, yf, 1, 1, color, msg, 4); } } } } - init(); successfullyParsed = true; </script> -<script src="../../../resources/js-test-post.js"></script> + <script src="../../../resources/js-test-post.js"></script> </body> </html>
--- a/content/canvas/test/webgl/conformance/misc/00_test_list.txt +++ b/content/canvas/test/webgl/conformance/misc/00_test_list.txt @@ -1,11 +1,13 @@ bad-arguments-test.html +--min-version 1.0.2 delayed-drawing.html error-reporting.html instanceof-test.html invalid-passed-params.html is-object.html null-object-behaviour.html +functions-returning-strings.html object-deletion-behaviour.html shader-precision-format.html type-conversion-test.html uninitialized-test.html webgl-specific.html
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/misc/delayed-drawing.html @@ -0,0 +1,63 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. + --> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL Delayed Drawing test.</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../resources/js-test-pre.js"></script> +<script src="../resources/webgl-test.js"> </script> +<script src="../resources/webgl-test-utils.js"> </script> +</head> +<body> +<canvas id="example" width="4" height="4" style="width: 40px; height: 30px;"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +description(document.title); +var wtu = WebGLTestUtils; +var canvas = document.getElementById("example"); +var gl = wtu.create3DContext(canvas); +var program = wtu.setupTexturedQuad(gl); + +glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); + +var tex = gl.createTexture(); +wtu.fillTexture(gl, tex, 5, 3, [0, 192, 128, 255]); + +var loc = gl.getUniformLocation(program, "tex"); +gl.uniform1i(loc, 0); + +gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); +gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); +gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); +gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + +drawAndCheck(); + +setTimeout(step2, 1000); + +function step2() { + drawAndCheck(); + finishTest(); +} + +function drawAndCheck() { + glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors before drawing."); + wtu.drawQuad(gl); + glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from drawing."); + wtu.checkCanvas( + gl, [0, 192, 128, 255], + "draw should be 0, 192, 128, 255"); +} + +successfullyParsed = true; +</script> +</body> +</html> +
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/misc/functions-returning-strings.html @@ -0,0 +1,92 @@ +<!-- +Copyright (c) 2012 Mozilla Foundation. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../resources/desktop-gl-constants.js" type="text/javascript"></script> +<script src="../../resources/js-test-pre.js"></script> +<script src="../resources/webgl-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<canvas id="canvas" width="2" height="2"> </canvas> +<script> +description("Test that functions returning strings really do return strings (and not e.g. null)"); +debug(""); + +var validVertexShaderString = + "attribute vec4 aVertex; attribute vec4 aColor; varying vec4 vColor; void main() { vColor = aColor; gl_Position = aVertex; }"; +var validFragmentShaderString = + "precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; }"; + +function shouldReturnString(_a) +{ + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + if (exception) + testFailed(_a + ' should return a string. Threw exception ' + exception); + else if (typeof _av == "string") + testPassed(_a + ' returns a string: "' + _av + '"'); + else + testFailed(_a + ' should return a string. Returns: "' + _av + '"'); +} + +var gl = create3DContext(document.getElementById("canvas")); +if (!gl) { + testFailed("context does not exist"); +} else { + var vs = gl.createShader(gl.VERTEX_SHADER); + shouldReturnString("gl.getShaderSource(vs)"); + shouldReturnString("gl.getShaderInfoLog(vs)"); + gl.shaderSource(vs, validVertexShaderString); + gl.compileShader(vs); + shouldReturnString("gl.getShaderSource(vs)"); + shouldReturnString("gl.getShaderInfoLog(vs)"); + + var fs = gl.createShader(gl.FRAGMENT_SHADER); + shouldReturnString("gl.getShaderSource(fs)"); + shouldReturnString("gl.getShaderInfoLog(fs)"); + gl.shaderSource(fs, validFragmentShaderString); + gl.compileShader(fs); + shouldReturnString("gl.getShaderSource(fs)"); + shouldReturnString("gl.getShaderInfoLog(fs)"); + + var prog = gl.createProgram(); + shouldReturnString("gl.getProgramInfoLog(prog)"); + gl.attachShader(prog, vs); + gl.attachShader(prog, fs); + gl.linkProgram(prog); + shouldReturnString("gl.getProgramInfoLog(prog)"); + + var exts = gl.getSupportedExtensions(); + for (i in exts) { + shouldReturnString("gl.getSupportedExtensions()[" + i + "]"); + } + + shouldReturnString("gl.getParameter(gl.VENDOR)"); + shouldReturnString("gl.getParameter(gl.RENDERER)"); + shouldReturnString("gl.getParameter(gl.VERSION)"); + shouldReturnString("gl.getParameter(gl.SHADING_LANGUAGE_VERSION)"); +} + +debug(""); +successfullyParsed = true; + +</script> +<script src="../../resources/js-test-post.js"></script> + +</body> +</html>
--- a/content/canvas/test/webgl/conformance/misc/invalid-passed-params.html +++ b/content/canvas/test/webgl/conformance/misc/invalid-passed-params.html @@ -132,17 +132,20 @@ context.shaderSource(fShader, "precision + "void main() {\n" + "gl_FragColor = vec4(" + validAttribName + ", 0.0, 0.0, 1.0); }"); context.compileShader(fShader); shouldBe("context.getError()", "context.NO_ERROR"); var program = context.createProgram(); context.attachShader(program, vShader); context.attachShader(program, fShader); context.linkProgram(program); -shouldBeTrue("context.getProgramParameter(program, context.LINK_STATUS)"); +var linkStatus = context.getProgramParameter(program, context.LINK_STATUS); +shouldBeTrue("linkStatus"); +if (!linkStatus) + debug(context.getProgramInfoLog(program)); shouldBe("context.getError()", "context.NO_ERROR"); context.bindAttribLocation(program, 1, validAttribName); shouldBe("context.getError()", "context.NO_ERROR"); context.getAttribLocation(program, validAttribName); shouldBe("context.getError()", "context.NO_ERROR"); context.getUniformLocation(program, validUniformName); shouldBe("context.getError()", "context.NO_ERROR");
--- a/content/canvas/test/webgl/conformance/programs/gl-bind-attrib-location-test.html +++ b/content/canvas/test/webgl/conformance/programs/gl-bind-attrib-location-test.html @@ -37,17 +37,17 @@ void main() </script> <script> description("This test ensures WebGL implementations don't allow names that start with 'gl_' when calling bindAttribLocation."); debug(""); debug("Canvas.getContext"); var gl = create3DContext(document.getElementById("canvas")); -shouldBeNonNull(gl); +shouldBeNonNull("gl"); function fail(x,y, buf, shouldBe) { var i = (y*50+x) * 4; var reason = "pixel at ("+x+","+y+") is ("+buf[i]+","+buf[i+1]+","+buf[i+2]+","+buf[i+3]+"), should be "+shouldBe; testFailed(reason); }
--- a/content/canvas/test/webgl/conformance/programs/program-test.html +++ b/content/canvas/test/webgl/conformance/programs/program-test.html @@ -76,18 +76,19 @@ function go() { assertMsg(gl.getShaderParameter(vs2, gl.COMPILE_STATUS) == true, "good vertex shader #2 should compile"); var vsBad = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vsBad, "WILL NOT COMPILE;"); gl.compileShader(vsBad); - assertMsg(gl.getShaderParameter(vsBad, gl.COMPILE_STATUS) == false, - "bad vertex shader should fail to compile"); + // GLSL 1.0.17 section 10.27. compile shader does not have to return failure. + //assertMsg(gl.getShaderParameter(vsBad, gl.COMPILE_STATUS) == false, + // "bad vertex shader should fail to compile"); var fs = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fs, "precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; }"); gl.compileShader(fs); assertMsg(gl.getShaderParameter(fs, gl.COMPILE_STATUS) == true, "good fragment shader should compile"); @@ -97,18 +98,19 @@ function go() { assertMsg(gl.getShaderParameter(fs2, gl.COMPILE_STATUS) == true, "good fragment shader #2 should compile"); var fsBad = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fsBad, "WILL NOT COMPILE;"); gl.compileShader(fsBad); - assertMsg(gl.getShaderParameter(fsBad, gl.COMPILE_STATUS) == false, - "bad fragment shader should fail to compile"); + // GLSL 1.0.17 section 10.27. compile shader does not have to return failure. + //assertMsg(gl.getShaderParameter(fsBad, gl.COMPILE_STATUS) == false, + // "bad fragment shader should fail to compile"); glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors at this point"); /////// Check attachShader() ///////////////////////////// function checkAttachShader(already_attached_shaders, shader, expected_error_code, errmsg) { var prog = gl.createProgram(); for (var i = 0; i < already_attached_shaders.length; ++i) @@ -191,18 +193,23 @@ function go() { gl.deleteShader(shaders[i]); } gl.bindAttribLocation(prog, 0, "aVertex"); gl.bindAttribLocation(prog, 1, "aColor"); gl.linkProgram(prog); if (gl.getError() != gl.NO_ERROR) assertMsg(false, "unexpected error in linkProgram()"); assertMsg(gl.getProgramParameter(prog, gl.LINK_STATUS) == expected_status, errmsg); + var infolog = gl.getProgramInfoLog(prog); + if (gl.getError() != gl.NO_ERROR) + assertMsg(false, "unexpected error in getProgramInfoLog()"); + if (typeof(infolog) != "string") + assertMsg(false, "getProgramInfoLog() did not return a string"); if (expected_status == true && gl.getProgramParameter(prog, gl.LINK_STATUS) == false) - debug(gl.getProgramInfoLog(prog)); + debug(infolog); if (gl.getError() != gl.NO_ERROR) assertMsg(false, "unexpected error in getProgramParameter()"); gl.useProgram(prog); if (expected_status == true) glErrorShouldBe(gl, gl.NO_ERROR, "using a valid program should succeed"); if (expected_status == false) glErrorShouldBe(gl, gl.INVALID_OPERATION, "using an invalid program should generate INVALID_OPERATION"); return prog; @@ -247,16 +254,20 @@ function go() { gl.drawArrays(gl.TRIANGLES, 0, 3); glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid program shouldn't generate a GL error"); gl.detachShader(progGood2, fs2); gl.attachShader(progGood2, fsBad); gl.linkProgram(progGood2); assertMsg(gl.getProgramParameter(progGood2, gl.LINK_STATUS) == false, "linking should fail with in-use formerly good program, with new bad shader attached"); + // Invalid link leaves previous valid program intact. + gl.drawArrays(gl.TRIANGLES, 0, 3); + glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid program shouldn't generate a GL error"); + gl.useProgram(progGood1); gl.drawArrays(gl.TRIANGLES, 0, 4); glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid when last used program shouldn't generate a GL error"); var progGood1 = checkLinkAndUse([vs, fs], true, true, "delete shaders after attaching them and before linking program should not affect linkProgram"); gl.useProgram(progGood1); gl.drawArrays(gl.TRIANGLES, 0, 4); glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid when last used program shouldn't generate a GL error");
--- a/content/canvas/test/webgl/conformance/reading/read-pixels-pack-alignment.html +++ b/content/canvas/test/webgl/conformance/reading/read-pixels-pack-alignment.html @@ -186,17 +186,17 @@ function runTestIteration(format, type, var bytesPerRow = width * bytesPerPixel + padding; var pos = bytesPerRow * (height - 1) + (width - 1) * bytesPerPixel; var numComponents = bytesPerPixel; for (var i = 0; i < numComponents; ++i) pixel[i] = array[pos + i]; for (var i = numComponents; i < 4; ++i) pixel[i] = 0; expectedColor = packColor(format, type, 255, 102, 0, 255); - shouldBeNonNull(expectedColor); + shouldBeNonNull("expectedColor"); shouldBe("pixel", "expectedColor"); } description('Verify readPixels() works fine with various PACK_ALIGNMENT values.'); shouldBeNonNull("gl = initWebGL('example', 'vshader', 'fshader', [ 'pos', 'colorIn' ], [ 0, 0, 0, 1 ], 1)"); gl.disable(gl.BLEND);
--- a/content/canvas/test/webgl/conformance/rendering/line-loop-tri-fan.html +++ b/content/canvas/test/webgl/conformance/rendering/line-loop-tri-fan.html @@ -66,16 +66,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE var green = [0,255,0,255]; var black = [0,0,0,255]; var isCorrect = true; for (var j = 0; j < w * w * 4; j += 4) { var correct = black; if (j < w * 4 || j > w * (w - 1) * 4 || j % (w * 4) == 0 || j % (w * 4) == (w - 1) * 4) { correct = green; } + // ignore corner pixels + if ((j == 0) || (j == 4*(w-1)) || (j == 4*w*(w-1)) || (j== 4*(w*w - 1))) { + continue; + } if (!checkPixel(buf, j, correct)) { isCorrect = false; break; } } if (isCorrect) { testPassed("Line loop was drawn correctly."); } else {
--- a/content/canvas/test/webgl/conformance/rendering/point-size.html +++ b/content/canvas/test/webgl/conformance/rendering/point-size.html @@ -6,47 +6,50 @@ found in the LICENSE file. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="../../resources/js-test-style.css"/> <script src="../../resources/js-test-pre.js"></script> <script src="../resources/webgl-test.js"></script> <script id="vshader" type="x-shader/x-vertex"> -attribute vec3 pos; -attribute vec4 colorIn; -uniform float pointSize; -varying vec4 color; + attribute vec3 pos; + attribute vec4 colorIn; + uniform float pointSize; + varying vec4 color; -void main() -{ - gl_PointSize = pointSize; - color = colorIn; - gl_Position = vec4(pos.xyz, 3.0); -} + void main() + { + gl_PointSize = pointSize; + color = colorIn; + gl_Position = vec4(pos, 1.0); + } </script> <script id="fshader" type="x-shader/x-fragment"> -precision mediump float; -varying vec4 color; - -void main() -{ - gl_FragColor = color; -} -</script> + precision mediump float; + varying vec4 color; + void main() + { + gl_FragColor = color; + } +</script> +</head> +<body> +<canvas id="testbed" width="2px" height="2px"></canvas> +<div id="description"></div> +<div id="console"></div> <script> -function runTest() -{ + description('Verify GL_VERTEX_PROGRAM_POINT_SIZE is enabled in WebGL'); + var gl = initWebGL('testbed', 'vshader', 'fshader', ['pos', 'colorIn'], [0, 0, 0, 1], 1, { antialias: false }); - if (!gl) { - testFailed('initWebGL(..) failed'); - return false; - } + shouldBeNonNull("gl"); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + gl.disable(gl.BLEND); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // The choice of (0.4, 0.4) ensures that the centers of the surrounding // pixels are not contained within the point when it is of size 1, but // that they definitely are when it is of size 2. var vertices = new Float32Array([ 0.4, 0.4, 0.0]); @@ -63,71 +66,60 @@ function runTest() gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, colorOffset); gl.enableVertexAttribArray(1); var locPointSize = gl.getUniformLocation(gl.program, 'pointSize'); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + debug('Draw a point of size 1 and verify it does not touch any other pixels.'); gl.uniform1f(locPointSize, 1.0); gl.drawArrays(gl.POINTS, 0, vertices.length / 3); var buf = new Uint8Array(2 * 2 * 4); gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, buf); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + var index = 0; + var correctColor; for (var y = 0; y < 2; ++y) { for (var x = 0; x < 2; ++x) { - var correctColor = [0, 0, 0]; + correctColor = [0, 0, 0]; if (x == 1 && y == 1) - correctColor[0] = 255; - if (buf[index] != correctColor[0] || buf[index + 1] != correctColor[1] || buf[index + 2] != correctColor[2]) { - testFailed('Drawing a point of size 1 touched pixels that should not be touched'); - return false; - } + shouldBe('buf[index]', '255'); + else + shouldBe('buf[index]', '0'); + shouldBe('buf[index + 1]', '0'); + shouldBe('buf[index + 2]', '0'); index += 4; } } gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); debug('Draw a point of size 2 and verify it fills the appropriate region.'); var pointSizeRange = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE); - if (pointSizeRange < 2.0) - return true; - - gl.uniform1f(locPointSize, 2.0); - gl.drawArrays(gl.POINTS, 0, vertices.length / 3); - gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, buf); - index = 0; - for (var y = 0; y < 2; ++y) { - for (var x = 0; x < 2; ++x) { - var correctColor = [255, 0, 0]; - if (buf[index] != correctColor[0] || buf[index + 1] != correctColor[1] || buf[index + 2] != correctColor[2]) { - testFailed('Drawing a point of size 2 failed to fill the appropriate region'); - return false; + if (pointSizeRange[1] >= 2.0) { + gl.uniform1f(locPointSize, 2.0); + gl.drawArrays(gl.POINTS, 0, vertices.length / 3); + gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, buf); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + index = 0; + for (var y = 0; y < 2; ++y) { + for (var x = 0; x < 2; ++x) { + shouldBe('buf[index]', '255'); + shouldBe('buf[index + 1]', '0'); + shouldBe('buf[index + 2]', '0'); + index += 4; } - index += 4; } } - return true; -} -</script> -</head> -<body> -<canvas id="testbed" width="2px" height="2px"></canvas> -<div id="description"></div> -<div id="console"></div> -<script> -description('Verify GL_VERTEX_PROGRAM_POINT_SIZE is enabled in WebGL'); - -if (runTest()) - testPassed(""); - -successfullyParsed = true; + successfullyParsed = true; </script> <script src="../../resources/js-test-post.js"></script> </body> </html>
--- a/content/canvas/test/webgl/conformance/resources/glsl-conformance-test.js +++ b/content/canvas/test/webgl/conformance/resources/glsl-conformance-test.js @@ -66,20 +66,21 @@ function runOneTest(gl, info) { if (!vShader) { vShader = wtu.loadShader(gl, vSource, gl.VERTEX_SHADER); if (info.vShaderTest) { if (!info.vShaderTest(vShader)) { testFailed("[vertex shader test] " + passMsg); return; } } - if ((vShader != null) != info.vShaderSuccess) { + // As per GLSL 1.0.17 10.27 we can only check for success on + // compileShader, not failure. + if (info.vShaderSuccess && !vShader) { testFailed("[unexpected vertex shader compile status] (expected: " + - info.vShaderSuccess + ") " + passMsg); - return; + info.vShaderSuccess + ") " + passMsg); } // Save the shaders so we test shared shader. if (vShader) { vShaderDB[vSource] = vShader; } } var fSource = info.fShaderPrep ? info.fShaderPrep(info.fShaderSource) : @@ -91,17 +92,19 @@ function runOneTest(gl, info) { fShader = wtu.loadShader(gl, fSource, gl.FRAGMENT_SHADER); if (info.fShaderTest) { if (!info.fShaderTest(fShader)) { testFailed("[fragment shdaer test] " + passMsg); return; } } //debug(fShader == null ? "fail" : "succeed"); - if ((fShader != null) != info.fShaderSuccess) { + // As per GLSL 1.0.17 10.27 we can only check for success on + // compileShader, not failure. + if (info.fShaderSuccess && !fShader) { testFailed("[unexpected fragment shader compile status] (expected: " + info.fShaderSuccess + ") " + passMsg); return; } // Safe the shaders so we test shared shader. if (fShader) { fShaderDB[fSource] = fShader; }
--- a/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js +++ b/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js @@ -582,26 +582,27 @@ var glErrorShouldBe = function(gl, glErr /** * Links a WebGL program, throws if there are errors. * @param {!WebGLContext} gl The WebGLContext to use. * @param {!WebGLProgram} program The WebGLProgram to link. * @param {function(string): void) opt_errorCallback callback for errors. */ var linkProgram = function(gl, program, opt_errorCallback) { + errFn = opt_errorCallback || testFailed; // Link the program gl.linkProgram(program); // Check the link status var linked = gl.getProgramParameter(program, gl.LINK_STATUS); if (!linked) { // something went wrong with the link var error = gl.getProgramInfoLog (program); - testFailed("Error in program linking:" + error); + errFn("Error in program linking:" + error); gl.deleteProgram(program); } }; /** * Sets up WebGL with shaders. * @param {string} canvasName The id of the canvas. @@ -926,17 +927,17 @@ var getScript = function(scriptId) { * @return {!WebGLShader} The created shader. */ var loadShaderFromScript = function( gl, scriptId, opt_shaderType, opt_errorCallback) { var shaderSource = ""; var shaderType; var shaderScript = document.getElementById(scriptId); if (!shaderScript) { - throw("*** Error: unknown script element" + scriptId); + throw("*** Error: unknown script element " + scriptId); } shaderSource = shaderScript.text; if (!opt_shaderType) { if (shaderScript.type == "x-shader/x-vertex") { shaderType = gl.VERTEX_SHADER; } else if (shaderScript.type == "x-shader/x-fragment") { shaderType = gl.FRAGMENT_SHADER; @@ -1027,16 +1028,51 @@ var loadProgram = function( gl.attachShader( program, loadShader( gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback)); linkProgram(gl, program, opt_errorCallback); return program; }; +/** + * Loads shaders from source, creates a program, attaches the shaders and + * links but expects error. + * + * GLSL 1.0.17 10.27 effectively says that compileShader can + * always succeed as long as linkProgram fails so we can't + * rely on compileShader failing. This function expects + * one of the shader to fail OR linking to fail. + * + * @param {!WebGLContext} gl The WebGLContext to use. + * @param {string} vertexShaderScriptId The vertex shader. + * @param {string} fragmentShaderScriptId The fragment shader. + * @return {WebGLProgram} The created program. + */ +var loadProgramFromScriptExpectError = function( + gl, vertexShaderScriptId, fragmentShaderScriptId) { + var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId); + if (!vertexShader) { + return null; + } + var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId); + if (!fragmentShader) { + return null; + } + var linkSuccess = true; + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + linkSuccess = true; + linkProgram(gl, program, function() { + linkSuccess = false; + }); + return linkSuccess ? program : null; +}; + var basePath; var getBasePath = function() { if (!basePath) { var expectedBase = "webgl-test-utils.js"; var scripts = document.getElementsByTagName('script'); for (var script, i = 0; script = scripts[i]; i++) { var src = script.src; var l = src.length; @@ -1139,16 +1175,17 @@ return { glEnumToString: glEnumToString, glErrorShouldBe: glErrorShouldBe, fillTexture: fillTexture, loadImageAsync: loadImageAsync, loadImagesAsync: loadImagesAsync, loadProgram: loadProgram, loadProgramFromFile: loadProgramFromFile, loadProgramFromScript: loadProgramFromScript, + loadProgramFromScriptExpectError: loadProgramFromScriptExpectError, loadShader: loadShader, loadShaderFromFile: loadShaderFromFile, loadShaderFromScript: loadShaderFromScript, loadStandardProgram: loadStandardProgram, loadStandardVertexShader: loadStandardVertexShader, loadStandardFragmentShader: loadStandardFragmentShader, loadTextFileAsync: loadTextFileAsync, loadTexture: loadTexture,
--- a/content/canvas/test/webgl/conformance/textures/00_test_list.txt +++ b/content/canvas/test/webgl/conformance/textures/00_test_list.txt @@ -1,8 +1,9 @@ +compressed-tex-image.html copy-tex-image-and-sub-image-2d.html gl-pixelstorei.html gl-teximage.html origin-clean-conformance.html tex-image-and-sub-image-2d-with-array-buffer-view.html tex-image-and-sub-image-2d-with-canvas.html tex-image-and-sub-image-2d-with-image-data.html tex-image-and-sub-image-2d-with-image.html
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/conformance/textures/compressed-tex-image.html @@ -0,0 +1,61 @@ +<!-- +Copyright (c) 2012 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. + --> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL CompressedTexImage and CompressedTexSubImage Tests</title> +<LINK rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../resources/js-test-pre.js"></script> +<script src="../resources/webgl-test.js"></script> +<script src="../resources/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +description("This test ensures WebGL implementations correctly implement compressedTexImage2D and compressedTexSubImage2D."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.createElement("canvas"); +var gl = wtu.create3DContext(canvas); + +const COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; +const COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; +const COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; +const ETC1_RGB8_OES = 0x8D64; +const COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; +const COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; + +var formats = null; + +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))"); + shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))"); + shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT5_EXT, 4, 4, 0, new Uint8Array(16))"); + shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ETC1_RGB8_OES, 4, 4, 0, new Uint8Array(8))"); + shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))"); + shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))"); + + shouldGenerateGLError(gl, gl.NO_ERROR, "formats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)"); + shouldBeNonNull("formats"); + shouldBe("formats.length", "0"); +} + +successfullyParsed = true; +</script> +<script src="../../resources/js-test-post.js"></script> +</body> +</html>
--- a/content/canvas/test/webgl/conformance/textures/texture-size-cube-maps.html +++ b/content/canvas/test/webgl/conformance/textures/texture-size-cube-maps.html @@ -144,16 +144,17 @@ function testSize(size) { gl.texParameteri( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_CUBE_MAP); } var err = gl.getError(); if (err == gl.OUT_OF_MEMORY) { + debug("out of memory"); return false; } if (err != gl.NO_ERROR) { testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err)); } for (var rr = 0; rr < rotations.length; ++rr) {
--- a/content/canvas/test/webgl/conformance/textures/texture-size.html +++ b/content/canvas/test/webgl/conformance/textures/texture-size.html @@ -91,17 +91,17 @@ function runTest() { } } if (size <= maxCubeMapSize) { gl.useProgram(programCubeMap); if (!checkTexture(size, size, true)) { return false; } } - return true; + return true; } if (doTest()) { ++power; setTimeout(runTest, 100); } else { finishTest(); } @@ -115,30 +115,32 @@ function checkTexture(width, height, cub var type = cubeMap ? "cube map" : "2D texture"; gl.bindTexture(target, tex); gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); fillLevel(0, width, height, color.rgba, cubeMap); var err = gl.getError(); if (err == gl.OUT_OF_MEMORY) { - return false; + debug("out of memory"); + return false; } if (err != gl.NO_ERROR) { - testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err)); + testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err)); } wtu.drawQuad(gl); wtu.checkCanvas(gl, color.rgba, type + " of size " + width + "x" + height + " with no mips should draw with " + color.name); count = (count + 1) % colors.length; color = colors[count]; fillLevel(0, width, height, color.rgba, cubeMap); gl.generateMipmap(target); var err = gl.getError(); if (err == gl.OUT_OF_MEMORY) { + debug("out of memory"); return false; } if (err != gl.NO_ERROR) { testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err)); } gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); wtu.drawQuad(gl); wtu.checkCanvas(gl, color.rgba, @@ -177,17 +179,17 @@ function fillLevel(level, width, height, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z] : [gl.TEXTURE_2D]; for (var ii = 0; ii < targets.length; ++ii) { - // debug(wtu.glEnumToString(gl, targets[ii])); + // debug(wtu.glEnumToString(gl, targets[ii])); gl.texImage2D( targets[ii], level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels); } } glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
--- a/content/canvas/test/webgl/conformance/typedarrays/array-unit-tests.html +++ b/content/canvas/test/webgl/conformance/typedarrays/array-unit-tests.html @@ -85,16 +85,68 @@ function assert(prefix, expected) { function printSummary() { if (allPassed) { debug("Test passed."); } else { debug("TEST FAILED"); } } +var byteLength; +var subBuffer; +function testSlice() { + function test(subBuf, starts, size) { + byteLength = size; + subBuffer = eval(subBuf); + var subArray = new Int8Array(subBuffer); + assertEq(subBuf, subBuffer.byteLength, byteLength); + for (var i = 0; i < size; ++i) + assertEq('Element ' + i, starts + i, subArray[i]); + } + + try { + running('testSlice'); + var buffer = new ArrayBuffer(32); + var array = new Int8Array(buffer); + for (var i = 0; i < 32; ++i) + array[i] = i; + + test("buffer.slice(0)", 0, 32); + test("buffer.slice(16)", 16, 16); + test("buffer.slice(24)", 24, 8); + test("buffer.slice(32)", 32, 0); + test("buffer.slice(40)", 32, 0); + test("buffer.slice(80)", 32, 0); + + test("buffer.slice(-8)", 24, 8); + test("buffer.slice(-16)", 16, 16); + test("buffer.slice(-24)", 8, 24); + test("buffer.slice(-32)", 0, 32); + test("buffer.slice(-40)", 0, 32); + test("buffer.slice(-80)", 0, 32); + + test("buffer.slice(0, 32)", 0, 32); + test("buffer.slice(0, 16)", 0, 16); + test("buffer.slice(8, 24)", 8, 16); + test("buffer.slice(16, 32)", 16, 16); + test("buffer.slice(24, 16)", 24, 0); + + test("buffer.slice(16, -8)", 16, 8); + test("buffer.slice(-20, 30)", 12, 18); + + test("buffer.slice(-8, -20)", 24, 0); + test("buffer.slice(-20, -8)", 12, 12); + test("buffer.slice(-40, 16)", 0, 16); + test("buffer.slice(-40, 40)", 0, 32); + pass(); + } catch (e) { + fail(e); + } +} + // // Tests for unsigned array variants // function testSetAndGet10To1(type, name) { running('test ' + name + ' SetAndGet10To1'); try { var array = new type(10); @@ -197,17 +249,21 @@ function testConstructWithTypedArrayOfSi function testIntegralArrayTruncationBehavior(type, name, unsigned) { running('test integral array truncation behavior for ' + name); var sourceData; var expectedResults; if (unsigned) { sourceData = [0.6, 10.6]; - expectedResults = [0, 10]; + if (type === Uint8ClampedArray) { + expectedResults = [1, 11]; + } else { + expectedResults = [0, 10]; + } } else { sourceData = [0.6, 10.6, -0.6, -10.6]; expectedResults = [0, 10, 0, -10]; } var numIterations = 10; var array = new type(numIterations); @@ -244,16 +300,24 @@ function testIntegralArrayTruncationBeha case Uint8Array: for (var ii = 0; ii < sourceData.length; ++ii) { for (var jj = 0; jj < numIterations; ++jj) { array[jj] = sourceData[ii]; assertEq('Storing ' + sourceData[ii], expectedResults[ii], array[jj]); } } break; + case Uint8ClampedArray: + for (var ii = 0; ii < sourceData.length; ++ii) { + for (var jj = 0; jj < numIterations; ++jj) { + array[jj] = sourceData[ii]; + assertEq('Storing ' + sourceData[ii], expectedResults[ii], array[jj]); + } + } + break; case Uint16Array: for (var ii = 0; ii < sourceData.length; ++ii) { for (var jj = 0; jj < numIterations; ++jj) { array[jj] = sourceData[ii]; assertEq('Storing ' + sourceData[ii], expectedResults[ii], array[jj]); } } break; @@ -514,24 +578,28 @@ function testConstructionWithOutOfRangeV var array = new type(buffer, 4, 0x3FFFFFFF); }, "Construction of " + name + " with out-of-range number of elements"); shouldThrowIndexSizeErr(function() { var buffer = new ArrayBuffer(4); var array = new type(buffer, 8); }, "Construction of " + name + " with out-of-range offset"); } -function testConstructionWithNegativeOutOfRangeValues(type, name, elementSizeInBytes) { - if (elementSizeInBytes > 1) { - try { - var array = new type(-1); - testFailed("Construction of " + name + " with negative size should throw exception"); - } catch (e) { - testPassed("Construction of " + name + " with negative size threw exception"); - } +function testConstructionWithNegativeOutOfRangeValues(type, name) { + try { + var buffer = new ArrayBuffer(-1); + testFailed("Construction of ArrayBuffer with negative size should throw exception"); + } catch (e) { + testPassed("Construction of ArrayBuffer with negative size threw exception"); + } + try { + var array = new type(-1); + testFailed("Construction of " + name + " with negative size should throw exception"); + } catch (e) { + testPassed("Construction of " + name + " with negative size threw exception"); } shouldThrowIndexSizeErr(function() { var buffer = new ArrayBuffer(4); var array = new type(buffer, 4, -2147483648); }, "Construction of " + name + " with negative out-of-range values"); } function testConstructionWithUnalignedOffset(type, name, elementSizeInBytes) { @@ -639,55 +707,117 @@ function testSubarrayWithDefaultValues(t } catch (e) { testFailed("Subarray of " + name + " threw exception"); } } catch (e) { testFailed("Exception: " + e); } } -function testSettingFromArrayWithOutOfRangeOffset(type, name) { - var webglArray = new type(32); - var array = []; - for (var i = 0; i < 16; i++) { - array.push(i); +function setWithInvalidOffset(type, name, length, + sourceType, sourceName, sourceLength, + offset, offsetDescription) { + var webglArray = new type(length); + var sourceArray = new sourceType(sourceLength); + for (var i = 0; i < sourceLength; i++) + sourceArray[i] = 42 + i; + try { + webglArray.set(sourceArray, offset); + testFailed("Setting " + name + " from " + sourceName + " with " + + offsetDescription + " offset was not caught"); + } catch (e) { + testPassed("Setting " + name + " from " + sourceName + " with " + + offsetDescription + " offset was caught"); + } +} + +function setWithValidOffset(type, name, length, + sourceType, sourceName, sourceLength, + offset, offsetDescription) { + running("Setting " + name + " from " + sourceName + " with " + + offsetDescription + " offset"); + var webglArray = new type(length); + var sourceArray = new sourceType(sourceLength); + for (var i = 0; i < sourceLength; i++) + sourceArray[i] = 42 + i; + try { + webglArray.set(sourceArray, offset); + offset = Math.floor(offset); + for (var i = 0; i < sourceLength; i++) { + assertEq("Element " + i + offset, sourceArray[i], webglArray[i + offset]); + } + pass(); + } catch (e) { + fail(e); } - try { - webglArray.set(array, 0x7FFFFFF8); - testFailed("Setting " + name + " from array with out-of-range offset was not caught"); - } catch (e) { - testPassed("Setting " + name + " from array with out-of-range offset was caught"); - } +} + + +function testSettingFromArrayWithOutOfRangeOffset(type, name) { + setWithInvalidOffset(type, name, 32, Array, "array", 16, + 0x7FFFFFF8, "out-of-range"); +} + +function testSettingFromTypedArrayWithOutOfRangeOffset(type, name) { + setWithInvalidOffset(type, name, 32, type, name, 16, + 0x7FFFFFF8, "out-of-range"); +} + +function testSettingFromArrayWithNegativeOffset(type, name) { + setWithInvalidOffset(type, name, 32, Array, "array", 16, + -1, "negative"); +} + +function testSettingFromTypedArrayWithNegativeOffset(type, name) { + setWithInvalidOffset(type, name, 32, type, name, 16, + -1, "negative"); +} + +function testSettingFromArrayWithMinusZeroOffset(type, name) { + setWithValidOffset(type, name, 32, Array, "array", 16, + -0, "-0"); +} + +function testSettingFromTypedArrayWithMinusZeroOffset(type, name) { + setWithValidOffset(type, name, 32, type, name, 16, + -0, "-0"); +} + +function testSettingFromArrayWithBoundaryOffset(type, name) { + setWithValidOffset(type, name, 32, Array, "array", 16, + 16, "boundary"); +} + +function testSettingFromTypedArrayWithBoundaryOffset(type, name) { + setWithValidOffset(type, name, 32, type, name, 16, + 16, "boundary"); +} + +function testSettingFromArrayWithNonIntegerOffset(type, name) { + setWithValidOffset(type, name, 32, Array, "array", 16, + 16.999, "non-integer"); +} + +function testSettingFromTypedArrayWithNonIntegerOffset(type, name) { + setWithValidOffset(type, name, 32, type, name, 16, + 16.999, "non-integer"); } function testSettingFromFakeArrayWithOutOfRangeLength(type, name) { var webglArray = new type(32); var array = {}; array.length = 0x80000000; try { webglArray.set(array, 8); testFailed("Setting " + name + " from fake array with invalid length was not caught"); } catch (e) { testPassed("Setting " + name + " from fake array with invalid length was caught"); } } -function testSettingFromTypedArrayWithOutOfRangeOffset(type, name) { - var webglArray = new type(32); - var srcArray = new type(16); - for (var i = 0; i < 16; i++) { - srcArray[i] = i; - } - try { - webglArray.set(srcArray, 0x7FFFFFF8); - testFailed("Setting " + name + " from " + name + " with out-of-range offset was not caught"); - } catch (e) { - testPassed("Setting " + name + " from " + name + " with out-of-range offset was caught"); - } -} function negativeTestGetAndSetMethods(type, name) { array = new type([2, 3]); shouldBeUndefined("array.get"); var exceptionThrown = false; // We deliberately check for an exception here rather than using // shouldThrow here because the precise contents of the syntax // error are not specified. @@ -746,16 +876,22 @@ function testNaNConversion(type, name) { } break; case Uint8Array: for (var i = 0; i < array.length; ++i) { array[i] = NaN; results[i] = array[i]; } break; + case Uint8ClampedArray: + for (var i = 0; i < array.length; ++i) { + array[i] = NaN; + results[i] = array[i]; + } + break; case Uint16Array: for (var i = 0; i < array.length; ++i) { array[i] = NaN; results[i] = array[i]; } break; case Uint32Array: for (var i = 0; i < array.length; ++i) { @@ -787,16 +923,18 @@ function testNaNConversion(type, name) { // // Test driver // function runTests() { allPassed = true; + testSlice(); + // The "name" attribute is a concession to browsers which don't // implement the "name" property on function objects var testCases = [ {name: "Float32Array", unsigned: false, integral: false, elementSizeInBytes: 4, testValues: [ -500.5, 500.5 ], @@ -832,16 +970,23 @@ function runTests() { }, {name: "Uint8Array", unsigned: true, integral: true, elementSizeInBytes: 1, testValues: [ 0, 255, -1, 256 ], expectedValues: [ 0, 255, 255, 0 ] }, + {name: "Uint8ClampedArray", + unsigned: true, + integral: true, + elementSizeInBytes: 1, + testValues: [ 0, 255, -1, 256 ], + expectedValues: [ 0, 255, 0, 255 ] + }, {name: "Uint16Array", unsigned: true, integral: true, elementSizeInBytes: 2, testValues: [ 0, 65535, -1, 65536 ], expectedValues: [ 0, 65535, 65535, 0 ] }, {name: "Uint32Array", @@ -886,27 +1031,35 @@ function runTests() { testCase.testValues, testCase.expectedValues); testConstructionBoundaryConditions(type, name, testCase.testValues, testCase.expectedValues); testConstructionWithNullBuffer(type, name); testConstructionWithOutOfRangeValues(type, name); - testConstructionWithNegativeOutOfRangeValues(type, name, testCase.elementSizeInBytes); + testConstructionWithNegativeOutOfRangeValues(type, name); testConstructionWithUnalignedOffset(type, name, testCase.elementSizeInBytes); testConstructionWithUnalignedLength(type, name, testCase.elementSizeInBytes); testConstructionOfHugeArray(type, name, testCase.elementSizeInBytes); testConstructionWithBothArrayBufferAndLength(type, name, testCase.elementSizeInBytes); testConstructionWithSubPortionOfArrayBuffer(type, name, testCase.elementSizeInBytes); testSubarrayWithOutOfRangeValues(type, name, testCase.elementSizeInBytes); testSubarrayWithDefaultValues(type, name, testCase.elementSizeInBytes); testSettingFromArrayWithOutOfRangeOffset(type, name); + testSettingFromTypedArrayWithOutOfRangeOffset(type, name); + testSettingFromArrayWithNegativeOffset(type, name); + testSettingFromTypedArrayWithNegativeOffset(type, name); + testSettingFromArrayWithMinusZeroOffset(type, name); + testSettingFromTypedArrayWithMinusZeroOffset(type, name); + testSettingFromArrayWithBoundaryOffset(type, name); + testSettingFromTypedArrayWithBoundaryOffset(type, name); + testSettingFromArrayWithNonIntegerOffset(type, name); + testSettingFromTypedArrayWithNonIntegerOffset(type, name); testSettingFromFakeArrayWithOutOfRangeLength(type, name); - testSettingFromTypedArrayWithOutOfRangeOffset(type, name); negativeTestGetAndSetMethods(type, name); testNaNConversion(type, name); } printSummary(); } runTests();
--- a/content/canvas/test/webgl/dont-load-image-from-internet.patch +++ b/content/canvas/test/webgl/dont-load-image-from-internet.patch @@ -1,10 +1,10 @@ # HG changeset patch -# Parent 07fbbfde4173da7d8e513bb2d52ae07e333dbf43 +# Parent 30d84739fa6136571e995045960c3fd90b44382c diff --git a/content/canvas/test/webgl/conformance/more/functions/readPixelsBadArgs.html b/content/canvas/test/webgl/conformance/more/functions/readPixelsBadArgs.html --- a/content/canvas/test/webgl/conformance/more/functions/readPixelsBadArgs.html +++ b/content/canvas/test/webgl/conformance/more/functions/readPixelsBadArgs.html @@ -110,10 +110,10 @@ Tests.testReadPixelsSOPCanvas = function Tests.endUnit = function(gl) { }
--- a/content/canvas/test/webgl/failing_tests_linux.txt +++ b/content/canvas/test/webgl/failing_tests_linux.txt @@ -5,10 +5,9 @@ conformance/glsl/misc/shader-with-long-l conformance/misc/uninitialized-test.html conformance/programs/gl-get-active-attribute.html conformance/textures/texture-mips.html conformance/uniforms/gl-uniform-bool.html conformance/more/conformance/quickCheckAPI-S_V.html conformance/more/functions/uniformfArrayLen1.html conformance/glsl/misc/attrib-location-length-limits.html conformance/glsl/misc/uniform-location-length-limits.html -conformance/renderbuffers/framebuffer-object-attachment.html -conformance/glsl/misc/struct-nesting-exceeds-maximum.html +conformance/renderbuffers/framebuffer-object-attachment.html \ No newline at end of file
--- a/content/canvas/test/webgl/failing_tests_mac.txt +++ b/content/canvas/test/webgl/failing_tests_mac.txt @@ -3,9 +3,9 @@ conformance/glsl/misc/glsl-function-node conformance/glsl/misc/glsl-long-variable-names.html conformance/glsl/misc/shader-with-256-character-identifier.frag.html conformance/glsl/misc/shader-with-long-line.html conformance/more/conformance/quickCheckAPI-S_V.html conformance/more/functions/uniformfBadArgs.html conformance/more/functions/uniformiBadArgs.html conformance/glsl/misc/attrib-location-length-limits.html conformance/glsl/misc/uniform-location-length-limits.html -conformance/glsl/misc/struct-nesting-exceeds-maximum.html +conformance/programs/program-test.html \ No newline at end of file
--- a/content/canvas/test/webgl/failing_tests_windows.txt +++ b/content/canvas/test/webgl/failing_tests_windows.txt @@ -3,10 +3,9 @@ conformance/glsl/functions/glsl-function conformance/glsl/functions/glsl-function-atan-xy.html conformance/glsl/misc/glsl-long-variable-names.html conformance/glsl/misc/shader-with-256-character-identifier.frag.html conformance/glsl/misc/shader-with-long-line.html conformance/more/conformance/quickCheckAPI-S_V.html conformance/more/functions/uniformfArrayLen1.html conformance/glsl/misc/attrib-location-length-limits.html conformance/glsl/misc/struct-nesting-under-maximum.html -conformance/glsl/misc/uniform-location-length-limits.html -conformance/glsl/misc/struct-nesting-exceeds-maximum.html +conformance/glsl/misc/uniform-location-length-limits.html \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/content/canvas/test/webgl/fix-webgl-harness-async.patch @@ -0,0 +1,22 @@ +diff --git a/content/canvas/test/webgl/resources/webgl-test-harness.js b/content/canvas/test/webgl/resources/webgl-test-harness.js +--- a/content/canvas/test/webgl/resources/webgl-test-harness.js ++++ b/content/canvas/test/webgl/resources/webgl-test-harness.js +@@ -362,18 +362,16 @@ TestHarness.prototype.addFiles_ = functi + } + log("total files: " + files.length); + for (var ii = 0; ii < files.length; ++ii) { + log("" + ii + ": " + files[ii]); + this.files.push(new TestFile(files[ii])); + this.reportFunc(TestHarness.reportType.ADD_PAGE, files[ii], undefined); + } + this.reportFunc(TestHarness.reportType.READY, undefined, undefined); +- this.nextFileIndex = files.length; +- this.lastFileIndex = files.length; + } + + TestHarness.prototype.runTests = function(opt_start, opt_count) { + var count = opt_count || this.files.length; + this.nextFileIndex = opt_start || 0; + this.lastFileIndex = this.nextFileIndex + count; + this.startNextFile(); + };
--- a/content/canvas/test/webgl/log-more-info-about-test-failures.patch +++ b/content/canvas/test/webgl/log-more-info-about-test-failures.patch @@ -1,10 +1,10 @@ # HG changeset patch -# Parent 1910ae60536dce7272cb0478089bf40806666de8 +# Parent 0122002fbffad16de0690a21aa87d0999d0cdcb9 diff --git a/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html b/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html --- a/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html +++ b/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html @@ -117,17 +117,18 @@ function init() var bufFunction = new Uint8Array(width * height * 4); var bufMacro = new Uint8Array(width * height * 4); if (drawAndRead("canvasFunction", "vshaderFunction", bufFunction) == false ||
--- a/content/canvas/test/webgl/remove-uniqueObjectTest.patch +++ b/content/canvas/test/webgl/remove-uniqueObjectTest.patch @@ -1,14 +1,14 @@ # HG changeset patch -# Parent a41d853e5110aca4f2c77c63db502f670d0e50a1 +# Parent efec656cc42addae5aa96c6691031d54d46436fe diff --git a/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html b/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html --- a/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html +++ b/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html -@@ -107,17 +107,18 @@ if (!gl) { +@@ -115,17 +115,18 @@ if (!gl) { } else { testPassed("Successfully enabled OES_standard_derivatives extension"); runSupportedTest(true); runHintTestEnabled(); runShaderTests(true); runOutputTests();
--- a/content/canvas/test/webgl/resources/webgl-test-harness.js +++ b/content/canvas/test/webgl/resources/webgl-test-harness.js @@ -90,105 +90,290 @@ var log = function(msg) { if (window.console && window.console.log) { window.console.log(msg); } }; /** * Loads text from an external file. This function is synchronous. * @param {string} url The url of the external file. - * @return {string} the loaded text if the request is synchronous. + * @param {!function(bool, string): void} callback that is sent a bool for + * success and the string. */ -var loadTextFileSynchronous = function(url) { +var loadTextFileAsynchronous = function(url, callback) { + log ("loading: " + url); var error = 'loadTextFileSynchronous failed to load url "' + url + '"'; var request; if (window.XMLHttpRequest) { request = new XMLHttpRequest(); if (request.overrideMimeType) { request.overrideMimeType('text/plain'); } } else { throw 'XMLHttpRequest is disabled'; } - request.open('GET', url, false); - request.send(null); - if (request.readyState != 4) { - throw error; + try { + request.open('GET', url, true); + request.onreadystatechange = function() { + if (request.readyState == 4) { + var text = ''; + // HTTP reports success with a 200 status. The file protocol reports + // success with zero. HTTP does not use zero as a status code (they + // start at 100). + // https://developer.mozilla.org/En/Using_XMLHttpRequest + var success = request.status == 200 || request.status == 0; + if (success) { + text = request.responseText; + } + log("loaded: " + url); + callback(success, text); + } + }; + request.send(null); + } catch (e) { + log("failed to load: " + url); + callback(false, ''); } - return request.responseText; +}; + +/** + * Compare version strings. + */ +var greaterThanOrEqualToVersion = function(have, want) { + have = have.split(" ")[0].split("."); + want = want.split(" ")[0].split("."); + + //have 1.2.3 want 1.1 + //have 1.1.1 want 1.1 + //have 1.0.9 want 1.1 + //have 1.1 want 1.1.1 + + for (var ii = 0; ii < want.length; ++ii) { + var wantNum = parseInt(want[ii]); + var haveNum = have[ii] ? parseInt(have[ii]) : 0 + if (haveNum < wantNum) { + return false; + } + } + return true; }; -var getFileList = function(url) { +/** + * Reads a file, recursively adding files referenced inside. + * + * Each line of URL is parsed, comments starting with '#' or ';' + * or '//' are stripped. + * + * arguments beginning with -- are extracted + * + * lines that end in .txt are recursively scanned for more files + * other lines are added to the list of files. + * + * @param {string} url The url of the file to read. + * @param {void function(boolean, !Array.<string>)} callback. + * Callback that is called with true for success and an + * array of filenames. + * @param {Object} options. Optional options + * + * Options: + * version: {string} The version of the conformance test. + * Tests with the argument --min-version <version> will + * be ignored version is less then <version> + * + */ +var getFileList = function(url, callback, options) { var files = []; - if (url.substr(url.length - 4) == '.txt') { - var lines = loadTextFileSynchronous(url).split('\n'); - var prefix = ''; - var lastSlash = url.lastIndexOf('/'); - if (lastSlash >= 0) { - prefix = url.substr(0, lastSlash + 1); + + var copyObject = function(obj) { + return JSON.parse(JSON.stringify(obj)); + }; + + var toCamelCase = function(str) { + return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase() }); + }; + + var globalOptions = copyObject(options); + globalOptions.defaultVersion = "1.0"; + + var getFileListImpl = function(prefix, line, hierarchicalOptions, callback) { + var files = []; + + var args = line.split(/\s+/); + var nonOptions = []; + var useTest = true; + var testOptions = {}; + for (var jj = 0; jj < args.length; ++jj) { + var arg = args[jj]; + if (arg[0] == '-') { + if (arg[1] != '-') { + throw ("bad option at in " + url + ":" + (ii + 1) + ": " + str); + } + var option = arg.substring(2); + switch (option) { + case 'min-version': + ++jj; + testOptions[toCamelCase(option)] = args[jj]; + break; + default: + throw ("bad unknown option '" + option + "' at in " + url + ":" + (ii + 1) + ": " + str); + } + } else { + nonOptions.push(arg); + } } - for (var ii = 0; ii < lines.length; ++ii) { - var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - if (str.length > 4 && - str[0] != '#' && - str[0] != ";" && - str.substr(0, 2) != "//") { - new_url = prefix + str; - files = files.concat(getFileList(new_url)); + var url = prefix + nonOptions.join(" "); + + if (url.substr(url.length - 4) != '.txt') { + var minVersion = testOptions.minVersion; + if (!minVersion) { + minVersion = hierarchicalOptions.defaultVersion; + } + + if (globalOptions.minVersion) { + useTest = greaterThanOrEqualToVersion(minVersion, globalOptions.minVersion); + } else { + useTest = greaterThanOrEqualToVersion(globalOptions.version, minVersion); } } - } else { - files.push(url); - } - return files; -} + + if (!useTest) { + callback(true, []); + return; + } + + if (url.substr(url.length - 4) == '.txt') { + // If a version was explicity specified pass it down. + if (testOptions.minVersion) { + hierarchicalOptions.defaultVersion = testOptions.minVersion; + } + loadTextFileAsynchronous(url, function() { + return function(success, text) { + if (!success) { + callback(false, ''); + return; + } + var lines = text.split('\n'); + var prefix = ''; + var lastSlash = url.lastIndexOf('/'); + if (lastSlash >= 0) { + prefix = url.substr(0, lastSlash + 1); + } + var fail = false; + var count = 1; + var index = 0; + for (var ii = 0; ii < lines.length; ++ii) { + var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + if (str.length > 4 && + str[0] != '#' && + str[0] != ";" && + str.substr(0, 2) != "//") { + ++count; + getFileListImpl(prefix, str, copyObject(hierarchicalOptions), function(index) { + return function(success, new_files) { + log("got files: " + new_files.length); + if (success) { + files[index] = new_files; + } + finish(success); + }; + }(index++)); + } + } + finish(true); + + function finish(success) { + if (!success) { + fail = true; + } + --count; + log("count: " + count); + if (!count) { + callback(!fail, files); + } + } + } + }()); + } else { + files.push(url); + callback(true, files); + } + }; + + getFileListImpl('', url, globalOptions, function(success, files) { + // flatten + var flat = []; + flatten(files); + function flatten(files) { + for (var ii = 0; ii < files.length; ++ii) { + var value = files[ii]; + if (typeof(value) == "string") { + flat.push(value); + } else { + flatten(value); + } + } + } + callback(success, flat); + }); +}; var TestFile = function(url) { this.url = url; }; -var TestHarness = function(iframe, filelistUrl, reportFunc) { +var TestHarness = function(iframe, filelistUrl, reportFunc, options) { this.window = window; this.iframe = iframe; this.reportFunc = reportFunc; - try { - var files = getFileList(filelistUrl); - } catch (e) { + this.timeoutDelay = 20000; + this.files = []; + + var that = this; + getFileList(filelistUrl, function() { + return function(success, files) { + that.addFiles_(success, files); + }; + }(), options); + +}; + +TestHarness.reportType = { + ADD_PAGE: 1, + READY: 2, + START_PAGE: 3, + TEST_RESULT: 4, + FINISH_PAGE: 5, + FINISHED_ALL_TESTS: 6 +}; + +TestHarness.prototype.addFiles_ = function(success, files) { + if (!success) { this.reportFunc( TestHarness.reportType.FINISHED_ALL_TESTS, 'Unable to load tests. Are you running locally?\n' + 'You need to run from a server or configure your\n' + 'browser to allow access to local files (not recommended).\n\n' + 'Note: An easy way to run from a server:\n\n' + '\tcd path_to_tests\n' + '\tpython -m SimpleHTTPServer\n\n' + 'then point your browser to ' + '<a href="http://localhost:8000/webgl-conformance-tests.html">' + 'http://localhost:8000/webgl-conformance-tests.html</a>', false) return; } - this.files = []; + log("total files: " + files.length); for (var ii = 0; ii < files.length; ++ii) { + log("" + ii + ": " + files[ii]); this.files.push(new TestFile(files[ii])); this.reportFunc(TestHarness.reportType.ADD_PAGE, files[ii], undefined); } - this.nextFileIndex = files.length; - this.lastFileIndex = files.length; - this.timeoutDelay = 20000; + this.reportFunc(TestHarness.reportType.READY, undefined, undefined); } -TestHarness.reportType = { - ADD_PAGE: 1, - START_PAGE: 2, - TEST_RESULT: 3, - FINISH_PAGE: 4, - FINISHED_ALL_TESTS: 5 -}; - TestHarness.prototype.runTests = function(opt_start, opt_count) { var count = opt_count || this.files.length; this.nextFileIndex = opt_start || 0; this.lastFileIndex = this.nextFileIndex + count; this.startNextFile(); }; TestHarness.prototype.setTimeout = function() {
--- a/content/canvas/test/webgl/test_webgl_conformance_test_suite.html +++ b/content/canvas/test/webgl/test_webgl_conformance_test_suite.html @@ -5,16 +5,22 @@ <title> Mochitest version of the WebGL Conformance Test Suite </title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="text/javascript" src="resources/webgl-test-harness.js"></script> <script> +var CONFORMANCE_TEST_VERSION = "1.0.1 (beta)"; + +var OPTIONS = { + version: CONFORMANCE_TEST_VERSION +}; + /** * This is copied from webgl-test-harness.js where it is defined as a private function, not accessible to us (argh!) * * Loads text from an external file. This function is synchronous. * @param {string} url The url of the external file. * @return {string} the loaded text if the request is synchronous. */ var loadTextFileSynchronous = function (url) { @@ -261,57 +267,84 @@ function start() { Reporter.prototype.finishedTestSuite = function() { for (var i = 0; i < testsExpectedToFail.length; ++i) if (testsSuccessful.indexOf(testsExpectedToFail[i]) != -1) todo(true, 'Test expected to fail, but passed: ' + testsExpectedToFail[i]); statusTextNode.textContent = 'Finished'; SimpleTest.finish(); } + Reporter.prototype.ready = function() { + statusTextNode.textContent = 'Loaded test lists. Starting tests...'; + window.webglTestHarness.runTests(); + } + Reporter.prototype.reportFunc = function(type, msg, success) { switch (type) { case reportType.ADD_PAGE: return this.addPage(msg); + case reportType.READY: + return this.ready(); case reportType.START_PAGE: return this.startPage(msg); case reportType.TEST_RESULT: return this.addResult(msg, success); case reportType.FINISH_PAGE: return this.finishPage(success); case reportType.FINISHED_ALL_TESTS: this.finishedTestSuite(); return true; default: throw 'unhandled'; break; }; }; + var getURLOptions = function(obj) { + var s = window.location.href; + var q = s.indexOf("?"); + var e = s.indexOf("#"); + if (e < 0) { + e = s.length; + } + var query = s.substring(q + 1, e); + var pairs = query.split("&"); + for (var ii = 0; ii < pairs.length; ++ii) { + var keyValue = pairs[ii].split("="); + var key = keyValue[0]; + var value = decodeURIComponent(keyValue[1]); + obj[key] = value; + } + }; + + getURLOptions(OPTIONS); + function runTestSuite() { var reporter = new Reporter(); // try to create a dummy WebGL context, just to catch context creation failures once here, // rather than having them result in 100's of failures (one in each test page) var canvas = document.getElementById("webglcheck-default"); var ctx = null; try { ctx = canvas.getContext("experimental-webgl"); } catch(e) {} if (ctx) { + statusTextNode.textContent = 'Loading test lists...'; var iframe = document.getElementById("testframe"); var testHarness = new WebGLTestHarnessModule.TestHarness( iframe, '00_test_list.txt', function(type, msg, success) { return reporter.reportFunc(type, msg, success); - }); + }, + OPTIONS); testHarness.setTimeoutDelay(20000); // and make it much higher when running under valgrind. window.webglTestHarness = testHarness; - testHarness.runTests(); } else { var errmsg = "Can't create a WebGL context"; reporter.fullResultsNode.textContent = errmsg; ok(false, errmsg); dump("WebGL mochitest failed: " + errmsg + "\n"); reporter.finishedTestSuite(); } };
deleted file mode 100644 --- a/content/canvas/test/webgl/undo-r15330-async-test-list-loading.patch +++ /dev/null @@ -1,236 +0,0 @@ -# HG changeset patch -# Parent fb36d18f04ef9b01ca87d3fde539d50c204f9bba -diff --git a/content/canvas/test/webgl/resources/webgl-test-harness.js b/content/canvas/test/webgl/resources/webgl-test-harness.js ---- a/content/canvas/test/webgl/resources/webgl-test-harness.js -+++ b/content/canvas/test/webgl/resources/webgl-test-harness.js -@@ -90,190 +90,105 @@ var log = function(msg) { - if (window.console && window.console.log) { - window.console.log(msg); - } - }; - - /** - * Loads text from an external file. This function is synchronous. - * @param {string} url The url of the external file. -- * @param {!function(bool, string): void} callback that is sent a bool for -- * success and the string. -+ * @return {string} the loaded text if the request is synchronous. - */ --var loadTextFileAsynchronous = function(url, callback) { -- log ("loading: " + url); -+var loadTextFileSynchronous = function(url) { - var error = 'loadTextFileSynchronous failed to load url "' + url + '"'; - var request; - if (window.XMLHttpRequest) { - request = new XMLHttpRequest(); - if (request.overrideMimeType) { - request.overrideMimeType('text/plain'); - } - } else { - throw 'XMLHttpRequest is disabled'; - } -- try { -- request.open('GET', url, true); -- request.onreadystatechange = function() { -- if (request.readyState == 4) { -- var text = ''; -- // HTTP reports success with a 200 status. The file protocol reports -- // success with zero. HTTP does not use zero as a status code (they -- // start at 100). -- // https://developer.mozilla.org/En/Using_XMLHttpRequest -- var success = request.status == 200 || request.status == 0; -- if (success) { -- text = request.responseText; -- } -- log("loaded: " + url); -- callback(success, text); -- } -- }; -- request.send(null); -- } catch (e) { -- log("failed to load: " + url); -- callback(false, ''); -+ request.open('GET', url, false); -+ request.send(null); -+ if (request.readyState != 4) { -+ throw error; - } -+ return request.responseText; - }; - --var getFileList = function(url, callback) { -+var getFileList = function(url) { - var files = []; -- -- var getFileListImpl = function(url, callback) { -- var files = []; -- if (url.substr(url.length - 4) == '.txt') { -- loadTextFileAsynchronous(url, function() { -- return function(success, text) { -- if (!success) { -- callback(false, ''); -- return; -- } -- var lines = text.split('\n'); -- var prefix = ''; -- var lastSlash = url.lastIndexOf('/'); -- if (lastSlash >= 0) { -- prefix = url.substr(0, lastSlash + 1); -- } -- var fail = false; -- var count = 1; -- var index = 0; -- for (var ii = 0; ii < lines.length; ++ii) { -- var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); -- if (str.length > 4 && -- str[0] != '#' && -- str[0] != ";" && -- str.substr(0, 2) != "//") { -- new_url = prefix + str; -- ++count; -- getFileListImpl(new_url, function(index) { -- return function(success, new_files) { -- log("got files: " + new_files.length); -- if (success) { -- files[index] = new_files; -- } -- finish(success); -- }; -- }(index++)); -- } -- } -- finish(true); -- -- function finish(success) { -- if (!success) { -- fail = true; -- } -- --count; -- log("count: " + count); -- if (!count) { -- callback(!fail, files); -- } -- } -- } -- }()); -- -- } else { -- files.push(url); -- callback(true, files); -+ if (url.substr(url.length - 4) == '.txt') { -+ var lines = loadTextFileSynchronous(url).split('\n'); -+ var prefix = ''; -+ var lastSlash = url.lastIndexOf('/'); -+ if (lastSlash >= 0) { -+ prefix = url.substr(0, lastSlash + 1); - } -- }; -- -- getFileListImpl(url, function(success, files) { -- // flatten -- var flat = []; -- flatten(files); -- function flatten(files) { -- for (var ii = 0; ii < files.length; ++ii) { -- var value = files[ii]; -- if (typeof(value) == "string") { -- flat.push(value); -- } else { -- flatten(value); -- } -+ for (var ii = 0; ii < lines.length; ++ii) { -+ var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); -+ if (str.length > 4 && -+ str[0] != '#' && -+ str[0] != ";" && -+ str.substr(0, 2) != "//") { -+ new_url = prefix + str; -+ files = files.concat(getFileList(new_url)); - } - } -- callback(success, flat); -- }); --}; -+ } else { -+ files.push(url); -+ } -+ return files; -+} - - var TestFile = function(url) { - this.url = url; - }; - - var TestHarness = function(iframe, filelistUrl, reportFunc) { - this.window = window; - this.iframe = iframe; - this.reportFunc = reportFunc; -- this.timeoutDelay = 20000; -- this.files = []; -- -- var that = this; -- getFileList(filelistUrl, function() { -- return function(success, files) { -- that.addFiles_(success, files); -- }; -- }()); -- --}; -- --TestHarness.reportType = { -- ADD_PAGE: 1, -- READY: 2, -- START_PAGE: 3, -- TEST_RESULT: 4, -- FINISH_PAGE: 5, -- FINISHED_ALL_TESTS: 6 --}; -- --TestHarness.prototype.addFiles_ = function(success, files) { -- if (!success) { -+ try { -+ var files = getFileList(filelistUrl); -+ } catch (e) { - this.reportFunc( - TestHarness.reportType.FINISHED_ALL_TESTS, - 'Unable to load tests. Are you running locally?\n' + - 'You need to run from a server or configure your\n' + - 'browser to allow access to local files (not recommended).\n\n' + - 'Note: An easy way to run from a server:\n\n' + - '\tcd path_to_tests\n' + - '\tpython -m SimpleHTTPServer\n\n' + - 'then point your browser to ' + - '<a href="http://localhost:8000/webgl-conformance-tests.html">' + - 'http://localhost:8000/webgl-conformance-tests.html</a>', - false) - return; - } -- log("total files: " + files.length); -+ this.files = []; - for (var ii = 0; ii < files.length; ++ii) { -- log("" + ii + ": " + files[ii]); - this.files.push(new TestFile(files[ii])); - this.reportFunc(TestHarness.reportType.ADD_PAGE, files[ii], undefined); - } -- this.reportFunc(TestHarness.reportType.READY, undefined, undefined); - this.nextFileIndex = files.length; - this.lastFileIndex = files.length; -+ this.timeoutDelay = 20000; - } - -+TestHarness.reportType = { -+ ADD_PAGE: 1, -+ START_PAGE: 2, -+ TEST_RESULT: 3, -+ FINISH_PAGE: 4, -+ FINISHED_ALL_TESTS: 5 -+}; -+ - TestHarness.prototype.runTests = function(opt_start, opt_count) { - var count = opt_count || this.files.length; - this.nextFileIndex = opt_start || 0; - this.lastFileIndex = this.nextFileIndex + count; - this.startNextFile(); - }; - - TestHarness.prototype.setTimeout = function() {
--- a/content/canvas/test/webgl/webgl-conformance-tests.html +++ b/content/canvas/test/webgl/webgl-conformance-tests.html @@ -45,16 +45,20 @@ found in the LICENSE file. list-style: none; padding-left: 1em; } </style> <script type="text/javascript" src="resources/webgl-test-harness.js"></script> <script> var CONFORMANCE_TEST_VERSION = "1.0.1 (beta)"; +var OPTIONS = { + version: CONFORMANCE_TEST_VERSION +}; + function start() { function log(msg) { if (window.console && window.console.log) { window.console.log(msg); } } @@ -212,17 +216,17 @@ function start() { li.appendChild(div); li.appendChild(ul); this.childUL = ul; this.elem = li; this.check = check; }; Folder.prototype.checked = function() { - return this.check.checked && + return this.check.checked && (this.folder ? this.folder.checked() : true); }; Folder.prototype.firstTestIndex = function() { return this.items[0].firstTestIndex(); }; Folder.prototype.numChildren = function() { @@ -242,17 +246,17 @@ function start() { Folder.prototype.getSubFolder = function(name) { var subFolder = this.subFolders[name]; if (subFolder === undefined) { subFolder = new Folder(this.reporter, this, this.depth + 1, name); this.subFolders[name] = subFolder; this.items.push(subFolder); this.childUL.appendChild(subFolder.elem); - } + } return subFolder; }; Folder.prototype.getOrCreateFolder = function(url) { var parts = url.split('/'); var folder = this; for (var pp = 0; pp < parts.length - 1; ++pp) { folder = folder.getSubFolder(parts[pp]); @@ -358,17 +362,17 @@ function start() { } var msg = ' (' + totalSuccessful + ' of ' + totalTests + ' passed' + timeout + ')'; this.fullResultsNode.textContent = msg; // generate a text summary var tx = ""; tx += "WebGL Conformance Test Results\n"; - tx += "Version " + CONFORMANCE_TEST_VERSION + "\n"; + tx += "Version " + OPTIONS.version + "\n"; tx += "\n"; tx += "-------------------\n\n"; tx += "User Agent: " + (navigator.userAgent ? navigator.userAgent : "(navigator.userAgent is null)") + "\n"; tx += "WebGL VENDOR: " + this.contextInfo["VENDOR"] + "\n"; tx += "WebGL VERSION: " + this.contextInfo["VERSION"] + "\n"; tx += "WebGL RENDERER: " + this.contextInfo["RENDERER"] + "\n"; tx += "Unmasked VENDOR: " + this.contextInfo["UNMASKED_VENDOR"] + "\n"; tx += "Unmasked RENDERER: " + this.contextInfo["UNMASKED_RENDERER"] + "\n"; @@ -427,26 +431,46 @@ function start() { case reportType.FINISHED_ALL_TESTS: return this.displayFinalResults(msg, success); default: throw 'unhandled'; break; }; }; - document.getElementById("testVersion").innerHTML = CONFORMANCE_TEST_VERSION; + var getURLOptions = function(obj) { + var s = window.location.href; + var q = s.indexOf("?"); + var e = s.indexOf("#"); + if (e < 0) { + e = s.length; + } + var query = s.substring(q + 1, e); + var pairs = query.split("&"); + for (var ii = 0; ii < pairs.length; ++ii) { + var keyValue = pairs[ii].split("="); + var key = keyValue[0]; + var value = decodeURIComponent(keyValue[1]); + obj[key] = value; + } + }; + + getURLOptions(OPTIONS); + + document.getElementById("testVersion").innerHTML = OPTIONS.version; var reporter = new Reporter(); var iframe = document.getElementById("testframe"); var testHarness = new WebGLTestHarnessModule.TestHarness( iframe, '00_test_list.txt', function(type, msg, success) { return reporter.reportFunc(type, msg, success); - }); + }, + OPTIONS); window.webglTestHarness = testHarness; var button = document.getElementById("runTestsButton"); button.disabled = true; button.onclick = function() { testHarness.runTests(); }; var textbutton = document.getElementById("showTextSummary"); textbutton.onclick = function() {
--- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -202,50 +202,72 @@ static void AppendSubString(nsAString& a "aContent is not a text node!"); const nsTextFragment* text = aContent->GetText(); if (!text) return; text->AppendTo(aString, PRInt32(aXPOffset), PRInt32(aXPLength)); } #if defined(XP_WIN) -static PRUint32 CountNewlinesIn(nsIContent* aContent, PRUint32 aMaxOffset) +static PRUint32 CountNewlinesInXPLength(nsIContent* aContent, + PRUint32 aXPLength) { NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), "aContent is not a text node!"); const nsTextFragment* text = aContent->GetText(); if (!text) return 0; - if (aMaxOffset == PR_UINT32_MAX) { - // search the entire string - aMaxOffset = text->GetLength(); - } + NS_ASSERTION(aXPLength == PR_UINT32_MAX || aXPLength <= text->GetLength(), + "text offset is out-of-bounds"); + const PRUint32 length = NS_MIN(aXPLength, text->GetLength()); PRUint32 newlines = 0; - for (PRUint32 i = 0; i < aMaxOffset; ++i) { + for (PRUint32 i = 0; i < length; ++i) { if (text->CharAt(i) == '\n') { ++newlines; } } return newlines; } + +static PRUint32 CountNewlinesInNativeLength(nsIContent* aContent, + PRUint32 aNativeLength) +{ + NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), + "aContent is not a text node!"); + const nsTextFragment* text = aContent->GetText(); + if (!text) { + return 0; + } + const PRUint32 xpLength = text->GetLength(); + PRUint32 newlines = 0; + for (PRUint32 i = 0, nativeOffset = 0; + i < xpLength && nativeOffset < aNativeLength; + ++i, ++nativeOffset) { + if (text->CharAt(i) == '\n') { + ++newlines; + ++nativeOffset; + } + } + return newlines; +} #endif static PRUint32 GetNativeTextLength(nsIContent* aContent, PRUint32 aMaxLength = PR_UINT32_MAX) { if (aContent->IsNodeOfType(nsINode::eTEXT)) { PRUint32 textLengthDifference = #if defined(XP_MACOSX) // On Mac, the length of a native newline ("\r") is equal to the length of // the XP newline ("\n"), so the native length is the same as the XP length. 0; #elif defined(XP_WIN) // On Windows, the length of a native newline ("\r\n") is twice the length of // the XP newline ("\n"), so XP length is equal to the length of the native // offset plus the number of newlines encountered in the string. - CountNewlinesIn(aContent, aMaxLength); + CountNewlinesInXPLength(aContent, aMaxLength); #else // On other platforms, the native and XP newlines are the same. 0; #endif const nsTextFragment* text = aContent->GetText(); if (!text) return 0; @@ -267,17 +289,17 @@ static PRUint32 ConvertToXPOffset(nsICon #if defined(XP_MACOSX) // On Mac, the length of a native newline ("\r") is equal to the length of // the XP newline ("\n"), so the native offset is the same as the XP offset. return aNativeOffset; #elif defined(XP_WIN) // On Windows, the length of a native newline ("\r\n") is twice the length of // the XP newline ("\n"), so XP offset is equal to the length of the native // offset minus the number of newlines encountered in the string. - return aNativeOffset - CountNewlinesIn(aContent, aNativeOffset); + return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset); #else // On other platforms, the native and XP newlines are the same. return aNativeOffset; #endif } static nsresult GenerateFlatTextContent(nsRange* aRange, nsAFlatString& aString)
--- a/content/html/content/test/Makefile.in +++ b/content/html/content/test/Makefile.in @@ -295,10 +295,19 @@ include $(topsrcdir)/config/rules.mk file_fullscreen-esc-exit-inner.html \ file_fullscreen-rollback.html \ test_li_attributes_reflection.html \ test_ol_attributes_reflection.html \ test_bug651956.html \ test_bug694503.html \ $(NULL) +_BROWSER_TEST_FILES = \ + browser_bug649778.js \ + file_bug649778.html \ + file_bug649778.html^headers^ \ + $(NULL) + libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) + +libs:: $(_BROWSER_TEST_FILES) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/content/html/content/test/browser_bug649778.js @@ -0,0 +1,67 @@ +// Test for bug 649778 - document.write may cause a document to be written to disk cache even when the page has Cache-Control: no-store + +// Globals +var testPath = "http://mochi.test:8888/browser/content/html/content/test/"; +var popup; + +function checkCache(url, policy, shouldExist) +{ + var cache = Components.classes["@mozilla.org/network/cache-service;1"]. + getService(Components.interfaces.nsICacheService); + var session = cache.createSession( + "wyciwyg", policy, + Components.interfaces.nsICache.STREAM_BASED); + try { + var cacheEntry = session.openCacheEntry( + url, Components.interfaces.nsICache.ACCESS_READ, true); + is(shouldExist, true, "Entry found"); + } + catch (e) { + is(shouldExist, false, "Entry not found"); + is(e.result, Components.results.NS_ERROR_CACHE_KEY_NOT_FOUND, + "Invalid error"); + } +} + +function getPopupURL() { + var sh = popup.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .sessionHistory; + + return sh.getEntryAtIndex(sh.index, false).URI.spec; +} + +function testContinue() { + var wyciwygURL = getPopupURL(); + is(wyciwygURL.substring(0, 10), "wyciwyg://", "Unexpected URL."); + popup.close() + + checkCache(wyciwygURL, Components.interfaces.nsICache.STORE_ON_DISK, false); + checkCache(wyciwygURL, Components.interfaces.nsICache.STORE_IN_MEMORY, true); + + finish(); +} + +function waitForWyciwygDocument() { + try { + var url = getPopupURL(); + if (url.substring(0, 10) == "wyciwyg://") { + setTimeout(testContinue, 0); + return; + } + } + catch (e) { + } + setTimeout(waitForWyciwygDocument, 100); +} + +// Entry point from Mochikit +function test() { + waitForExplicitFinish(); + + popup = window.open(testPath + "file_bug649778.html", "popup 0", + "height=200,width=200,location=yes," + + "menubar=yes,status=yes,toolbar=yes,dependent=yes"); + + waitForWyciwygDocument(); +}
new file mode 100644 --- /dev/null +++ b/content/html/content/test/file_bug649778.html @@ -0,0 +1,11 @@ +<html> +<script> +function test() { + document.open(); + document.write('<html><body>WYCIWYG DOCUMENT</body></html>'); + document.close(); +} +</script> +<body onload="setTimeout(test, 0);"> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/content/html/content/test/file_bug649778.html^headers^ @@ -0,0 +1,1 @@ +Cache-Control: no-store
--- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -131,16 +131,17 @@ #include "nsRange.h" #include "mozAutoDocUpdate.h" #include "nsCCUncollectableMarker.h" #include "nsHtml5Module.h" #include "prprf.h" #include "mozilla/dom/Element.h" #include "mozilla/Preferences.h" #include "nsMimeTypes.h" +#include "nsIRequest.h" using namespace mozilla; using namespace mozilla::dom; #define NS_MAX_DOCUMENT_WRITE_DEPTH 20 #include "prmem.h" #include "prtime.h" @@ -1414,16 +1415,17 @@ nsHTMLDocument::Open(const nsAString& aC } // Grab a reference to the calling documents security info (if any) // and URIs as they may be lost in the call to Reset(). nsCOMPtr<nsISupports> securityInfo = callerDoc->GetSecurityInfo(); nsCOMPtr<nsIURI> uri = callerDoc->GetDocumentURI(); nsCOMPtr<nsIURI> baseURI = callerDoc->GetBaseURI(); nsCOMPtr<nsIPrincipal> callerPrincipal = callerDoc->NodePrincipal(); + nsCOMPtr<nsIChannel> callerChannel = callerDoc->GetChannel(); // We're called from script. Make sure the script is from the same // origin, not just that the caller can access the document. This is // needed to keep document principals from ever changing, which is // needed because of the way we use our XOW code, and is a sane // thing to do anyways. bool equals = false; @@ -1485,16 +1487,31 @@ nsHTMLDocument::Open(const nsAString& aC // We can't depend on channels implementing property bags, so do our // base URI manually after reset. // Set the caller principal, if any, on the channel so that we'll // make sure to use it when we reset. rv = channel->SetOwner(callerPrincipal); NS_ENSURE_SUCCESS(rv, rv); + if (callerChannel) { + nsLoadFlags callerLoadFlags; + rv = callerChannel->GetLoadFlags(&callerLoadFlags); + NS_ENSURE_SUCCESS(rv, rv); + + nsLoadFlags loadFlags; + rv = channel->GetLoadFlags(&loadFlags); + NS_ENSURE_SUCCESS(rv, rv); + + loadFlags |= callerLoadFlags & nsIRequest::INHIBIT_PERSISTENT_CACHING; + + rv = channel->SetLoadFlags(loadFlags); + NS_ENSURE_SUCCESS(rv, rv); + } + // Before we reset the doc notify the globalwindow of the change, // but only if we still have a window (i.e. our window object the // current inner window in our outer window). // Hold onto ourselves on the offchance that we're down to one ref nsCOMPtr<nsIDOMDocument> kungFuDeathGrip = do_QueryInterface((nsIHTMLDocument*)this);
--- a/content/media/test/Makefile.in +++ b/content/media/test/Makefile.in @@ -122,16 +122,17 @@ include $(topsrcdir)/config/rules.mk test_info_leak.html \ test_load.html \ test_load_candidates.html \ test_load_same_resource.html \ test_load_source.html \ test_loop.html \ test_media_selection.html \ test_mozLoadFrom.html \ + test_no_load_event.html \ test_networkState.html \ test_new_audio.html \ test_paused.html \ test_paused_after_ended.html \ test_play_events.html \ test_play_events_2.html \ test_playback.html \ test_playback_errors.html \
new file mode 100644 --- /dev/null +++ b/content/media/test/test_no_load_event.html @@ -0,0 +1,59 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=715469 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 715469</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="text/javascript" src="manifest.js"></script> +</head> +<body onload="start();"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=715469">Mozilla Bug 715469</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> + +<script type="application/javascript"> + +/** Test for Bug 715469 **/ + +var gotLoadEvent = false; + +function start() { + var resource = getPlayableVideo(gSmallTests); + if (resource == null) { + todo(false, "No types supported"); + } else { + SimpleTest.waitForExplicitFinish(); + var v = document.createElement("video"); + v.src = resource.name; + v.addEventListener("loadeddata", function(){v.play();}, false); + v.controls = "true"; + + v.addEventListener("load", + function(){ + gotLoadEvent = true; + }, + false); + + v.addEventListener("ended", finished, false); + + document.body.appendChild(v); + } +} + +function finished() { + is(gotLoadEvent, false, "Should not receive a load on the video element"); + SimpleTest.finish(); +} + + +</script> +</pre> +</body> +</html>
--- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -2194,17 +2194,17 @@ nsSMILTimedElement::NotifyNewInterval() "Attempting to notify dependents of a new interval but the interval " "is not set"); nsSMILTimeContainer* container = GetTimeContainer(); if (container) { container->SyncPauseTime(); } - NotifyTimeDependentsParams params = { mCurrentInterval, container }; + NotifyTimeDependentsParams params = { this, container }; mTimeDependents.EnumerateEntries(NotifyNewIntervalCallback, ¶ms); } void nsSMILTimedElement::NotifyChangedInterval(nsSMILInterval* aInterval, bool aBeginObjectChanged, bool aEndObjectChanged) { @@ -2304,14 +2304,19 @@ nsSMILTimedElement::NotifyNewIntervalCal { NS_ABORT_IF_FALSE(aKey, "Null hash key for time container hash table"); NS_ABORT_IF_FALSE(aKey->GetKey(), "null nsSMILTimeValueSpec in set of time dependents"); NotifyTimeDependentsParams* params = static_cast<NotifyTimeDependentsParams*>(aData); NS_ABORT_IF_FALSE(params, "null data ptr while enumerating hashtable"); - NS_ABORT_IF_FALSE(params->mCurrentInterval, "null current-interval ptr"); + nsSMILInterval* interval = params->mTimedElement->mCurrentInterval; + // It's possible that in notifying one new time dependent of a new interval + // that a chain reaction is triggered which results in the original interval + // disappearing. If that's the case we can skip sending further notifications. + if (!interval) + return PL_DHASH_STOP; nsSMILTimeValueSpec* spec = aKey->GetKey(); - spec->HandleNewInterval(*params->mCurrentInterval, params->mTimeContainer); + spec->HandleNewInterval(*interval, params->mTimeContainer); return PL_DHASH_NEXT; }
--- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -361,17 +361,17 @@ protected: public: bool Equals(const nsSMILInstanceTime* aElem1, const nsSMILInstanceTime* aElem2) const; bool LessThan(const nsSMILInstanceTime* aElem1, const nsSMILInstanceTime* aElem2) const; }; struct NotifyTimeDependentsParams { - nsSMILInterval* mCurrentInterval; + nsSMILTimedElement* mTimedElement; nsSMILTimeContainer* mTimeContainer; }; // Templated helper functions template <class TestFunctor> void RemoveInstanceTimes(InstanceTimeList& aArray, TestFunctor& aTest); //
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -801,43 +801,45 @@ nsDOMWindowUtils::Focus(nsIDOMElement* a else fm->ClearFocus(mWindow); } return NS_OK; } NS_IMETHODIMP -nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener) +nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener, + PRInt32 aExtraForgetSkippableCalls) { SAMPLE_LABEL("GC", "GarbageCollect"); // Always permit this in debug builds. #ifndef DEBUG if (!IsUniversalXPConnectCapable()) { return NS_ERROR_DOM_SECURITY_ERR; } #endif nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS); - nsJSContext::CycleCollectNow(aListener); + nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls); return NS_OK; } NS_IMETHODIMP -nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener) +nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener, + PRInt32 aExtraForgetSkippableCalls) { // Always permit this in debug builds. #ifndef DEBUG if (!IsUniversalXPConnectCapable()) { return NS_ERROR_DOM_SECURITY_ERR; } #endif - nsJSContext::CycleCollectNow(aListener); + nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls); return NS_OK; } NS_IMETHODIMP nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType, float aX, float aY, PRUint32 aDirection,
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -278,16 +278,19 @@ static PRInt32 gRefCnt static PRInt32 gOpenPopupSpamCount = 0; static PopupControlState gPopupControlState = openAbused; static PRInt32 gRunningTimeoutDepth = 0; static bool gMouseDown = false; static bool gDragServiceDisabled = false; static FILE *gDumpFile = nsnull; static PRUint64 gNextWindowID = 0; static PRUint32 gSerialCounter = 0; +static PRUint32 gTimeoutsRecentlySet = 0; +static TimeStamp gLastRecordedRecentTimeouts; +#define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC) #ifdef DEBUG_jst PRInt32 gTimeoutCnt = 0; #endif #if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP)) static bool gDOMWindowDumpEnabled = false; #endif @@ -9049,16 +9052,17 @@ nsGlobalWindow::SetTimeoutOrInterval(nsI } if (subsumes) { timeout->mPrincipal = subjectPrincipal; } else { timeout->mPrincipal = ourPrincipal; } + ++gTimeoutsRecentlySet; TimeDuration delta = TimeDuration::FromMilliseconds(realInterval); if (!IsFrozen() && !mTimeoutsSuspendDepth) { // If we're not currently frozen, then we set timeout->mWhen to be the // actual firing time of the timer (i.e., now + delta). We also actually // create a timer and fire it off. timeout->mWhen = TimeStamp::Now() + delta; @@ -9223,16 +9227,26 @@ nsGlobalWindow::RunTimeout(nsTimeout *aT // Maybe the timeout that the event was fired for has been deleted // and there are no others timeouts with deadlines that make them // eligible for execution yet. Go away. if (!last_expired_timeout) { return; } + // Record telemetry information about timers set recently. + TimeDuration recordingInterval = TimeDuration::FromMilliseconds(STATISTICS_INTERVAL); + if (gLastRecordedRecentTimeouts.IsNull() || + now - gLastRecordedRecentTimeouts > recordingInterval) { + PRUint32 count = gTimeoutsRecentlySet; + gTimeoutsRecentlySet = 0; + Telemetry::Accumulate(Telemetry::DOM_TIMERS_RECENTLY_SET, count); + gLastRecordedRecentTimeouts = now; + } + // Insert a dummy timeout into the list of timeouts between the // portion of the list that we are about to process now and those // timeouts that will be processed in a future call to // win_run_timeout(). This dummy timeout serves as the head of the // list for any timeouts inserted as a result of running a timeout. dummy_timeout.mFiringDepth = firingDepth; dummy_timeout.mWhen = now; PR_INSERT_AFTER(&dummy_timeout, last_expired_timeout);
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -134,16 +134,20 @@ static PRLogModuleInfo* gJSDiagnostics; // The amount of time we wait from the first request to GC to actually // doing the first GC. #define NS_FIRST_GC_DELAY 10000 // ms // The amount of time we wait between a request to CC (after GC ran) // and doing the actual CC. #define NS_CC_DELAY 5000 // ms +#define NS_CC_SKIPPABLE_DELAY 250 // ms + +#define NS_CC_FORCED (5 * 60 * PR_USEC_PER_SEC) // 5 min + #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT // if you add statics here, add them to the list in nsJSRuntime::Startup static nsITimer *sGCTimer; static nsITimer *sShrinkGCBuffersTimer; static nsITimer *sCCTimer; @@ -157,16 +161,25 @@ static bool sGCHasRun; // all cases. This counter also gets reset if we end up GC'ing while // we're waiting for a slow page to load. IOW, this count may be 0 // even when there are pending loads. static PRUint32 sPendingLoadCount; static bool sLoadingInProgress; static PRUint32 sCCollectedWaitingForGC; static bool sPostGCEventsToConsole; +static PRUint32 sCCTimerFireCount = 0; +static PRUint32 sMinForgetSkippableTime = PR_UINT32_MAX; +static PRUint32 sMaxForgetSkippableTime = 0; +static PRUint32 sTotalForgetSkippableTime = 0; +static PRUint32 sRemovedPurples = 0; +static PRUint32 sForgetSkippableBeforeCC = 0; +static PRUint32 sPreviousSuspectedCount = 0; + +static bool sCleanupSinceLastGC = true; nsScriptNameSpaceManager *gNameSpaceManager; static nsIJSRuntimeService *sRuntimeService; JSRuntime *nsJSRuntime::sRuntime; static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1"; @@ -3251,30 +3264,40 @@ nsJSContext::ShrinkGCBuffersNow() KillShrinkGCBuffersTimer(); JS_ShrinkGCBuffers(nsJSRuntime::sRuntime); } //Static void -nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener) +nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, + PRInt32 aExtraForgetSkippableCalls) { if (!NS_IsMainThread()) { return; } SAMPLE_LABEL("GC", "CycleCollectNow"); NS_TIME_FUNCTION_MIN(1.0); KillCCTimer(); PRTime start = PR_Now(); PRUint32 suspected = nsCycleCollector_suspectedCount(); + + for (PRInt32 i = 0; i < aExtraForgetSkippableCalls; ++i) { + nsCycleCollector_forgetSkippable(); + } + + // nsCycleCollector_forgetSkippable may mark some gray js to black. + if (!sCleanupSinceLastGC && aExtraForgetSkippableCalls >= 0) { + nsCycleCollector_forgetSkippable(); + } PRUint32 collected = nsCycleCollector_collect(aListener); sCCollectedWaitingForGC += collected; // If we collected a substantial amount of cycles, poke the GC since more objects // might be unreachable now. if (sCCollectedWaitingForGC > 250) { PokeGC(js::gcreason::CC_WAITING); } @@ -3290,28 +3313,45 @@ nsJSContext::CycleCollectNow(nsICycleCol if (sPostGCEventsToConsole) { PRTime delta = 0; if (sFirstCollectionTime) { delta = now - sFirstCollectionTime; } else { sFirstCollectionTime = now; } - NS_NAMED_LITERAL_STRING(kFmt, - "CC(T+%.1f) collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms."); + NS_NAMED_MULTILINE_LITERAL_STRING(kFmt, + NS_LL("CC(T+%.1f) collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms.\n") + NS_LL("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, removed: %lu")); nsString msg; + PRUint32 cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1; + sMinForgetSkippableTime = (sMinForgetSkippableTime == PR_UINT32_MAX) + ? 0 : sMinForgetSkippableTime; msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC, collected, sCCollectedWaitingForGC, suspected, - (now - start) / PR_USEC_PER_MSEC)); + (now - start) / PR_USEC_PER_MSEC, + sForgetSkippableBeforeCC, + sMinForgetSkippableTime / PR_USEC_PER_MSEC, + sMaxForgetSkippableTime / PR_USEC_PER_MSEC, + (sTotalForgetSkippableTime / cleanups) / + PR_USEC_PER_MSEC, + sTotalForgetSkippableTime / PR_USEC_PER_MSEC, + sRemovedPurples)); nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); if (cs) { cs->LogStringMessage(msg.get()); } } + sMinForgetSkippableTime = PR_UINT32_MAX; + sMaxForgetSkippableTime = 0; + sTotalForgetSkippableTime = 0; + sRemovedPurples = 0; + sForgetSkippableBeforeCC = 0; + sCleanupSinceLastGC = true; } // static void GCTimerFired(nsITimer *aTimer, void *aClosure) { NS_RELEASE(sGCTimer); @@ -3326,19 +3366,60 @@ ShrinkGCBuffersTimerFired(nsITimer *aTim nsJSContext::ShrinkGCBuffersNow(); } // static void CCTimerFired(nsITimer *aTimer, void *aClosure) { - NS_RELEASE(sCCTimer); - - nsJSContext::CycleCollectNow(); + if (sDidShutdown) { + return; + } + ++sCCTimerFireCount; + if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) { + PRUint32 suspected = nsCycleCollector_suspectedCount(); + if ((sPreviousSuspectedCount + 100) > suspected) { + // Just few new suspected objects, return early. + return; + } + sPreviousSuspectedCount = suspected; + PRTime startTime; + if (sPostGCEventsToConsole) { + startTime = PR_Now(); + } + nsCycleCollector_forgetSkippable(); + sCleanupSinceLastGC = true; + if (sPostGCEventsToConsole) { + PRTime delta = PR_Now() - startTime; + if (sMinForgetSkippableTime > delta) { + sMinForgetSkippableTime = delta; + } + if (sMaxForgetSkippableTime < delta) { + sMaxForgetSkippableTime = delta; + } + sTotalForgetSkippableTime += delta; + sRemovedPurples += (suspected - nsCycleCollector_suspectedCount()); + ++sForgetSkippableBeforeCC; + } + } else { + sPreviousSuspectedCount = 0; + nsJSContext::KillCCTimer(); + if (nsCycleCollector_suspectedCount() > 500 || + sLastCCEndTime + NS_CC_FORCED < PR_Now()) { + nsJSContext::CycleCollectNow(); + } + } +} + +// static +bool +nsJSContext::CleanupSinceLastGC() +{ + return sCleanupSinceLastGC; } // static void nsJSContext::LoadStart() { sLoadingInProgress = true; ++sPendingLoadCount; @@ -3409,40 +3490,31 @@ nsJSContext::PokeShrinkGCBuffers() NS_SHRINK_GC_BUFFERS_DELAY, nsITimer::TYPE_ONE_SHOT); } // static void nsJSContext::MaybePokeCC() { - if (nsCycleCollector_suspectedCount() > 1000) { - PokeCC(); - } -} - -// static -void -nsJSContext::PokeCC() -{ - if (sCCTimer || !sGCHasRun) { - // There's already a timer for GC'ing, or GC hasn't run yet, just return. + if (sCCTimer) { return; } - CallCreateInstance("@mozilla.org/timer;1", &sCCTimer); - - if (!sCCTimer) { - // Failed to create timer (probably because we're in XPCOM shutdown) - return; + if (nsCycleCollector_suspectedCount() > 100 || + sLastCCEndTime + NS_CC_FORCED < PR_Now()) { + sCCTimerFireCount = 0; + CallCreateInstance("@mozilla.org/timer;1", &sCCTimer); + if (!sCCTimer) { + return; + } + sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull, + NS_CC_SKIPPABLE_DELAY, + nsITimer::TYPE_REPEATING_SLACK); } - - sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull, - NS_CC_DELAY, - nsITimer::TYPE_ONE_SHOT); } //static void nsJSContext::KillGCTimer() { if (sGCTimer) { sGCTimer->Cancel(); @@ -3499,16 +3571,18 @@ DOMGCFinishedCallback(JSRuntime *rt, JSC double(delta) / PR_USEC_PER_SEC, status)); nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); if (cs) { cs->LogStringMessage(msg.get()); } } sCCollectedWaitingForGC = 0; + sCleanupSinceLastGC = false; + if (sGCTimer) { // If we were waiting for a GC to happen, kill the timer. nsJSContext::KillGCTimer(); // If this is a compartment GC, restart it. We still want // a full GC to happen. Compartment GCs usually happen as a // result of last-ditch or MaybeGC. In both cases its // probably a time of heavy activity and we want to delay @@ -3518,17 +3592,17 @@ DOMGCFinishedCallback(JSRuntime *rt, JSC // We poked the GC, so we can kill any pending CC here. nsJSContext::KillCCTimer(); } } else { // If this was a full GC, poke the CC to run soon. if (!comp) { sGCHasRun = true; - nsJSContext::PokeCC(); + nsJSContext::MaybePokeCC(); } } // If we didn't end up scheduling a GC, make sure that we release GC buffers // soon after canceling previous shrinking attempt nsJSContext::KillShrinkGCBuffersTimer(); if (!sGCTimer) { nsJSContext::PokeShrinkGCBuffers();
--- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -178,30 +178,34 @@ public: NS_DECL_NSIXPCSCRIPTNOTIFY static void LoadStart(); static void LoadEnd(); static void GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind = nsGCNormal); static void ShrinkGCBuffersNow(); - static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull); + // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be + // called even if the previous collection was GC. + static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull, + PRInt32 aExtraForgetSkippableCalls = 0); static void PokeGC(js::gcreason::Reason aReason); static void KillGCTimer(); static void PokeShrinkGCBuffers(); static void KillShrinkGCBuffersTimer(); - static void PokeCC(); static void MaybePokeCC(); static void KillCCTimer(); virtual void GC(js::gcreason::Reason aReason); + static bool CleanupSinceLastGC(); + nsIScriptGlobalObject* GetCachedGlobalObject() { // Verify that we have a global so that this // does always return a null when GetGlobalObject() is null. JSObject* global = JS_GetGlobalObject(mContext); return global ? mGlobalObjectRef.get() : nsnull; } protected:
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -65,17 +65,17 @@ interface nsIDOMEvent; interface nsITransferable; interface nsIQueryContentEventResult; interface nsIDOMWindow; interface nsIDOMBlob; interface nsIDOMFile; interface nsIFile; interface nsIDOMTouch; -[scriptable, uuid(e01171b0-712a-47ce-8552-b7b2ef0a2507)] +[scriptable, uuid(ab6e9c71-8aa1-40bb-8bf9-65e16429055f)] interface nsIDOMWindowUtils : nsISupports { /** * Image animation mode of the window. When this attribute's value * is changed, the implementation should set all images in the window * to the given value. That is, when set to kDontAnimMode, all images * will stop animating. The attribute's value must be one of the * animationMode values from imgIContainer. @@ -398,30 +398,44 @@ interface nsIDOMWindowUtils : nsISupport * Force a garbage collection followed by a cycle collection. * * Will throw a DOM security error if called without UniversalXPConnect * privileges in non-debug builds. Available to all callers in debug builds. * * @param aListener listener that receives information about the CC graph * (see @mozilla.org/cycle-collector-logger;1 for a logger * component) + * @param aExtraForgetSkippableCalls indicates how many times + * nsCycleCollector_forgetSkippable will + * be called before running cycle collection. + * -1 prevents the default + * nsCycleCollector_forgetSkippable call + * which happens after garbage collection. */ - void garbageCollect([optional] in nsICycleCollectorListener aListener); + void garbageCollect([optional] in nsICycleCollectorListener aListener, + [optional] in long aExtraForgetSkippableCalls); /** * Force a cycle collection without garbage collection. * * Will throw a DOM security error if called without UniversalXPConnect * privileges in non-debug builds. Available to all callers in debug builds. * * @param aListener listener that receives information about the CC graph * (see @mozilla.org/cycle-collector-logger;1 for a logger * component) + * @param aExtraForgetSkippableCalls indicates how many times + * nsCycleCollector_forgetSkippable will + * be called before running cycle collection. + * -1 prevents the default + * nsCycleCollector_forgetSkippable call + * which happens after garbage collection. */ - void cycleCollect([optional] in nsICycleCollectorListener aListener); + void cycleCollect([optional] in nsICycleCollectorListener aListener, + [optional] in long aExtraForgetSkippableCalls); /** Synthesize a simple gesture event for a window. The event types * supported are: MozSwipeGesture, MozMagnifyGestureStart, * MozMagnifyGestureUpdate, MozMagnifyGesture, MozRotateGestureStart, * MozRotateGestureUpdate, MozRotateGesture, MozPressTapGesture, and * MozTapGesture. * * Cannot be accessed from unprivileged context (not
--- a/dom/wifi/nsWifiWorker.js +++ b/dom/wifi/nsWifiWorker.js @@ -478,25 +478,44 @@ var WifiManager = (function() { var handler = manager["on" + eventName]; if (handler) { if (!eventObject) eventObject = ({}); handler.call(eventObject); } } + function parseStatus(status) { + if (status === null) { + debug("Unable to get wpa supplicant's status"); + return; + } + + var lines = status.split("\n"); + for (let i = 0; i < lines.length; ++i) { + let [key, value] = lines[i].split("="); + if (key === "wpa_state") { + if (value === "COMPLETED") + onconnected(); + } + } + } + // try to connect to the supplicant var connectTries = 0; var retryTimer = null; function connectCallback(ok) { if (ok === 0) { - // tell the event worker to start waiting for events + // Tell the event worker to start waiting for events. retryTimer = null; waitForEvent(); notify("supplicantconnection"); + + // Load up the supplicant state. + statusCommand(parseStatus); return; } if (connectTries++ < 3) { // try again in 5 seconds if (!retryTimer) retryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); retryTimer.initWithCallback(function(timer) { @@ -783,51 +802,27 @@ function nsWifiWorker() { } WifiManager.onsupplicantlost = function() { debug("Couldn't connect to supplicant"); } var networks = Object.create(null); WifiManager.onscanresultsavailable = function() { debug("Scan results are available! Asking for them."); - if (networks["Mozilla Guest"]) - return; WifiManager.getScanResults(function(r) { let lines = r.split("\n"); // NB: Skip the header line. - let added = !("Mozilla Guest" in networks); for (let i = 1; i < lines.length; ++i) { // bssid / frequency / signal level / flags / ssid var match = /([\S]+)\s+([\S]+)\s+([\S]+)\s+(\[[\S]+\])?\s+(.*)/.exec(lines[i]) if (match) networks[match[5]] = match[1]; else debug("Match didn't find anything for: " + lines[i]); } - - if (("Mozilla Guest" in networks) && added) { - debug("Mozilla Guest exists in networks, trying to connect!"); - var config = Object.create(null); - config["ssid"] = '"Mozilla Guest"'; - //config["bssid"] = '"' + networks["Mozilla Guest"] + '"'; - config["key_mgmt"] = "NONE"; - config["scan_ssid"] = 1; - WifiManager.addNetwork(config, function (ok) { - if (ok) { - WifiManager.enableNetwork(config.netId, false, function (ok) { - if (ok) - debug("Enabled the network!"); - else - debug("Failed to enable the network :("); - }); - } else { - debug("Failed to add the network :("); - } - }); - } }); } WifiManager.setWifiEnabled(true, function (ok) { if (ok === 0) WifiManager.start(); else debug("Couldn't start Wifi");
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -227,26 +227,26 @@ public: } NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback* aCallback, nsISupports* aClosure) { AssertIsOnMainThread(); - JS::IterateData data(xpc::JsMallocSizeOf, xpc::GetCompartmentName, - xpc::DestroyCompartmentName); - nsresult rv = CollectForRuntime(/* isQuick = */false, &data); + JS::RuntimeStats rtStats(xpc::JsMallocSizeOf, xpc::GetCompartmentName, + xpc::DestroyCompartmentName); + nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats); if (NS_FAILED(rv)) { return rv; } // Always report, even if we're disabled, so that we at least get an entry // in about::memory. - ReportJSRuntimeStats(data, mPathPrefix, aCallback, aClosure); + ReportJSRuntimeStats(rtStats, mPathPrefix, aCallback, aClosure); return NS_OK; } NS_IMETHOD GetExplicitNonHeap(PRInt64 *aAmount) { AssertIsOnMainThread(); @@ -1519,17 +1519,17 @@ public: bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { JSAutoSuspendRequest asr(aCx); *mSucceeded = mIsQuick ? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast<int64_t*>(mData), xpc::JsMallocSizeOf) - : JS::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), static_cast<JS::IterateData*>(mData)); + : JS::CollectRuntimeStats(JS_GetRuntime(aCx), static_cast<JS::RuntimeStats*>(mData)); { MutexAutoLock lock(mMutex); mDone = true; mCondVar.Notify(); } return true;
--- a/embedding/android/Makefile.in +++ b/embedding/android/Makefile.in @@ -194,13 +194,13 @@ RES_DRAWABLE = $(addprefix res/drawable/ $(RESOURCES): $(RES_DIRS) $(subst res/,$(srcdir)/resources/,$(RESOURCES)) @echo "creating $@" $(NSINSTALL) $(subst res/,$(srcdir)/resources/,$@) $(dir $@) R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/drawable/icon.png res/drawable-hdpi/icon.png AndroidManifest.xml chrome $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko -gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/values/strings.xml FORCE +gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) chrome FORCE $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@ libs:: classes.dex $(INSTALL) classes.dex $(FINAL_TARGET)
--- a/extensions/auth/nsAuthGSSAPI.cpp +++ b/extensions/auth/nsAuthGSSAPI.cpp @@ -160,17 +160,18 @@ gssInit() "gss", "gssapi_krb5", "gssapi" }; const char *const verLibNames[] = { "libgssapi_krb5.so.2", /* MIT - FC, Suse10, Debian */ "libgssapi.so.4", /* Heimdal - Suse10, MDK */ - "libgssapi.so.1" /* Heimdal - Suse9, CITI - FC, MDK, Suse10*/ + "libgssapi.so.1", /* Heimdal - Suse9, CITI - FC, MDK, Suse10*/ + "libgssapi.so" /* OpenBSD */ }; for (size_t i = 0; i < ArrayLength(verLibNames) && !lib; ++i) { lib = PR_LoadLibrary(verLibNames[i]); /* The CITI libgssapi library calls exit() during * initialization if it's not correctly configured. Try to * ensure that we never use this library for our GSSAPI
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff +++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff @@ -9138,428 +9138,430 @@ 15460a21264,21266 > cancelled/U > canceller/M > cancelling 15559a21366 > capita 15629,15630d21435 < carburetter/SM < carburettor/SM -15788d21592 +15701a21507 +> carnitas +15788d21593 < cashpoint/S -15797d21600 +15797d21601 < cassino/M -15832a21636 +15832a21637 > catalyses -15940d21743 +15940d21744 < caviare/M -16372c22175 +16372c22176 < chickenshit/S! --- > chickenshit/SM! -16404c22207 +16404c22208 < children --- > children/M -16488d22290 +16488d22291 < chlorophyl/M -16629,16630c22431 +16629,16630c22432 < cider's < cider/S --- > cider/MS -17072d22872 +17072d22873 < cocain/M -17102,17103c22902 +17102,17103c22903 < cocksucker's < cocksucker/S! --- > cocksucker/SM! -17755c23554 +17755c23555 < confer/S --- > confer/SB -18151d23949 +18151d23950 < convenor/S -18206c24004 +18206c24005 < cookie/M --- > cookie/SM -18467a24266 +18467a24267 > could've -19246c25045 +19246c25046 < cysteine --- > cysteine/M -20196,20197c25995,25996 +20196,20197c25996,25997 < dialog/SM < dialogue/SM --- > dialog/SMGD > dialogue/SMRGD -20481a26281 +20481a26282 > disclose/DSG -20830c26630 +20830c26631 < dogie/M --- > dogie/SM -20895a26696 +20895a26697 > donator/MS -21820a27622 +21820a27623 > elicitor/MS -22071a27874 +22071a27875 > encyclopaedia -22556a28360 +22556a28361 > estoppel -22638c28442 +22638c28443 < euthanize --- > euthanize/DSG -22719a28524 +22719a28525 > exabyte/MS -22947a28753 +22947a28754 > experimentalism -23207,23208d29012 +23207,23208d29013 < faecal < faeces/M -23215c29019 +23215c29020 < faggoting's --- > faggot/SMG -23701a29506 +23701a29507 > filesystem/MS -24155c29960 +24155c29961 < fluidized --- > fluidize/DSG -24216a30022 +24216a30023 > foci -24736d30541 +24736d30542 < frier/M -24855,24856c30660,30661 +24855,24856c30661,30662 < fucker/M! < fuckhead/S! --- > fucker/SM! > fuckhead/SM! -24953d30757 +24953d30758 < furore/MS -25125c30929 +25125c30930 < gaolbird/S --- > gaolbirds -25180d30983 +25180d30984 < gasolene/M -25190a30994 +25190a30995 > gastroenterologist/M -25262c31066 +25262c31067 < geezer/M --- > geezer/MS -25327c31131 +25327c31132 < genomic --- > genomic/S -25462a31267 +25462a31268 > gigabit/MS -25464a31270,31272 +25464a31271,31273 > gigajoule/MS > gigapixel/MS > gigawatt/MS -25560d31367 +25560d31368 < glamourize/DSG -25674c31481 +25674c31482 < glycerine's --- > glycerine/M -25905c31712 +25905c31713 < gram/MS --- > gram/KMS -25909d31715 +25909d31716 < gramme/SM -26063c31869,31870 +26063c31870,31871 < greybeard --- > grey/MDRTGSP > greybeard/SM -26066c31873 +26066c31874 < greyness --- > greyness/M -26246,26247d32052 +26246,26247d32053 < guerilla's < guerillas -26432,26436d32236 +26432,26436d32237 < haemoglobin's < haemophilia/M < haemorrhage/DSMG < haemorrhoid/S < haemorrhoids/M -27167c32967 +27167c32968 < hexane --- > hexane/SM -27273a33074 +27273a33075 > hippopotami -27875d33675 +27875d33676 < hyaena/SM -28017c33817 +28017c33818 < iPod/M --- > iPod/MS -28105a33906 +28105a33907 > idolator/SM -28513c34314 +28513c34315 < inbound --- > inbound/s -28650a34452 +28650a34453 > indices -28812d34613 +28812d34614 < inflexion/SM -29216a35018 +29216a35019 > intern/GDL -29272a35075,35078 +29272a35076,35079 > intersex > intersexual/MS > intersexualism > intersexuality -29724c35530 +29724c35531 < jewellery's --- > jewellery/M -29870a35677 +29870a35678 > judgement/MS -30066c35873 +30066c35874 < kiddie/M --- > kiddie/SM -30262,30263c36069 +30262,30263c36070 < kraut's < kraut/S! --- > kraut/MS! -30665a36472 +30665a36473 > lector/MS -31031c36838 +31031c36839 < linguini's --- > linguini/M -31151,31152c36958 +31151,31152c36959 < liver's < liver/S --- > liver/MS -32230c38036 +32230c38037 < meanie/M --- > meanie/MS -32317,32318c38123 +32317,32318c38124 < megadeath/M < megadeaths --- > megadeath/SM -32320c38125 +32320c38126 < megajoules --- > megajoule/SM -32329c38134 +32329c38135 < megapixel/S --- > megapixel/MS -32708a38514 +32708a38515 > might've -32777d38582 +32777d38583 < millionnaire/M -32934a38740 +32934a38741 > miscommunication/S -32991a38798 +32991a38799 > misjudgement/MS -33784a39592 +33784a39593 > must've -33963c39771 +33963c39772 < native/MS --- > native/MSY -34169,34171c39977,39978 +34169,34171c39978,39979 < neurone/S < neurophysiology < neuroscience --- > neurophysiology/M > neuroscience/MS -34275c40082 +34275c40083 < nightie/M --- > nightie/SM -35104a40912 +35104a40913 > octopi -35219d41026 +35219d41027 < oleomargarin/M -35226a41034 +35226a41035 > oligo -35913c41721 +35913c41722 < oversize/D --- > oversize -36056,36059d41863 +36056,36059d41864 < paederast/S < paediatrician's < paediatricians < paediatrics/M -36291a42096 +36291a42097 > paralyses -36403d42207 +36403d42208 < parrakeet/MS -36449d42252 +36449d42253 < partizan/SM -37093a42897 +37093a42898 > petabyte/MS -37102c42906 +37102c42907 < petitioner/M --- > petitioner/MS -37264a43069 +37264a43070 > phosphorylate/DSGN -37316d43120 +37316d43121 < phrenetic -37796a43601 +37796a43602 > plugin/MS -37987c43792 +37987c43793 < polypeptide/S --- > polypeptide/MS -38291d44095 +38291d44096 < practise's -38451a44256 +38451a44257 > prejudgement/MS -38891a44697,44698 +38891a44698,44699 > pronate/DSGN > pronator/MS -38951c44758 +38951c44759 < proprietorship/M --- > proprietorship/MS -39039a44847 +39039a44848 > provender/M -40036a45845 +40036a45846 > recency -40141a45951 +40141a45952 > recuse/DGS -40208a46019 +40208a46020 > refactor/SMDG -40244d46054 +40244d46055 < reflexion/SM -40829c46639 +40829c46640 < reverie/M --- > reverie/MS -41415a47226 +41415a47227 > sabre/MS -41914c47725 +41914c47726 < schnaps's --- > schnaps/M -41949c47760 +41949c47761 < schrod's --- > schrod/SM -41998a42010 +41998a47811 > scot-free -42883,42885c48695 +42883,42885c48696 < shit's < shit/S! < shite/S! --- > shit/MS! -42887,42888c48697,48698 +42887,42888c48698,48699 < shithead/S! < shitload/! --- > shithead/MS! > shitload/MS! -42891c48701 +42891c48702 < shitty/RT! --- > shitty/TR! -42976a48787 +42976a48788 > should've -43008c48819 +43008c48820 < showtime --- > showtime/MS -43724,43726c49535 +43724,43726c49536 < smoulder's < smouldered < smoulders --- > smoulder/GSMD -44062c49871 +44062c49872 < sonofabitch --- > sonofabitch/! -44371a50181 +44371a50182 > spick/S! -44383c50193 +44383c50194 < spik/S --- > spik/S! -46106a51917 +46106a51918 > syllabi -46160c51971 +46160c51972 < synch/GMD --- > synch/GMDS -46167d51977 +46167d51978 < synchs -46203,46204c52013,52014 +46203,46204c52014,52015 < sysadmin/S < sysop/S --- > sysadmin/MS > sysop/MS -46752a52563 +46752a52564 > terabit/MS -46753a52565,52566 +46753a52566,52567 > terahertz/M > terapixel/MS -46817a52631 +46817a52632 > testcase/MS -46831a52646 +46831a52647 > testsuite/MS -46925a52741 +46925a52742 > theremin/MS -47755a53572 +47755a53573 > transfect/DSMG -47774a53592,53593 +47774a53593,53594 > transgenderism > transgene/MS -47951c53770 +47951c53771 < triage/M --- > triage/MG -48869a54689 +48869a54690 <