зеркало из https://github.com/mozilla/pjs.git
Merge m-c to s-c.
This commit is contained in:
Коммит
95077b288f
|
@ -787,10 +787,15 @@ interface nsIAccessibleRole : nsISupports
|
|||
*/
|
||||
const unsigned long ROLE_NOTE = 123;
|
||||
|
||||
/**
|
||||
* A figure. Used for things like HTML5 figure element.
|
||||
*/
|
||||
const unsigned long ROLE_FIGURE = 124;
|
||||
|
||||
/**
|
||||
* It's not role actually. This constant is important to help ensure
|
||||
* nsRoleMap's are synchronized.
|
||||
*/
|
||||
const unsigned long ROLE_LAST_ENTRY = 124;
|
||||
const unsigned long ROLE_LAST_ENTRY = 125;
|
||||
};
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@ static const PRUint32 atkRoleMap[] = {
|
|||
ATK_ROLE_TABLE_CELL, // nsIAccessibleRole::ROLE_GRID_CELL 121
|
||||
ATK_ROLE_PANEL, // nsIAccessibleRole::ROLE_EMBEDDED_OBJECT 122
|
||||
ATK_ROLE_SECTION, // nsIAccessibleRole::ROLE_NOTE 123
|
||||
ATK_ROLE_PANEL, // nsIAccessibleRole::ROLE_FIGURE 124
|
||||
kROLE_ATK_LAST_ENTRY // nsIAccessibleRole::ROLE_LAST_ENTRY
|
||||
};
|
||||
|
||||
|
|
|
@ -1605,7 +1605,20 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
|
|||
nsIWeakReference* aWeakShell)
|
||||
{
|
||||
// This method assumes we're in an HTML namespace.
|
||||
nsIAtom *tag = aContent->Tag();
|
||||
nsIAtom* tag = aContent->Tag();
|
||||
if (tag == nsGkAtoms::figcaption) {
|
||||
nsAccessible* accessible =
|
||||
new nsHTMLFigcaptionAccessible(aContent, aWeakShell);
|
||||
NS_IF_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::figure) {
|
||||
nsAccessible* accessible = new nsHTMLFigureAccessible(aContent, aWeakShell);
|
||||
NS_IF_ADDREF(accessible);
|
||||
return accessible;
|
||||
}
|
||||
|
||||
if (tag == nsGkAtoms::legend) {
|
||||
nsAccessible* accessible = new nsHTMLLegendAccessible(aContent, aWeakShell);
|
||||
NS_IF_ADDREF(accessible);
|
||||
|
|
|
@ -424,7 +424,8 @@ static const char kRoleNames[][20] = {
|
|||
"flat equation", //ROLE_FLAT_EQUATION
|
||||
"gridcell", //ROLE_GRID_CELL
|
||||
"embedded object", //ROLE_EMBEDDED_OBJECT
|
||||
"note" //ROLE_NOTE
|
||||
"note", //ROLE_NOTE
|
||||
"figure" //ROLE_FIGURE
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -793,3 +793,106 @@ nsHTMLLegendAccessible::NativeRole()
|
|||
{
|
||||
return nsIAccessibleRole::ROLE_LABEL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLFigureAccessible
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
nsHTMLFigureAccessible::
|
||||
nsHTMLFigureAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
|
||||
nsHyperTextAccessibleWrap(aContent, aShell)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLFigureAccessible::GetAttributesInternal(nsIPersistentProperties* aAttributes)
|
||||
{
|
||||
nsresult rv = nsHyperTextAccessibleWrap::GetAttributesInternal(aAttributes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Expose figure xml-role.
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("figure"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsHTMLFigureAccessible::NativeRole()
|
||||
{
|
||||
return nsIAccessibleRole::ROLE_FIGURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLFigureAccessible::GetNameInternal(nsAString& aName)
|
||||
{
|
||||
nsresult rv = nsHyperTextAccessibleWrap::GetNameInternal(aName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!aName.IsEmpty())
|
||||
return NS_OK;
|
||||
|
||||
nsIContent* captionContent = Caption();
|
||||
if (captionContent) {
|
||||
return nsTextEquivUtils::
|
||||
AppendTextEquivFromContent(this, captionContent, &aName);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Relation
|
||||
nsHTMLFigureAccessible::RelationByType(PRUint32 aType)
|
||||
{
|
||||
Relation rel = nsHyperTextAccessibleWrap::RelationByType(aType);
|
||||
if (aType == nsIAccessibleRelation::RELATION_LABELLED_BY)
|
||||
rel.AppendTarget(Caption());
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsHTMLFigureAccessible::Caption() const
|
||||
{
|
||||
for (nsIContent* childContent = mContent->GetFirstChild(); childContent;
|
||||
childContent = childContent->GetNextSibling()) {
|
||||
if (childContent->NodeInfo()->Equals(nsGkAtoms::figcaption,
|
||||
mContent->GetNameSpaceID())) {
|
||||
return childContent;
|
||||
}
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLFigcaptionAccessible
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
nsHTMLFigcaptionAccessible::
|
||||
nsHTMLFigcaptionAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
|
||||
nsHyperTextAccessibleWrap(aContent, aShell)
|
||||
{
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsHTMLFigcaptionAccessible::NativeRole()
|
||||
{
|
||||
return nsIAccessibleRole::ROLE_CAPTION;
|
||||
}
|
||||
|
||||
Relation
|
||||
nsHTMLFigcaptionAccessible::RelationByType(PRUint32 aType)
|
||||
{
|
||||
Relation rel = nsHyperTextAccessibleWrap::RelationByType(aType);
|
||||
if (aType != nsIAccessibleRelation::RELATION_LABEL_FOR)
|
||||
return rel;
|
||||
|
||||
nsAccessible* figure = Parent();
|
||||
if (figure &&
|
||||
figure->GetContent()->NodeInfo()->Equals(nsGkAtoms::figure,
|
||||
mContent->GetNameSpaceID())) {
|
||||
rel.AppendTarget(figure);
|
||||
}
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
|
|
@ -228,4 +228,36 @@ public:
|
|||
virtual Relation RelationByType(PRUint32 aType);
|
||||
};
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Accessible for HTML5 figure element.
|
||||
*/
|
||||
class nsHTMLFigureAccessible : public nsHyperTextAccessibleWrap
|
||||
{
|
||||
public:
|
||||
nsHTMLFigureAccessible(nsIContent* aContent, nsIWeakReference* aShell);
|
||||
|
||||
// nsAccessible
|
||||
virtual nsresult GetAttributesInternal(nsIPersistentProperties* aAttributes);
|
||||
virtual nsresult GetNameInternal(nsAString& aName);
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual Relation RelationByType(PRUint32 aType);
|
||||
|
||||
protected:
|
||||
nsIContent* Caption() const;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible for HTML5 figcaption element.
|
||||
*/
|
||||
class nsHTMLFigcaptionAccessible : public nsHyperTextAccessibleWrap
|
||||
{
|
||||
public:
|
||||
nsHTMLFigcaptionAccessible(nsIContent* aContent, nsIWeakReference* aShell);
|
||||
|
||||
// nsAccessible
|
||||
virtual PRUint32 NativeRole();
|
||||
virtual Relation RelationByType(PRUint32 aType);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -166,5 +166,6 @@ static const NSString* AXRoles [] = {
|
|||
NSAccessibilityGroupRole, // ROLE_GRID_CELL
|
||||
NSAccessibilityGroupRole, // ROLE_EMBEDDED_OBJECT
|
||||
NSAccessibilityGroupRole, // ROLE_NOTE
|
||||
NSAccessibilityGroupRole, // ROLE_FIGURE
|
||||
@"ROLE_LAST_ENTRY" // ROLE_LAST_ENTRY. bogus role that will never be shown (just marks the end of this array)!
|
||||
};
|
||||
|
|
|
@ -446,6 +446,9 @@ static const WindowsRoleMapItem gWindowsRoleMap[] = {
|
|||
// nsIAccessibleRole::ROLE_NOTE
|
||||
{ USE_ROLE_STRING, IA2_ROLE_NOTE },
|
||||
|
||||
// nsIAccessibleRole::ROLE_FIGURE
|
||||
{ ROLE_SYSTEM_GROUPING, ROLE_SYSTEM_GROUPING },
|
||||
|
||||
// nsIAccessibleRole::ROLE_LAST_ENTRY
|
||||
{ ROLE_WINDOWS_LAST_ENTRY, ROLE_WINDOWS_LAST_ENTRY }
|
||||
};
|
||||
|
|
|
@ -46,6 +46,7 @@ DIRS = \
|
|||
actions \
|
||||
attributes \
|
||||
editabletext \
|
||||
elm \
|
||||
events \
|
||||
focus \
|
||||
hyperlink \
|
||||
|
@ -94,10 +95,6 @@ _TEST_FILES =\
|
|||
test_childAtPoint.html \
|
||||
test_childAtPoint.xul \
|
||||
test_descr.html \
|
||||
test_elm_landmarks.html \
|
||||
test_elm_listbox.xul \
|
||||
test_elm_nsApplicationAcc.html \
|
||||
test_elm_plugin.html \
|
||||
test_nsIAccessibleDocument.html \
|
||||
test_nsIAccessibleImage.html \
|
||||
test_nsIAccessNode_utils.html \
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2009
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Alexander Surkov <surkov.alexander@gmail.com> (original author)
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# 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 *****
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = accessible/elm
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES =\
|
||||
test_figure.html \
|
||||
test_landmarks.html \
|
||||
test_listbox.xul \
|
||||
test_nsApplicationAcc.html \
|
||||
test_plugin.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>HTML5 figure/figcaption tests</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../attributes.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../relations.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../name.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function doTest()
|
||||
{
|
||||
testRole("figure", ROLE_FIGURE);
|
||||
testRole("figcaption", ROLE_CAPTION);
|
||||
|
||||
todo(false, "figure name gets extra whitespace in the end!");
|
||||
testName("figure", "figure caption ");
|
||||
testName("figcaption", null);
|
||||
|
||||
testRelation("figure", RELATION_LABELLED_BY, "figcaption");
|
||||
testRelation("figcaption", RELATION_LABEL_FOR, "figure");
|
||||
|
||||
testAttrs("figure", {"xml-roles" : "figure"}, true);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
title="Implement figure and figcaption accessibility"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=658272">
|
||||
Mozilla Bug 658272
|
||||
</a><br/>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<figure id="figure">
|
||||
<figcaption id="figcaption">figure caption</figcaption>
|
||||
</figure>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -9,11 +9,11 @@
|
|||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="common.js"></script>
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="role.js"></script>
|
||||
src="../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="attributes.js"></script>
|
||||
src="../attributes.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
|
@ -10,9 +10,9 @@
|
|||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="common.js"></script>
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="role.js"></script>
|
||||
src="../role.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
|
@ -8,9 +8,9 @@
|
|||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="common.js"></script>
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="role.js"></script>
|
||||
src="../role.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function doTest()
|
|
@ -9,11 +9,11 @@
|
|||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="common.js"></script>
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="role.js"></script>
|
||||
src="../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="states.js"></script>
|
||||
src="../states.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
|
@ -20,6 +20,7 @@ const ROLE_DIALOG = nsIAccessibleRole.ROLE_DIALOG;
|
|||
const ROLE_DOCUMENT = nsIAccessibleRole.ROLE_DOCUMENT;
|
||||
const ROLE_EMBEDDED_OBJECT = nsIAccessibleRole.ROLE_EMBEDDED_OBJECT;
|
||||
const ROLE_ENTRY = nsIAccessibleRole.ROLE_ENTRY;
|
||||
const ROLE_FIGURE = nsIAccessibleRole.ROLE_FIGURE;
|
||||
const ROLE_FOOTER = nsIAccessibleRole.ROLE_FOOTER;
|
||||
const ROLE_FLAT_EQUATION = nsIAccessibleRole.ROLE_FLAT_EQUATION;
|
||||
const ROLE_FORM = nsIAccessibleRole.ROLE_FORM;
|
||||
|
|
|
@ -209,18 +209,20 @@ var gLanguagesDialog = {
|
|||
if (arrayOfPrefs[i] == selectedID)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this._selectedItemID = selectedID;
|
||||
|
||||
|
||||
if (preference.value == "")
|
||||
preference.value = selectedID;
|
||||
else
|
||||
preference.value += "," + selectedID;
|
||||
else {
|
||||
arrayOfPrefs.unshift(selectedID);
|
||||
preference.value = arrayOfPrefs.join(",");
|
||||
}
|
||||
|
||||
this._acceptLanguages[selectedID] = true;
|
||||
this._availableLanguages.selectedItem = null;
|
||||
|
||||
// Reuild the available list with the added item removed...
|
||||
// Rebuild the available list with the added item removed...
|
||||
this._buildAvailableLanguageList();
|
||||
|
||||
this._availableLanguages.setAttribute("label", this._availableLanguages.getAttribute("label2"));
|
||||
|
|
|
@ -136,6 +136,10 @@
|
|||
@BINPATH@/components/dom.xpt
|
||||
@BINPATH@/components/dom_apps.xpt
|
||||
@BINPATH@/components/dom_base.xpt
|
||||
#ifdef MOZ_B2G_RIL
|
||||
@BINPATH@/components/dom_telephony.xpt
|
||||
@BINPATH@/components/dom_telephony_worker.xpt
|
||||
#endif
|
||||
@BINPATH@/components/dom_battery.xpt
|
||||
@BINPATH@/components/dom_canvas.xpt
|
||||
@BINPATH@/components/dom_core.xpt
|
||||
|
@ -358,6 +362,12 @@
|
|||
@BINPATH@/components/contentSecurityPolicy.js
|
||||
@BINPATH@/components/contentAreaDropListener.manifest
|
||||
@BINPATH@/components/contentAreaDropListener.js
|
||||
#ifdef MOZ_B2G_RIL
|
||||
@BINPATH@/components/nsTelephonyWorker.manifest
|
||||
@BINPATH@/components/nsTelephonyWorker.js
|
||||
@BINPATH@/components/Telephony.manifest
|
||||
@BINPATH@/components/Telephony.js
|
||||
#endif
|
||||
@BINPATH@/components/BrowserProfileMigrators.manifest
|
||||
@BINPATH@/components/ChromeProfileMigrator.js
|
||||
#ifdef XP_MACOSX
|
||||
|
|
|
@ -68,6 +68,10 @@ components/nsUrlClassifierTable.js
|
|||
components/nsXmlRpcClient.js
|
||||
components/pluginGlue.js
|
||||
components/sidebar.xpt
|
||||
#ifdef MOZ_B2G_RIL
|
||||
components/dom_telephony.xpt
|
||||
components/dom_telephony_worker.xpt
|
||||
#endif
|
||||
components/WeaveCrypto.js
|
||||
components/WeaveCrypto.manifest
|
||||
components/xmlextras.xpt
|
||||
|
@ -230,6 +234,7 @@ greprefs/xpinstall.js
|
|||
install.rdf
|
||||
modules/ISO8601DateUtils.jsm
|
||||
modules/JSON.jsm
|
||||
modules/SpatialNavigation.js
|
||||
modules/utils.js
|
||||
mozilla-runtime@BIN_SUFFIX@
|
||||
old-homepage-default.properties
|
||||
|
@ -912,6 +917,12 @@ xpicleanup@BIN_SUFFIX@
|
|||
components/PlacesProtocolHandler.js
|
||||
components/storage-Legacy.js
|
||||
components/storage-mozStorage.js
|
||||
#ifdef MOZ_B2G_RIL
|
||||
components/nsTelephonyWorker.manifest
|
||||
components/nsTelephonyWorker.js
|
||||
components/Telephony.manifest
|
||||
components/Telephony.js
|
||||
#endif
|
||||
components/txEXSLTRegExFunctions.js
|
||||
components/Weave.js
|
||||
components/WebContentConverter.js
|
||||
|
@ -1129,6 +1140,10 @@ xpicleanup@BIN_SUFFIX@
|
|||
components/docshell.xpt
|
||||
components/dom.xpt
|
||||
components/dom_base.xpt
|
||||
#ifdef MOZ_B2G_RIL
|
||||
components/dom_telephony.xpt
|
||||
components/dom_telephony_worker.xpt
|
||||
#endif
|
||||
components/dom_canvas.xpt
|
||||
components/dom_core.xpt
|
||||
components/dom_css.xpt
|
||||
|
@ -1298,6 +1313,7 @@ xpicleanup@BIN_SUFFIX@
|
|||
components/libimgicon.so
|
||||
dictionaries/PL.aff
|
||||
dictionaries/PL.dic
|
||||
icons/document.png
|
||||
icons/mozicon16.xpm
|
||||
icons/mozicon50.xpm
|
||||
plugins/libnullplugin.so
|
||||
|
|
|
@ -3,6 +3,7 @@ from devicemanager import DeviceManager, DMError
|
|||
import re
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
class DeviceManagerADB(DeviceManager):
|
||||
|
||||
|
@ -13,6 +14,7 @@ class DeviceManagerADB(DeviceManager):
|
|||
self.retries = 0
|
||||
self._sock = None
|
||||
self.useRunAs = False
|
||||
self.useZip = False
|
||||
self.packageName = None
|
||||
if packageName == None:
|
||||
if os.getenv('USER'):
|
||||
|
@ -30,6 +32,10 @@ class DeviceManagerADB(DeviceManager):
|
|||
except:
|
||||
self.useRunAs = False
|
||||
self.packageName = None
|
||||
try:
|
||||
self.verifyZip()
|
||||
except:
|
||||
self.useZip = False
|
||||
try:
|
||||
# a test to see if we have root privs
|
||||
files = self.listFiles("/data/data")
|
||||
|
@ -103,31 +109,41 @@ class DeviceManagerADB(DeviceManager):
|
|||
def pushDir(self, localDir, remoteDir):
|
||||
# adb "push" accepts a directory as an argument, but if the directory
|
||||
# contains symbolic links, the links are pushed, rather than the linked
|
||||
# files; we push file-by-file to get around this limitation
|
||||
# files; we either zip/unzip or push file-by-file to get around this
|
||||
# limitation
|
||||
try:
|
||||
if (not self.dirExists(remoteDir)):
|
||||
self.mkDirs(remoteDir+"/x")
|
||||
for root, dirs, files in os.walk(localDir, followlinks='true'):
|
||||
relRoot = os.path.relpath(root, localDir)
|
||||
for file in files:
|
||||
localFile = os.path.join(root, file)
|
||||
remoteFile = remoteDir + "/"
|
||||
if (relRoot!="."):
|
||||
remoteFile = remoteFile + relRoot + "/"
|
||||
remoteFile = remoteFile + file
|
||||
self.pushFile(localFile, remoteFile)
|
||||
for dir in dirs:
|
||||
targetDir = remoteDir + "/"
|
||||
if (relRoot!="."):
|
||||
targetDir = targetDir + relRoot + "/"
|
||||
targetDir = targetDir + dir
|
||||
if (not self.dirExists(targetDir)):
|
||||
self.mkDir(targetDir)
|
||||
if (self.useZip):
|
||||
localZip = tempfile.mktemp()+".zip"
|
||||
remoteZip = remoteDir + "/adbdmtmp.zip"
|
||||
subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
|
||||
self.pushFile(localZip, remoteZip)
|
||||
os.remove(localZip)
|
||||
self.checkCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir])
|
||||
self.checkCmdAs(["shell", "rm", remoteZip])
|
||||
else:
|
||||
if (not self.dirExists(remoteDir)):
|
||||
self.mkDirs(remoteDir+"/x")
|
||||
for root, dirs, files in os.walk(localDir, followlinks='true'):
|
||||
relRoot = os.path.relpath(root, localDir)
|
||||
for file in files:
|
||||
localFile = os.path.join(root, file)
|
||||
remoteFile = remoteDir + "/"
|
||||
if (relRoot!="."):
|
||||
remoteFile = remoteFile + relRoot + "/"
|
||||
remoteFile = remoteFile + file
|
||||
self.pushFile(localFile, remoteFile)
|
||||
for dir in dirs:
|
||||
targetDir = remoteDir + "/"
|
||||
if (relRoot!="."):
|
||||
targetDir = targetDir + relRoot + "/"
|
||||
targetDir = targetDir + dir
|
||||
if (not self.dirExists(targetDir)):
|
||||
self.mkDir(targetDir)
|
||||
self.checkCmdAs(["shell", "chmod", "777", remoteDir])
|
||||
return True
|
||||
return remoteDir
|
||||
except:
|
||||
print "pushing " + localDir + " to " + remoteDir + " failed"
|
||||
return False
|
||||
return None
|
||||
|
||||
# external function
|
||||
# returns:
|
||||
|
@ -241,11 +257,25 @@ class DeviceManagerADB(DeviceManager):
|
|||
acmd = ["shell", "am","start"]
|
||||
cmd = ' '.join(cmd).strip()
|
||||
i = cmd.find(" ")
|
||||
# SUT identifies the URL by looking for :\\ -- another strategy to consider
|
||||
re_url = re.compile('^[http|file|chrome|about].*')
|
||||
last = cmd.rfind(" ")
|
||||
uri = ""
|
||||
args = ""
|
||||
if re_url.match(cmd[last:].strip()):
|
||||
args = cmd[i:last].strip()
|
||||
uri = cmd[last:].strip()
|
||||
else:
|
||||
args = cmd[i:].strip()
|
||||
acmd.append("-n")
|
||||
acmd.append(cmd[0:i] + "/.App")
|
||||
acmd.append("--es")
|
||||
acmd.append("args")
|
||||
acmd.append(cmd[i:])
|
||||
if args != "":
|
||||
acmd.append("args")
|
||||
acmd.append(args)
|
||||
if uri != "":
|
||||
acmd.append("-d")
|
||||
acmd.append(''.join(['\'',uri, '\'']));
|
||||
print acmd
|
||||
self.checkCmd(acmd)
|
||||
return outputFile;
|
||||
|
@ -578,3 +608,25 @@ class DeviceManagerADB(DeviceManager):
|
|||
self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
|
||||
self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
|
||||
|
||||
def isUnzipAvailable(self):
|
||||
data = self.runCmd(["shell", "unzip"]).stdout.read()
|
||||
if (re.search('Usage', data)):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def isLocalZipAvailable(self):
|
||||
try:
|
||||
subprocess.check_call(["zip", "-?"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
def verifyZip(self):
|
||||
# If "zip" can be run locally, and "unzip" can be run remotely, then pushDir
|
||||
# can use these to push just one file per directory -- a significant
|
||||
# optimization for large directories.
|
||||
self.useZip = False
|
||||
if (self.isUnzipAvailable() and self.isLocalZipAvailable()):
|
||||
print "will use zip to push directories"
|
||||
self.useZip = True
|
||||
|
|
|
@ -129,7 +129,6 @@ MOZ_LIBSTDCXX_TARGET_VERSION=@MOZ_LIBSTDCXX_TARGET_VERSION@
|
|||
MOZ_LIBSTDCXX_HOST_VERSION=@MOZ_LIBSTDCXX_HOST_VERSION@
|
||||
INCREMENTAL_LINKER = @INCREMENTAL_LINKER@
|
||||
MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@
|
||||
MOZ_MAIL_NEWS = @MOZ_MAIL_NEWS@
|
||||
ENABLE_TESTS = @ENABLE_TESTS@
|
||||
IBMBIDI = @IBMBIDI@
|
||||
MOZ_UNIVERSALCHARDET = @MOZ_UNIVERSALCHARDET@
|
||||
|
@ -289,6 +288,8 @@ MOZ_GIO_LIBS = @MOZ_GIO_LIBS@
|
|||
MOZ_NATIVE_NSPR = @MOZ_NATIVE_NSPR@
|
||||
MOZ_NATIVE_NSS = @MOZ_NATIVE_NSS@
|
||||
|
||||
MOZ_B2G_RIL = @MOZ_B2G_RIL@
|
||||
|
||||
BUILD_CTYPES = @BUILD_CTYPES@
|
||||
|
||||
COMPILE_ENVIRONMENT = @COMPILE_ENVIRONMENT@
|
||||
|
|
13
configure.in
13
configure.in
|
@ -4981,6 +4981,7 @@ cairo-gonk)
|
|||
TK_LIBS='$(MOZ_CAIRO_LIBS)'
|
||||
MOZ_WEBGL=1
|
||||
MOZ_PDF_PRINTING=1
|
||||
MOZ_B2G_RIL=1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
@ -7573,6 +7574,18 @@ MOZ_ARG_ENABLE_BOOL(quantify,
|
|||
MOZ_QUANTIFY=1,
|
||||
MOZ_QUANTIFY= )
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable Radio Interface for B2G (Gonk usually)
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(b2g-ril,
|
||||
[ --enable-b2g-ril Set compile flags necessary for testing B2G Radio Interface Layer via network sockets ],
|
||||
MOZ_B2G_RIL=1,
|
||||
MOZ_B2G_RIL= )
|
||||
if test -n "$MOZ_B2G_RIL"; then
|
||||
AC_DEFINE(MOZ_B2G_RIL)
|
||||
fi
|
||||
AC_SUBST(MOZ_B2G_RIL)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Support for demangling undefined symbols
|
||||
dnl ========================================================
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
onmessage = function(event) {
|
||||
var blob = event.data;
|
||||
|
||||
blob.mozSlice(1, 5);
|
||||
|
||||
postMessage("done");
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<html class="reftest-wait">
|
||||
<script type="text/javascript">
|
||||
var worker = new Worker("700512-worker.js");
|
||||
|
||||
var bb = new MozBlobBuilder();
|
||||
|
||||
bb.append("foo");
|
||||
bb.append("bar");
|
||||
|
||||
worker.onmessage = function() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
worker.postMessage(bb.getBlob());
|
||||
</script>
|
||||
</html>
|
|
@ -100,4 +100,5 @@ load 693212.xhtml
|
|||
load 698974-1.html
|
||||
load 700090-1.html
|
||||
load 700090-2.html
|
||||
load 700512.html
|
||||
load xhr_html_nullresponse.html
|
||||
|
|
|
@ -141,7 +141,7 @@ interface nsIContentViewManager : nsISupports
|
|||
readonly attribute nsIContentView rootContentView;
|
||||
};
|
||||
|
||||
[scriptable, uuid(12905a29-4246-475a-81d4-fc389197df02)]
|
||||
[scriptable, uuid(efc0b731-45dc-4189-8ffa-d3eeeb850977)]
|
||||
interface nsIFrameLoader : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -258,6 +258,13 @@ interface nsIFrameLoader : nsISupports
|
|||
const unsigned long EVENT_MODE_DONT_FORWARD_TO_CHILD = 0x00000001;
|
||||
|
||||
attribute unsigned long eventMode;
|
||||
|
||||
/**
|
||||
* If false, then the subdocument is not clipped to its CSS viewport, and the
|
||||
* subdocument's viewport scrollbar(s) are not rendered.
|
||||
* Defaults to true.
|
||||
*/
|
||||
attribute boolean clipSubdocument;
|
||||
};
|
||||
|
||||
native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
|
||||
|
|
|
@ -143,14 +143,12 @@ public:
|
|||
nsRefPtr<nsIDocShell> mDocShell;
|
||||
};
|
||||
|
||||
static void InvalidateFrame(nsIFrame* aFrame)
|
||||
static void InvalidateFrame(nsIFrame* aFrame, PRUint32 aFlags)
|
||||
{
|
||||
if (!aFrame)
|
||||
return;
|
||||
nsRect rect = nsRect(nsPoint(0, 0), aFrame->GetRect().Size());
|
||||
// NB: we pass INVALIDATE_NO_THEBES_LAYERS here to keep view
|
||||
// semantics the same for both in-process and out-of-process
|
||||
// <browser>. This is just a transform of the layer subtree in
|
||||
// both.
|
||||
aFrame->InvalidateWithFlags(rect, nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
|
||||
aFrame->InvalidateWithFlags(rect, aFlags);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsContentView, nsIContentView)
|
||||
|
@ -189,8 +187,11 @@ nsContentView::Update(const ViewConfig& aConfig)
|
|||
|
||||
// XXX could be clever here and compute a smaller invalidation
|
||||
// rect
|
||||
nsIFrame* frame = mFrameLoader->GetPrimaryFrameOfOwningContent();
|
||||
InvalidateFrame(frame);
|
||||
// NB: we pass INVALIDATE_NO_THEBES_LAYERS here to keep view
|
||||
// semantics the same for both in-process and out-of-process
|
||||
// <browser>. This is just a transform of the layer subtree in
|
||||
// both.
|
||||
InvalidateFrame(mFrameLoader->GetPrimaryFrameOfOwningContent(), nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -328,6 +329,7 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
|
|||
, mDelayRemoteDialogs(false)
|
||||
, mRemoteBrowserShown(false)
|
||||
, mRemoteFrame(false)
|
||||
, mClipSubdocument(true)
|
||||
, mCurrentRemoteFrame(nsnull)
|
||||
, mRemoteBrowser(nsnull)
|
||||
, mRenderMode(RENDER_MODE_DEFAULT)
|
||||
|
@ -1693,7 +1695,11 @@ nsFrameLoader::SetRenderMode(PRUint32 aRenderMode)
|
|||
}
|
||||
|
||||
mRenderMode = aRenderMode;
|
||||
InvalidateFrame(GetPrimaryFrameOfOwningContent());
|
||||
// NB: we pass INVALIDATE_NO_THEBES_LAYERS here to keep view
|
||||
// semantics the same for both in-process and out-of-process
|
||||
// <browser>. This is just a transform of the layer subtree in
|
||||
// both.
|
||||
InvalidateFrame(GetPrimaryFrameOfOwningContent(), nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1711,6 +1717,38 @@ nsFrameLoader::SetEventMode(PRUint32 aEventMode)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrameLoader::GetClipSubdocument(bool* aResult)
|
||||
{
|
||||
*aResult = mClipSubdocument;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrameLoader::SetClipSubdocument(bool aClip)
|
||||
{
|
||||
mClipSubdocument = aClip;
|
||||
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
|
||||
if (frame) {
|
||||
InvalidateFrame(frame, 0);
|
||||
frame->PresContext()->PresShell()->
|
||||
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
|
||||
nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
|
||||
if (subdocFrame) {
|
||||
nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
|
||||
if (subdocRootFrame) {
|
||||
nsIFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()->
|
||||
GetRootScrollFrame();
|
||||
if (subdocRootScrollFrame) {
|
||||
frame->PresContext()->PresShell()->
|
||||
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
nsFrameLoader::GetSubDocumentSize(const nsIFrame *aIFrame)
|
||||
{
|
||||
|
|
|
@ -287,6 +287,8 @@ public:
|
|||
mozilla::dom::Element* GetOwnerContent() { return mOwnerContent; }
|
||||
void SetOwnerContent(mozilla::dom::Element* aContent);
|
||||
|
||||
bool ShouldClipSubdocument() { return mClipSubdocument; }
|
||||
|
||||
private:
|
||||
|
||||
bool ShouldUseRemoteProcess();
|
||||
|
@ -338,7 +340,9 @@ private:
|
|||
|
||||
bool mDelayRemoteDialogs : 1;
|
||||
bool mRemoteBrowserShown : 1;
|
||||
bool mRemoteFrame;
|
||||
bool mRemoteFrame : 1;
|
||||
bool mClipSubdocument : 1;
|
||||
|
||||
// XXX leaking
|
||||
nsCOMPtr<nsIObserver> mChildHost;
|
||||
RenderFrameParent* mCurrentRemoteFrame;
|
||||
|
|
|
@ -236,13 +236,6 @@ WebGLContext::WebGLContext()
|
|||
|
||||
mShaderValidation = true;
|
||||
|
||||
mMapBuffers.Init();
|
||||
mMapTextures.Init();
|
||||
mMapPrograms.Init();
|
||||
mMapShaders.Init();
|
||||
mMapFramebuffers.Init();
|
||||
mMapRenderbuffers.Init();
|
||||
|
||||
mBlackTexturesAreInitialized = false;
|
||||
mFakeBlackStatus = DoNotNeedFakeBlack;
|
||||
|
||||
|
@ -314,72 +307,6 @@ WebGLContext::~WebGLContext()
|
|||
mContextRestorer = nsnull;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
DeleteTextureFunction(const PRUint32& aKey, WebGLTexture *aValue, void *aData)
|
||||
{
|
||||
gl::GLContext *gl = (gl::GLContext *) aData;
|
||||
NS_ASSERTION(!aValue->Deleted(), "Texture is still in mMapTextures, but is deleted?");
|
||||
GLuint name = aValue->GLName();
|
||||
gl->fDeleteTextures(1, &name);
|
||||
aValue->Delete();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
DeleteBufferFunction(const PRUint32& aKey, WebGLBuffer *aValue, void *aData)
|
||||
{
|
||||
gl::GLContext *gl = (gl::GLContext *) aData;
|
||||
NS_ASSERTION(!aValue->Deleted(), "Buffer is still in mMapBuffers, but is deleted?");
|
||||
GLuint name = aValue->GLName();
|
||||
gl->fDeleteBuffers(1, &name);
|
||||
aValue->Delete();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
DeleteFramebufferFunction(const PRUint32& aKey, WebGLFramebuffer *aValue, void *aData)
|
||||
{
|
||||
gl::GLContext *gl = (gl::GLContext *) aData;
|
||||
NS_ASSERTION(!aValue->Deleted(), "Framebuffer is still in mMapFramebuffers, but is deleted?");
|
||||
GLuint name = aValue->GLName();
|
||||
gl->fDeleteFramebuffers(1, &name);
|
||||
aValue->Delete();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
DeleteRenderbufferFunction(const PRUint32& aKey, WebGLRenderbuffer *aValue, void *aData)
|
||||
{
|
||||
gl::GLContext *gl = (gl::GLContext *) aData;
|
||||
NS_ASSERTION(!aValue->Deleted(), "Renderbuffer is still in mMapRenderbuffers, but is deleted?");
|
||||
GLuint name = aValue->GLName();
|
||||
gl->fDeleteRenderbuffers(1, &name);
|
||||
aValue->Delete();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
DeleteProgramFunction(const PRUint32& aKey, WebGLProgram *aValue, void *aData)
|
||||
{
|
||||
gl::GLContext *gl = (gl::GLContext *) aData;
|
||||
NS_ASSERTION(!aValue->Deleted(), "Program is still in mMapPrograms, but is deleted?");
|
||||
GLuint name = aValue->GLName();
|
||||
gl->fDeleteProgram(name);
|
||||
aValue->Delete();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
DeleteShaderFunction(const PRUint32& aKey, WebGLShader *aValue, void *aData)
|
||||
{
|
||||
gl::GLContext *gl = (gl::GLContext *) aData;
|
||||
NS_ASSERTION(!aValue->Deleted(), "Shader is still in mMapShaders, but is deleted?");
|
||||
GLuint name = aValue->GLName();
|
||||
gl->fDeleteShader(name);
|
||||
aValue->Delete();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DestroyResourcesAndContext()
|
||||
{
|
||||
|
@ -388,23 +315,30 @@ WebGLContext::DestroyResourcesAndContext()
|
|||
|
||||
gl->MakeCurrent();
|
||||
|
||||
mMapTextures.EnumerateRead(DeleteTextureFunction, gl);
|
||||
mMapTextures.Clear();
|
||||
mBound2DTextures.Clear();
|
||||
mBoundCubeMapTextures.Clear();
|
||||
mBoundArrayBuffer = nsnull;
|
||||
mBoundElementArrayBuffer = nsnull;
|
||||
mCurrentProgram = nsnull;
|
||||
mBoundFramebuffer = nsnull;
|
||||
mBoundRenderbuffer = nsnull;
|
||||
|
||||
mMapBuffers.EnumerateRead(DeleteBufferFunction, gl);
|
||||
mMapBuffers.Clear();
|
||||
mAttribBuffers.Clear();
|
||||
|
||||
mMapPrograms.EnumerateRead(DeleteProgramFunction, gl);
|
||||
mMapPrograms.Clear();
|
||||
|
||||
mMapShaders.EnumerateRead(DeleteShaderFunction, gl);
|
||||
mMapShaders.Clear();
|
||||
|
||||
mMapFramebuffers.EnumerateRead(DeleteFramebufferFunction, gl);
|
||||
mMapFramebuffers.Clear();
|
||||
|
||||
mMapRenderbuffers.EnumerateRead(DeleteRenderbufferFunction, gl);
|
||||
mMapRenderbuffers.Clear();
|
||||
while (mTextures.Length())
|
||||
mTextures.Last()->DeleteOnce();
|
||||
while (mBuffers.Length())
|
||||
mBuffers.Last()->DeleteOnce();
|
||||
while (mRenderbuffers.Length())
|
||||
mRenderbuffers.Last()->DeleteOnce();
|
||||
while (mFramebuffers.Length())
|
||||
mFramebuffers.Last()->DeleteOnce();
|
||||
while (mShaders.Length())
|
||||
mShaders.Last()->DeleteOnce();
|
||||
while (mPrograms.Length())
|
||||
mPrograms.Last()->DeleteOnce();
|
||||
while (mUniformLocations.Length())
|
||||
mUniformLocations.Last()->DeleteOnce();
|
||||
|
||||
if (mBlackTexturesAreInitialized) {
|
||||
gl->fDeleteTextures(1, &mBlackTexture2D);
|
||||
|
@ -1270,7 +1204,6 @@ NS_IMPL_RELEASE(WebGLBuffer)
|
|||
DOMCI_DATA(WebGLBuffer, WebGLBuffer)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLBuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLBuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLBuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLBuffer)
|
||||
|
@ -1282,7 +1215,6 @@ NS_IMPL_RELEASE(WebGLTexture)
|
|||
DOMCI_DATA(WebGLTexture, WebGLTexture)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLTexture)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLTexture)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLTexture)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLTexture)
|
||||
|
@ -1294,7 +1226,6 @@ NS_IMPL_RELEASE(WebGLProgram)
|
|||
DOMCI_DATA(WebGLProgram, WebGLProgram)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLProgram)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLProgram)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLProgram)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLProgram)
|
||||
|
@ -1306,7 +1237,6 @@ NS_IMPL_RELEASE(WebGLShader)
|
|||
DOMCI_DATA(WebGLShader, WebGLShader)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLShader)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLShader)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLShader)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLShader)
|
||||
|
@ -1318,7 +1248,6 @@ NS_IMPL_RELEASE(WebGLFramebuffer)
|
|||
DOMCI_DATA(WebGLFramebuffer, WebGLFramebuffer)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLFramebuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLFramebuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLFramebuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLFramebuffer)
|
||||
|
@ -1330,7 +1259,6 @@ NS_IMPL_RELEASE(WebGLRenderbuffer)
|
|||
DOMCI_DATA(WebGLRenderbuffer, WebGLRenderbuffer)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLRenderbuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLRenderbuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLRenderbuffer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLRenderbuffer)
|
||||
|
@ -1342,7 +1270,6 @@ NS_IMPL_RELEASE(WebGLUniformLocation)
|
|||
DOMCI_DATA(WebGLUniformLocation, WebGLUniformLocation)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLUniformLocation)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLUniformLocation)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLUniformLocation)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLUniformLocation)
|
||||
|
@ -1354,7 +1281,6 @@ NS_IMPL_RELEASE(WebGLActiveInfo)
|
|||
DOMCI_DATA(WebGLActiveInfo, WebGLActiveInfo)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLActiveInfo)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLActiveInfo)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLActiveInfo)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLActiveInfo)
|
||||
|
@ -1379,7 +1305,6 @@ NS_IMPL_RELEASE(WebGLExtension)
|
|||
DOMCI_DATA(WebGLExtension, WebGLExtension)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLExtension)
|
||||
NS_INTERFACE_MAP_ENTRY(WebGLExtension)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLExtension)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLExtension)
|
||||
|
@ -1391,8 +1316,6 @@ NS_IMPL_RELEASE(WebGLExtensionStandardDerivatives)
|
|||
DOMCI_DATA(WebGLExtensionStandardDerivatives, WebGLExtensionStandardDerivatives)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WebGLExtensionStandardDerivatives)
|
||||
//NS_INTERFACE_MAP_ENTRY(WebGLExtensionStandardDerivatives)
|
||||
//NS_INTERFACE_MAP_ENTRY(WebGLExtension)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebGLExtensionStandardDerivatives)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, WebGLExtension)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebGLExtensionStandardDerivatives)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -121,22 +121,6 @@ NS_IMETHODIMP WebGLContext::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6) { \
|
|||
MakeContextCurrent(); gl->f##glname(a1,a2,a3,a4,a5,a6); return NS_OK; \
|
||||
}
|
||||
|
||||
already_AddRefed<WebGLUniformLocation>
|
||||
WebGLProgram::GetUniformLocationObject(GLint glLocation)
|
||||
{
|
||||
WebGLUniformLocation *existingLocationObject;
|
||||
if (mMapUniformLocations.Get(glLocation, &existingLocationObject)) {
|
||||
return existingLocationObject;
|
||||
}
|
||||
|
||||
if (glLocation < 0) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsRefPtr<WebGLUniformLocation> loc = new WebGLUniformLocation(mContext, this, glLocation);
|
||||
mMapUniformLocations.Put(glLocation, loc);
|
||||
return loc.forget();
|
||||
}
|
||||
|
||||
//
|
||||
// WebGL API
|
||||
|
@ -150,7 +134,7 @@ WebGLContext::ActiveTexture(WebGLenum texture)
|
|||
if (mContextLost)
|
||||
return NS_OK;
|
||||
|
||||
if (texture < LOCAL_GL_TEXTURE0 || texture >= LOCAL_GL_TEXTURE0+mBound2DTextures.Length())
|
||||
if (texture < LOCAL_GL_TEXTURE0 || texture >= LOCAL_GL_TEXTURE0 + mBound2DTextures.Length())
|
||||
return ErrorInvalidEnum("ActiveTexture: texture unit %d out of range (0..%d)",
|
||||
texture, mBound2DTextures.Length()-1);
|
||||
|
||||
|
@ -186,10 +170,6 @@ WebGLContext::AttachShader(nsIWebGLProgram *pobj, nsIWebGLShader *shobj)
|
|||
if (!program->AttachShader(shader))
|
||||
return ErrorInvalidOperation("AttachShader: shader is already attached");
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
gl->fAttachShader(progname, shadername);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1087,13 +1067,8 @@ WebGLContext::CreateProgram(nsIWebGLProgram **retval)
|
|||
|
||||
*retval = nsnull;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
WebGLuint name = gl->fCreateProgram();
|
||||
|
||||
WebGLProgram *prog = new WebGLProgram(this, name);
|
||||
WebGLProgram *prog = new WebGLProgram(this);
|
||||
NS_ADDREF(*retval = prog);
|
||||
mMapPrograms.Put(name, prog);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1112,13 +1087,8 @@ WebGLContext::CreateShader(WebGLenum type, nsIWebGLShader **retval)
|
|||
return ErrorInvalidEnumInfo("createShader: type", type);
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
WebGLuint name = gl->fCreateShader(type);
|
||||
|
||||
WebGLShader *shader = new WebGLShader(this, name, type);
|
||||
WebGLShader *shader = new WebGLShader(this, type);
|
||||
NS_ADDREF(*retval = shader);
|
||||
mMapShaders.Put(name, shader);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1152,11 +1122,17 @@ WebGLContext::DeleteBuffer(nsIWebGLBuffer *bobj)
|
|||
if (isNull || isDeleted)
|
||||
return NS_OK;
|
||||
|
||||
MakeContextCurrent();
|
||||
if (mBoundArrayBuffer == buf)
|
||||
BindBuffer(LOCAL_GL_ARRAY_BUFFER, nsnull);
|
||||
if (mBoundElementArrayBuffer == buf)
|
||||
BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, nsnull);
|
||||
|
||||
gl->fDeleteBuffers(1, &bufname);
|
||||
buf->Delete();
|
||||
mMapBuffers.Remove(bufname);
|
||||
for (int i = 0; i < mGLMaxVertexAttribs; i++) {
|
||||
if (mAttribBuffers[i].buf == buf)
|
||||
mAttribBuffers[i].buf = nsnull;
|
||||
}
|
||||
|
||||
buf->RequestDelete();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1167,8 +1143,8 @@ WebGLContext::DeleteFramebuffer(nsIWebGLFramebuffer *fbobj)
|
|||
if (mContextLost)
|
||||
return NS_OK;
|
||||
|
||||
WebGLuint fbufname;
|
||||
WebGLFramebuffer *fbuf;
|
||||
WebGLuint fbufname;
|
||||
bool isNull, isDeleted;
|
||||
if (!GetConcreteObjectAndGLName("deleteFramebuffer", fbobj, &fbuf, &fbufname, &isNull, &isDeleted))
|
||||
return NS_OK;
|
||||
|
@ -1176,14 +1152,10 @@ WebGLContext::DeleteFramebuffer(nsIWebGLFramebuffer *fbobj)
|
|||
if (isNull || isDeleted)
|
||||
return NS_OK;
|
||||
|
||||
MakeContextCurrent();
|
||||
fbuf->RequestDelete();
|
||||
|
||||
gl->fDeleteFramebuffers(1, &fbufname);
|
||||
fbuf->Delete();
|
||||
mMapFramebuffers.Remove(fbufname);
|
||||
|
||||
if (mBoundFramebuffer && mBoundFramebuffer->GLName() == fbufname)
|
||||
mBoundFramebuffer = NULL;
|
||||
if (mBoundFramebuffer == fbuf)
|
||||
BindFramebuffer(LOCAL_GL_FRAMEBUFFER, nsnull);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1194,8 +1166,8 @@ WebGLContext::DeleteRenderbuffer(nsIWebGLRenderbuffer *rbobj)
|
|||
if (mContextLost)
|
||||
return NS_OK;
|
||||
|
||||
WebGLuint rbufname;
|
||||
WebGLRenderbuffer *rbuf;
|
||||
WebGLuint rbufname;
|
||||
bool isNull, isDeleted;
|
||||
if (!GetConcreteObjectAndGLName("deleteRenderbuffer", rbobj, &rbuf, &rbufname, &isNull, &isDeleted))
|
||||
return NS_OK;
|
||||
|
@ -1203,26 +1175,13 @@ WebGLContext::DeleteRenderbuffer(nsIWebGLRenderbuffer *rbobj)
|
|||
if (isNull || isDeleted)
|
||||
return NS_OK;
|
||||
|
||||
MakeContextCurrent();
|
||||
if (mBoundFramebuffer)
|
||||
mBoundFramebuffer->DetachRenderbuffer(rbuf);
|
||||
|
||||
// XXX we need to track renderbuffer attachments; from glDeleteRenderbuffers man page:
|
||||
if (mBoundRenderbuffer == rbuf)
|
||||
BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nsnull);
|
||||
|
||||
/*
|
||||
If a renderbuffer object that is currently bound is deleted, the binding reverts
|
||||
to 0 (the absence of any renderbuffer object). Additionally, special care
|
||||
must be taken when deleting a renderbuffer object if the image of the renderbuffer
|
||||
is attached to a framebuffer object. In this case, if the deleted renderbuffer object is
|
||||
attached to the currently bound framebuffer object, it is
|
||||
automatically detached. However, attachments to any other framebuffer objects are the
|
||||
responsibility of the application.
|
||||
*/
|
||||
|
||||
gl->fDeleteRenderbuffers(1, &rbufname);
|
||||
rbuf->Delete();
|
||||
mMapRenderbuffers.Remove(rbufname);
|
||||
|
||||
if (mBoundRenderbuffer && mBoundRenderbuffer->GLName() == rbufname)
|
||||
mBoundRenderbuffer = NULL;
|
||||
rbuf->RequestDelete();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1233,8 +1192,8 @@ WebGLContext::DeleteTexture(nsIWebGLTexture *tobj)
|
|||
if (mContextLost)
|
||||
return NS_OK;
|
||||
|
||||
WebGLuint texname;
|
||||
WebGLTexture *tex;
|
||||
WebGLuint texname;
|
||||
bool isNull, isDeleted;
|
||||
if (!GetConcreteObjectAndGLName("deleteTexture", tobj, &tex, &texname, &isNull, &isDeleted))
|
||||
return NS_OK;
|
||||
|
@ -1242,11 +1201,20 @@ WebGLContext::DeleteTexture(nsIWebGLTexture *tobj)
|
|||
if (isNull || isDeleted)
|
||||
return NS_OK;
|
||||
|
||||
MakeContextCurrent();
|
||||
if (mBoundFramebuffer)
|
||||
mBoundFramebuffer->DetachTexture(tex);
|
||||
|
||||
gl->fDeleteTextures(1, &texname);
|
||||
tex->Delete();
|
||||
mMapTextures.Remove(texname);
|
||||
for (int i = 0; i < mGLMaxTextureUnits; i++) {
|
||||
if ((tex->Target() == LOCAL_GL_TEXTURE_2D && mBound2DTextures[i] == tex) ||
|
||||
(tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP && mBoundCubeMapTextures[i] == tex))
|
||||
{
|
||||
ActiveTexture(LOCAL_GL_TEXTURE0 + i);
|
||||
BindTexture(tex->Target(), nsnull);
|
||||
}
|
||||
}
|
||||
ActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
|
||||
|
||||
tex->RequestDelete();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1266,12 +1234,7 @@ WebGLContext::DeleteProgram(nsIWebGLProgram *pobj)
|
|||
if (isNull || isDeleted)
|
||||
return NS_OK;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
gl->fDeleteProgram(progname);
|
||||
|
||||
prog->DeleteWhenNotCurrent();
|
||||
mMapPrograms.Remove(progname);
|
||||
prog->RequestDelete();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1291,11 +1254,7 @@ WebGLContext::DeleteShader(nsIWebGLShader *sobj)
|
|||
if (isNull || isDeleted)
|
||||
return NS_OK;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
gl->fDeleteShader(shadername);
|
||||
shader->DeleteWhenNotAttached();
|
||||
mMapShaders.Remove(shadername);
|
||||
shader->RequestDelete();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1319,12 +1278,6 @@ WebGLContext::DetachShader(nsIWebGLProgram *pobj, nsIWebGLShader *shobj)
|
|||
if (!program->DetachShader(shader))
|
||||
return ErrorInvalidOperation("DetachShader: shader is not attached");
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
gl->fDetachShader(progname, shadername);
|
||||
|
||||
shader->DetachedFromProgram();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2555,14 +2508,8 @@ WebGLContext::CreateBuffer(nsIWebGLBuffer **retval)
|
|||
|
||||
*retval = nsnull;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
WebGLuint name;
|
||||
gl->fGenBuffers(1, &name);
|
||||
|
||||
WebGLBuffer *globj = new WebGLBuffer(this, name);
|
||||
WebGLBuffer *globj = new WebGLBuffer(this);
|
||||
NS_ADDREF(*retval = globj);
|
||||
mMapBuffers.Put(name, globj);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2577,12 +2524,8 @@ WebGLContext::CreateTexture(nsIWebGLTexture **retval)
|
|||
|
||||
MakeContextCurrent();
|
||||
|
||||
WebGLuint name;
|
||||
gl->fGenTextures(1, &name);
|
||||
|
||||
WebGLTexture *globj = new WebGLTexture(this, name);
|
||||
WebGLTexture *globj = new WebGLTexture(this);
|
||||
NS_ADDREF(*retval = globj);
|
||||
mMapTextures.Put(name, globj);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2611,7 +2554,8 @@ WebGLContext::GetProgramParameter(nsIWebGLProgram *pobj, PRUint32 pname, nsIVari
|
|||
|
||||
WebGLuint progname;
|
||||
bool isDeleted;
|
||||
if (!GetGLName<WebGLProgram>("getProgramParameter: program", pobj, &progname, nsnull, &isDeleted))
|
||||
WebGLProgram *prog;
|
||||
if (!GetConcreteObjectAndGLName("getProgramParameter: program", pobj, &prog, &progname, nsnull, &isDeleted))
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> wrval = do_CreateInstance("@mozilla.org/variant;1");
|
||||
|
@ -2634,6 +2578,8 @@ WebGLContext::GetProgramParameter(nsIWebGLProgram *pobj, PRUint32 pname, nsIVari
|
|||
}
|
||||
break;
|
||||
case LOCAL_GL_DELETE_STATUS:
|
||||
wrval->SetAsBool(prog->IsDeleteRequested());
|
||||
break;
|
||||
case LOCAL_GL_LINK_STATUS:
|
||||
{
|
||||
GLint i = 0;
|
||||
|
@ -2992,9 +2938,10 @@ WebGLContext::GetUniformLocation(nsIWebGLProgram *pobj, const nsAString& name, n
|
|||
|
||||
GLint intlocation = gl->fGetUniformLocation(progname, NS_LossyConvertUTF16toASCII(name).get());
|
||||
|
||||
nsRefPtr<nsIWebGLUniformLocation> loc = prog->GetUniformLocationObject(intlocation);
|
||||
*retval = loc.forget().get();
|
||||
|
||||
WebGLUniformLocation *loc = nsnull;
|
||||
if (intlocation >= 0)
|
||||
NS_ADDREF(loc = new WebGLUniformLocation(this, prog, intlocation));
|
||||
*retval = loc;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3123,15 +3070,9 @@ WebGLContext::IsBuffer(nsIWebGLBuffer *bobj, WebGLboolean *retval)
|
|||
bool isDeleted;
|
||||
WebGLuint buffername;
|
||||
WebGLBuffer *buffer;
|
||||
bool ok = GetConcreteObjectAndGLName("isBuffer", bobj, &buffer, &buffername, nsnull, &isDeleted) &&
|
||||
!isDeleted &&
|
||||
buffer->HasEverBeenBound();
|
||||
if (ok) {
|
||||
MakeContextCurrent();
|
||||
ok = gl->fIsBuffer(buffername);
|
||||
}
|
||||
|
||||
*retval = ok;
|
||||
*retval = GetConcreteObjectAndGLName("isBuffer", bobj, &buffer, &buffername, nsnull, &isDeleted) &&
|
||||
!isDeleted &&
|
||||
buffer->HasEverBeenBound();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3147,15 +3088,9 @@ WebGLContext::IsFramebuffer(nsIWebGLFramebuffer *fbobj, WebGLboolean *retval)
|
|||
bool isDeleted;
|
||||
WebGLuint fbname;
|
||||
WebGLFramebuffer *fb;
|
||||
bool ok = GetConcreteObjectAndGLName("isFramebuffer", fbobj, &fb, &fbname, nsnull, &isDeleted) &&
|
||||
!isDeleted &&
|
||||
fb->HasEverBeenBound();
|
||||
if (ok) {
|
||||
MakeContextCurrent();
|
||||
ok = gl->fIsFramebuffer(fbname);
|
||||
}
|
||||
|
||||
*retval = ok;
|
||||
*retval = GetConcreteObjectAndGLName("isFramebuffer", fbobj, &fb, &fbname, nsnull, &isDeleted) &&
|
||||
!isDeleted &&
|
||||
fb->HasEverBeenBound();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3170,10 +3105,8 @@ WebGLContext::IsProgram(nsIWebGLProgram *pobj, WebGLboolean *retval)
|
|||
|
||||
bool isDeleted;
|
||||
WebGLProgram *prog = nsnull;
|
||||
bool ok = GetConcreteObject("isProgram", pobj, &prog, nsnull, &isDeleted, false) &&
|
||||
!isDeleted;
|
||||
|
||||
*retval = ok;
|
||||
*retval = GetConcreteObject("isProgram", pobj, &prog, nsnull, &isDeleted, false) &&
|
||||
!isDeleted;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3189,15 +3122,9 @@ WebGLContext::IsRenderbuffer(nsIWebGLRenderbuffer *rbobj, WebGLboolean *retval)
|
|||
bool isDeleted;
|
||||
WebGLuint rbname;
|
||||
WebGLRenderbuffer *rb;
|
||||
bool ok = GetConcreteObjectAndGLName("isRenderBuffer", rbobj, &rb, &rbname, nsnull, &isDeleted) &&
|
||||
!isDeleted &&
|
||||
rb->HasEverBeenBound();
|
||||
if (ok) {
|
||||
MakeContextCurrent();
|
||||
ok = gl->fIsRenderbuffer(rbname);
|
||||
}
|
||||
|
||||
*retval = ok;
|
||||
*retval = GetConcreteObjectAndGLName("isRenderBuffer", rbobj, &rb, &rbname, nsnull, &isDeleted) &&
|
||||
!isDeleted &&
|
||||
rb->HasEverBeenBound();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3212,10 +3139,8 @@ WebGLContext::IsShader(nsIWebGLShader *sobj, WebGLboolean *retval)
|
|||
|
||||
bool isDeleted;
|
||||
WebGLShader *shader = nsnull;
|
||||
bool ok = GetConcreteObject("isShader", sobj, &shader, nsnull, &isDeleted, false) &&
|
||||
!isDeleted;
|
||||
|
||||
*retval = ok;
|
||||
*retval = GetConcreteObject("isShader", sobj, &shader, nsnull, &isDeleted, false) &&
|
||||
!isDeleted;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3231,15 +3156,9 @@ WebGLContext::IsTexture(nsIWebGLTexture *tobj, WebGLboolean *retval)
|
|||
bool isDeleted;
|
||||
WebGLuint texname;
|
||||
WebGLTexture *tex;
|
||||
bool ok = GetConcreteObjectAndGLName("isTexture", tobj, &tex, &texname, nsnull, &isDeleted) &&
|
||||
!isDeleted &&
|
||||
tex->HasEverBeenBound();
|
||||
if (ok) {
|
||||
MakeContextCurrent();
|
||||
ok = gl->fIsTexture(texname);
|
||||
}
|
||||
|
||||
*retval = ok;
|
||||
*retval = GetConcreteObjectAndGLName("isTexture", tobj, &tex, &texname, nsnull, &isDeleted) &&
|
||||
!isDeleted &&
|
||||
tex->HasEverBeenBound();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -4336,12 +4255,8 @@ WebGLContext::UseProgram(nsIWebGLProgram *pobj)
|
|||
|
||||
gl->fUseProgram(progname);
|
||||
|
||||
WebGLProgram* previous = mCurrentProgram;
|
||||
mCurrentProgram = prog;
|
||||
|
||||
if (previous)
|
||||
previous->NoLongerCurrent();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -4376,14 +4291,8 @@ WebGLContext::CreateFramebuffer(nsIWebGLFramebuffer **retval)
|
|||
|
||||
*retval = 0;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
GLuint name;
|
||||
gl->fGenFramebuffers(1, &name);
|
||||
|
||||
WebGLFramebuffer *globj = new WebGLFramebuffer(this, name);
|
||||
WebGLFramebuffer *globj = new WebGLFramebuffer(this);
|
||||
NS_ADDREF(*retval = globj);
|
||||
mMapFramebuffers.Put(name, globj);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -4396,14 +4305,8 @@ WebGLContext::CreateRenderbuffer(nsIWebGLRenderbuffer **retval)
|
|||
|
||||
*retval = 0;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
GLuint name;
|
||||
gl->fGenRenderbuffers(1, &name);
|
||||
|
||||
WebGLRenderbuffer *globj = new WebGLRenderbuffer(this, name);
|
||||
WebGLRenderbuffer *globj = new WebGLRenderbuffer(this);
|
||||
NS_ADDREF(*retval = globj);
|
||||
mMapRenderbuffers.Put(name, globj);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -4566,6 +4469,8 @@ WebGLContext::GetShaderParameter(nsIWebGLShader *sobj, WebGLenum pname, nsIVaria
|
|||
}
|
||||
break;
|
||||
case LOCAL_GL_DELETE_STATUS:
|
||||
wrval->SetAsBool(shader->IsDeleteRequested());
|
||||
break;
|
||||
case LOCAL_GL_COMPILE_STATUS:
|
||||
{
|
||||
GLint i = 0;
|
||||
|
|
|
@ -57,10 +57,10 @@ using namespace mozilla;
|
|||
bool
|
||||
WebGLProgram::UpdateInfo(gl::GLContext *gl)
|
||||
{
|
||||
gl->fGetProgramiv(mName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &mAttribMaxNameLength);
|
||||
gl->fGetProgramiv(mName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &mUniformMaxNameLength);
|
||||
gl->fGetProgramiv(mName, LOCAL_GL_ACTIVE_UNIFORMS, &mUniformCount);
|
||||
gl->fGetProgramiv(mName, LOCAL_GL_ACTIVE_ATTRIBUTES, &mAttribCount);
|
||||
gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &mAttribMaxNameLength);
|
||||
gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &mUniformMaxNameLength);
|
||||
gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_UNIFORMS, &mUniformCount);
|
||||
gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &mAttribCount);
|
||||
|
||||
GLint numVertexAttribs;
|
||||
if (mContext->MinCapabilityMode()) {
|
||||
|
@ -77,9 +77,9 @@ WebGLProgram::UpdateInfo(gl::GLContext *gl)
|
|||
GLint attrnamelen;
|
||||
GLint attrsize;
|
||||
GLenum attrtype;
|
||||
gl->fGetActiveAttrib(mName, i, mAttribMaxNameLength, &attrnamelen, &attrsize, &attrtype, nameBuf);
|
||||
gl->fGetActiveAttrib(mGLName, i, mAttribMaxNameLength, &attrnamelen, &attrsize, &attrtype, nameBuf);
|
||||
if (attrnamelen > 0) {
|
||||
GLint loc = gl->fGetAttribLocation(mName, nameBuf);
|
||||
GLint loc = gl->fGetAttribLocation(mGLName, nameBuf);
|
||||
mAttribsInUse[loc] = true;
|
||||
}
|
||||
}
|
||||
|
@ -515,7 +515,6 @@ WebGLContext::InitAndValidateGL()
|
|||
|
||||
mAttribBuffers.Clear();
|
||||
|
||||
mUniformTextures.Clear();
|
||||
mBound2DTextures.Clear();
|
||||
mBoundCubeMapTextures.Clear();
|
||||
|
||||
|
@ -526,13 +525,6 @@ WebGLContext::InitAndValidateGL()
|
|||
mBoundFramebuffer = nsnull;
|
||||
mBoundRenderbuffer = nsnull;
|
||||
|
||||
mMapTextures.Clear();
|
||||
mMapBuffers.Clear();
|
||||
mMapPrograms.Clear();
|
||||
mMapShaders.Clear();
|
||||
mMapFramebuffers.Clear();
|
||||
mMapRenderbuffers.Clear();
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
// on desktop OpenGL, we always keep vertex attrib 0 array enabled
|
||||
|
|
|
@ -44,8 +44,6 @@ namespace mozilla {
|
|||
class WebGLExtensionLoseContext;
|
||||
class WebGLExtensionStandardDerivatives;
|
||||
|
||||
#define WEBGLEXTENSIONLOSECONTEXT_PRIVATE_IID \
|
||||
{0xb0afc2eb, 0x0895, 0x4509, {0x98, 0xde, 0x5c, 0x38, 0x3d, 0x16, 0x06, 0x94}}
|
||||
class WebGLExtensionLoseContext :
|
||||
public nsIWebGLExtensionLoseContext,
|
||||
public WebGLExtension
|
||||
|
@ -56,14 +54,8 @@ public:
|
|||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIWEBGLEXTENSIONLOSECONTEXT
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLEXTENSIONLOSECONTEXT_PRIVATE_IID)
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(WebGLExtensionLoseContext, WEBGLACTIVEINFO_PRIVATE_IID)
|
||||
|
||||
#define WEBGLEXTENSIONSTANDARDDERIVATIVES_PRIVATE_IID \
|
||||
{0x3de3dfd9, 0x864a, 0x4e4c, {0x98, 0x9b, 0x29, 0x77, 0xea, 0xa8, 0x0b, 0x7b}}
|
||||
class WebGLExtensionStandardDerivatives :
|
||||
public nsIWebGLExtensionStandardDerivatives,
|
||||
public WebGLExtension
|
||||
|
@ -74,12 +66,8 @@ public:
|
|||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIWEBGLEXTENSION
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLEXTENSIONSTANDARDDERIVATIVES_PRIVATE_IID)
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(WebGLExtensionStandardDerivatives, WEBGLACTIVEINFO_PRIVATE_IID)
|
||||
|
||||
}
|
||||
|
||||
#endif // WEBGLEXTENSIONS_H_
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
This is a local copy of the WebGL conformance suite, SVN revision 15981
|
||||
This is a local copy of the WebGL conformance suite, SVN revision 16237
|
||||
|
||||
The canonical location for this testsuite is:
|
||||
|
||||
|
|
|
@ -11,13 +11,15 @@ var wtu = WebGLTestUtils;
|
|||
var canvas;
|
||||
var gl;
|
||||
var shouldGenerateGLError;
|
||||
var extension_name = "WEBKIT_lose_context";
|
||||
var extension_name = "WEBGL_EXT_lose_context";
|
||||
var extension;
|
||||
var bufferObjects;
|
||||
var program;
|
||||
var texture;
|
||||
var texColor = [255, 10, 20, 255];
|
||||
var allowRestore;
|
||||
var contextLostEventFired;
|
||||
var contextRestoredEventFired;
|
||||
|
||||
function init()
|
||||
{
|
||||
|
@ -28,15 +30,14 @@ function init()
|
|||
description("Tests behavior under a restored context.");
|
||||
|
||||
shouldGenerateGLError = wtu.shouldGenerateGLError;
|
||||
runTests();
|
||||
testLosingContext();
|
||||
}
|
||||
|
||||
function runTests()
|
||||
function runTest1()
|
||||
{
|
||||
testLosingContext();
|
||||
testLosingAndRestoringContext();
|
||||
|
||||
finish();
|
||||
finishTest();
|
||||
}
|
||||
|
||||
function setupTest()
|
||||
|
@ -56,35 +57,75 @@ function setupTest()
|
|||
function testLosingContext()
|
||||
{
|
||||
if (!setupTest())
|
||||
return;
|
||||
finishTest();
|
||||
|
||||
debug("Test losing a context and inability to restore it.");
|
||||
|
||||
canvas.addEventListener("webglcontextlost", testLostContext);
|
||||
canvas.addEventListener("webglcontextlost", function() {
|
||||
testLostContext();
|
||||
// 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()");
|
||||
testLosingAndRetoreingContext();
|
||||
}, 1);
|
||||
});
|
||||
canvas.addEventListener("webglcontextrestored", testShouldNotRestoreContext);
|
||||
allowRestore = false;
|
||||
contextLostEventFired = false;
|
||||
contextRestoredEventFired = false;
|
||||
|
||||
testOriginalContext();
|
||||
extension.loseContext();
|
||||
shouldGenerateGLError(gl, gl.INVALID_OPERATION, "extension.restoreContext()");
|
||||
debug("");
|
||||
// The context should be lost immediately.
|
||||
shouldBeTrue("gl.isContextLost()");
|
||||
shouldBe("gl.getError()", "gl.CONTEXT_LOST_WEBGL");
|
||||
shouldBe("gl.getError()", "gl.NO_ERROR");
|
||||
// gl methods should be no-ops
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP));
|
||||
// but the event should not have been fired.
|
||||
shouldBeFalse("contextLostEventFired");
|
||||
}
|
||||
|
||||
function testLosingAndRestoringContext()
|
||||
{
|
||||
if (!setupTest())
|
||||
return;
|
||||
finishTest();
|
||||
|
||||
debug("");
|
||||
debug("Test losing and restoring a context.");
|
||||
|
||||
canvas.addEventListener("webglcontextlost", testLostContext);
|
||||
canvas.addEventListener("webglcontextrestored", testRestoredContext);
|
||||
canvas.addEventListener("webglcontextlost", function() {
|
||||
testLostContext();
|
||||
// restore the context after this event has exited.
|
||||
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);
|
||||
});
|
||||
canvas.addEventListener("webglcontextrestored", function() {
|
||||
testRestoredContext();
|
||||
finishTest();
|
||||
});
|
||||
allowRestore = true;
|
||||
contextLostEventFired = false;
|
||||
contextRestoredEventFired = false;
|
||||
|
||||
testOriginalContext();
|
||||
extension.loseContext();
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()");
|
||||
debug("");
|
||||
// The context should be lost immediately.
|
||||
shouldBeTrue("gl.isContextLost()");
|
||||
shouldBe("gl.getError()", "gl.CONTEXT_LOST_WEBGL");
|
||||
shouldBe("gl.getError()", "gl.NO_ERROR");
|
||||
// gl methods should be no-ops
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP));
|
||||
// but the event should not have been fired.
|
||||
shouldBeFalse("contextLostEventFired");
|
||||
}
|
||||
|
||||
function testRendering()
|
||||
|
@ -118,12 +159,13 @@ function testOriginalContext()
|
|||
function testLostContext(e)
|
||||
{
|
||||
debug("Test lost context");
|
||||
shouldBeFalse("contextLostEventFired");
|
||||
contextLostEventFired = true;
|
||||
shouldBeTrue("gl.isContextLost()");
|
||||
shouldBe("gl.getError()", "gl.CONTEXT_LOST_WEBGL");
|
||||
shouldBe("gl.getError()", "gl.NO_ERROR");
|
||||
debug("");
|
||||
if (allowRestore)
|
||||
e.preventDefault();
|
||||
if (allowRestore)
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function testShouldNotRestoreContext(e)
|
||||
|
@ -147,6 +189,8 @@ function testResources(expected)
|
|||
function testRestoredContext()
|
||||
{
|
||||
debug("Test restored context");
|
||||
shouldBeFalse("contextRestoredEventFired");
|
||||
contextRestoredEventFired = true;
|
||||
shouldBeFalse("gl.isContextLost()");
|
||||
shouldBe("gl.getError()", "gl.NO_ERROR");
|
||||
|
||||
|
@ -160,16 +204,6 @@ function testRestoredContext()
|
|||
debug("");
|
||||
}
|
||||
|
||||
function finish() {
|
||||
successfullyParsed = true;
|
||||
var epilogue = document.createElement("script");
|
||||
epilogue.onload = function() {
|
||||
if (window.nonKhronosFrameworkNotifyDone)
|
||||
window.nonKhronosFrameworkNotifyDone();
|
||||
};
|
||||
epilogue.src = "../../resources/js-test-post.js";
|
||||
document.body.appendChild(epilogue);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
|
|
@ -11,7 +11,7 @@ var wtu;
|
|||
var canvas;
|
||||
var gl;
|
||||
var shouldGenerateGLError;
|
||||
var extension_name = "WEBKIT_lose_context";
|
||||
var extension_name = "WEBGL_EXT_lose_context";
|
||||
var extension;
|
||||
|
||||
var buffer;
|
||||
|
@ -51,7 +51,7 @@ function init()
|
|||
extension = gl.getExtension(extension_name);
|
||||
if (!extension) {
|
||||
debug(extension_name + " extension not found.");
|
||||
finish();
|
||||
finishTest();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -65,8 +65,8 @@ function loseContext()
|
|||
debug("");
|
||||
debug("Lose context");
|
||||
|
||||
// Note: this will cause the context to be lost, and the
|
||||
// webglcontextlost event listener to be called, immediately.
|
||||
// Note: this will cause the context to be lost, but the
|
||||
// webglcontextlost event listener to be queued.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "extension.loseContext()");
|
||||
debug("");
|
||||
}
|
||||
|
@ -302,19 +302,9 @@ function testLostContext()
|
|||
|
||||
debug("");
|
||||
|
||||
finish();
|
||||
finishTest();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
successfullyParsed = true;
|
||||
var epilogue = document.createElement("script");
|
||||
epilogue.onload = function() {
|
||||
if (window.nonKhronosFrameworkNotifyDone)
|
||||
window.nonKhronosFrameworkNotifyDone();
|
||||
};
|
||||
epilogue.src = "../../resources/js-test-post.js";
|
||||
document.body.appendChild(epilogue);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="init()">
|
||||
|
|
|
@ -3,4 +3,6 @@ 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
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
var img_4x4_rgb_dxt1 = [
|
||||
0xe0,0x07,0x00,0xf8,0x11,0x10,0x15,0x00,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_4x4_rgba_dxt1 = [
|
||||
0xe0,0x07,0x00,0xf8,0x13,0x10,0x15,0x00,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_4x4_rgba_dxt3 = [
|
||||
0xf6,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_4x4_rgba_dxt5 = [
|
||||
0xf6,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_4x4_rgba_raw = [
|
||||
0xff,0x00,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_8x8_rgb_dxt1 = [
|
||||
0xe0,0x07,0x00,0xf8,0x11,0x10,0x15,0x00,0x1f,0x00,0xe0,0xff,0x11,0x10,0x15,0x00,0xe0,0x07,0x1f,0xf8,0x44,0x45,0x40,0x55,0x1f,0x00,0xff,0x07,0x44,0x45,0x40,0x55,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_8x8_rgba_dxt1 = [
|
||||
0xe0,0x07,0x00,0xf8,0x13,0x13,0x15,0x00,0x1f,0x00,0xe0,0xff,0x11,0x10,0x15,0x00,0xe0,0x07,0x1f,0xf8,0x44,0x45,0x43,0x57,0x1f,0x00,0xff,0x07,0x44,0x45,0x40,0x55,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_8x8_rgba_dxt3 = [
|
||||
0xf6,0xff,0xf6,0xff,0xff,0xff,0xff,0xff,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0xff,0x1f,0x00,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xff,0x1f,0xf8,0xe0,0x07,0x11,0x10,0x15,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x1f,0x00,0x11,0x10,0x15,0x00,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_8x8_rgba_dxt5 = [
|
||||
0xff,0x69,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xff,0x1f,0x00,0x44,0x45,0x40,0x55,0xff,0x69,0x00,0x00,0x00,0x01,0x10,0x00,0x1f,0xf8,0xe0,0x07,0x11,0x10,0x15,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x07,0x1f,0x00,0x11,0x10,0x15,0x00,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_8x8_rgba_pvrtc4bpp = [
|
||||
0x33,0x30,0x3f,0x00,0xe0,0x83,0x00,0xfc,0xcc,0xcf,0xc0,0xff,0xe0,0x83,0x1f,0xfc,0xcc,0xcf,0xc0,0xff,0xe0,0xff,0x1f,0x80,0x33,0x30,0x3f,0x00,0xfe,0x83,0x1f,0x80,
|
||||
];
|
|
@ -0,0 +1,3 @@
|
|||
var img_8x8_rgba_raw = [
|
||||
0xff,0x00,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0x69,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x69,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
|
||||
];
|
|
@ -0,0 +1,520 @@
|
|||
<!--
|
||||
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 WEBGL_EXPERIMENTAL_compressed_textures Conformance Tests</title>
|
||||
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
|
||||
<style>
|
||||
img {
|
||||
border: 1px solid black;
|
||||
margin-right: 1em;
|
||||
}
|
||||
.testimages {
|
||||
}
|
||||
|
||||
.testimages br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.testimages > div {
|
||||
float: left;
|
||||
margin: 1em;
|
||||
}
|
||||
</style>
|
||||
<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>
|
||||
<script src="../resources/webgl-test-utils.js"></script>
|
||||
<script src="compressed-textures/4x4.rgb.dxt1.js"></script>
|
||||
<script src="compressed-textures/4x4.rgba.dxt1.js"></script>
|
||||
<script src="compressed-textures/4x4.rgba.dxt5.js"></script>
|
||||
<script src="compressed-textures/8x8.rgb.dxt1.js"></script>
|
||||
<script src="compressed-textures/8x8.rgba.dxt1.js"></script>
|
||||
<script src="compressed-textures/8x8.rgba.dxt5.js"></script>
|
||||
<script src="compressed-textures/8x8.rgba.pvrtc4bpp.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="description"></div>
|
||||
<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas>
|
||||
<div id="console"></div>
|
||||
<!-- Shaders for testing standard derivatives -->
|
||||
<script>
|
||||
description("This test verifies the functionality of the OES_vertex_array_object extension, if it is available.");
|
||||
|
||||
debug("");
|
||||
|
||||
var wtu = WebGLTestUtils;
|
||||
var canvas = document.getElementById("canvas");
|
||||
var gl = wtu.create3DContext(canvas, {antialias: false});
|
||||
var program = wtu.setupTexturedQuad(gl);
|
||||
var ext = null;
|
||||
var vao = null;
|
||||
var validFormats = {
|
||||
COMPRESSED_RGB_S3TC_DXT1_EXT : 0x83F0,
|
||||
COMPRESSED_RGBA_S3TC_DXT1_EXT : 0x83F1,
|
||||
COMPRESSED_RGBA_S3TC_DXT5_EXT : 0x83F3,
|
||||
ETC1_RGB8_OES : 0x8D64,
|
||||
COMPRESSED_RGB_PVRTC_4BPPV1_IMG : 0x8C00,
|
||||
COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0x8C02
|
||||
};
|
||||
var name;
|
||||
var supportedFormats;
|
||||
|
||||
if (!gl) {
|
||||
testFailed("WebGL context does not exist");
|
||||
} else {
|
||||
testPassed("WebGL context exists");
|
||||
|
||||
// Run tests with extension disabled
|
||||
runTestDisabled();
|
||||
|
||||
// Query the extension and store globally so shouldBe can access it
|
||||
ext = gl.getExtension("WEBGL_EXPERIMENTAL_compressed_textures");
|
||||
if (!ext) {
|
||||
testPassed("No WEBGL_EXPERIMENTAL_compressed_textures support -- this is legal");
|
||||
runSupportedTest(false);
|
||||
} else {
|
||||
testPassed("Successfully enabled WEBGL_EXPERIMENTAL_compressed_textures extension");
|
||||
|
||||
runSupportedTest(true);
|
||||
runTestExtension();
|
||||
}
|
||||
}
|
||||
|
||||
function runSupportedTest(extensionEnabled) {
|
||||
var supported = gl.getSupportedExtensions();
|
||||
if (supported.indexOf("WEBGL_EXPERIMENTAL_compressed_textures") >= 0) {
|
||||
if (extensionEnabled) {
|
||||
testPassed("WEBGL_EXPERIMENTAL_compressed_textures listed as supported and getExtension succeeded");
|
||||
} else {
|
||||
testFailed("WEBGL_EXPERIMENTAL_compressed_textures listed as supported but getExtension failed");
|
||||
}
|
||||
} else {
|
||||
if (extensionEnabled) {
|
||||
testFailed("WEBGL_EXPERIMENTAL_compressed_textures not listed as supported but getExtension succeeded");
|
||||
} else {
|
||||
testPassed("WEBGL_EXPERIMENTAL_compressed_textures not listed as supported and getExtension failed -- this is legal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function runTestDisabled() {
|
||||
debug("Testing binding enum with extension disabled");
|
||||
|
||||
// Default value is null
|
||||
if (gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS) === null) {
|
||||
testPassed("Default value of COMPRESSED_TEXTURE_FORMATS is null");
|
||||
} else {
|
||||
testFailed("Default value of COMPRESSED_TEXTURE_FORMATS is not null");
|
||||
}
|
||||
}
|
||||
|
||||
function checkIsValidFormat(format) {
|
||||
for (var name in validFormats) {
|
||||
if (format == validFormats[name]) {
|
||||
testPassed("supported format " + formatToString(format) + " is valid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
testFailed("supported format " + formatToString(format) + " is not valid");
|
||||
}
|
||||
|
||||
function formatToString(format) {
|
||||
for (var p in ext) {
|
||||
if (ext[p] == format) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return "0x" + format.toString(16);
|
||||
}
|
||||
|
||||
function runTestExtension() {
|
||||
debug("Testing WEBGL_EXPERIMENTAL_compressed_textures");
|
||||
|
||||
// check that all format enums exist.
|
||||
for (name in validFormats) {
|
||||
var expected = "0x" + validFormats[name].toString(16);
|
||||
var actual = "ext['" + name + "']";
|
||||
shouldBe(actual, expected);
|
||||
}
|
||||
|
||||
supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
|
||||
// Even if no formats are supported this should return an array.
|
||||
shouldBeTrue("supportedFormats.length !== undefined");
|
||||
|
||||
// check that each format is an allowed format
|
||||
for (var ii = 0; ii < supportedFormats.length; ++ii) {
|
||||
checkIsValidFormat(supportedFormats[ii]);
|
||||
}
|
||||
|
||||
// Test each format
|
||||
for (var ii = 0; ii < supportedFormats.length; ++ii) {
|
||||
switch (supportedFormats[ii]) {
|
||||
case ext.COMPRESSED_RGB_S3TC_DXT1_EXT:
|
||||
testDXT1_RGB();
|
||||
break;
|
||||
case ext.COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
testDXT1_RGBA();
|
||||
break;
|
||||
case ext.COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
testDXT5_RGBA();
|
||||
break;
|
||||
case ext.ETC1_RGB8_OES:
|
||||
testETC1_RGB8();
|
||||
break;
|
||||
case ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
|
||||
testPVRTC_RGB_4BPPV1();
|
||||
break;
|
||||
case ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
|
||||
testPVRTC_RGBA_4BPPV1();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testDXT1_RGB() {
|
||||
var tests = [
|
||||
{ width: 4,
|
||||
height: 4,
|
||||
channels: 3,
|
||||
data: img_4x4_rgb_dxt1,
|
||||
format: ext.COMPRESSED_RGB_S3TC_DXT1_EXT
|
||||
},
|
||||
{ width: 8,
|
||||
height: 8,
|
||||
channels: 3,
|
||||
data: img_8x8_rgb_dxt1,
|
||||
format: ext.COMPRESSED_RGB_S3TC_DXT1_EXT
|
||||
}
|
||||
];
|
||||
testDXTTextures(tests);
|
||||
}
|
||||
|
||||
function testDXT1_RGBA() {
|
||||
var tests = [
|
||||
{ width: 4,
|
||||
height: 4,
|
||||
channels: 4,
|
||||
data: img_4x4_rgba_dxt1,
|
||||
format: ext.COMPRESSED_RGBA_S3TC_DXT1_EXT
|
||||
},
|
||||
{ width: 8,
|
||||
height: 8,
|
||||
channels: 4,
|
||||
data: img_8x8_rgba_dxt1,
|
||||
format: ext.COMPRESSED_RGBA_S3TC_DXT1_EXT
|
||||
}
|
||||
];
|
||||
testDXTTextures(tests);
|
||||
}
|
||||
|
||||
function testDXT5_RGBA() {
|
||||
var tests = [
|
||||
{ width: 4,
|
||||
height: 4,
|
||||
channels: 4,
|
||||
data: img_4x4_rgba_dxt5,
|
||||
format: ext.COMPRESSED_RGBA_S3TC_DXT5_EXT
|
||||
},
|
||||
{ width: 8,
|
||||
height: 8,
|
||||
channels: 4,
|
||||
data: img_8x8_rgba_dxt5,
|
||||
format: ext.COMPRESSED_RGBA_S3TC_DXT5_EXT
|
||||
}
|
||||
];
|
||||
testDXTTextures(tests);
|
||||
}
|
||||
|
||||
function testETC1_RGB8() {
|
||||
testFailed("ETC1 test not yet implemented");
|
||||
}
|
||||
|
||||
function testPVRTC_RGB_4BPPV1() {
|
||||
var tests = [
|
||||
{ width: 8,
|
||||
height: 8,
|
||||
channels: 4,
|
||||
data: img_8x8_rgba_pvrtc4bpp,
|
||||
format: ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG
|
||||
}
|
||||
];
|
||||
testPVRTCTextures(tests);
|
||||
}
|
||||
|
||||
function testPVRTC_RGB_4BPPV1() {
|
||||
var tests = [
|
||||
{ width: 8,
|
||||
height: 8,
|
||||
channels: 4,
|
||||
data: img_8x8_rgba_pvrtc4bpp,
|
||||
format: ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
|
||||
}
|
||||
];
|
||||
testPVRTCTextures(tests);
|
||||
}
|
||||
|
||||
function testDXTTextures(tests) {
|
||||
debug("<hr/>");
|
||||
for (var ii = 0; ii < tests.length; ++ii) {
|
||||
testDXTTexture(tests[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
function uncompressDXTBlock(
|
||||
destBuffer, destX, destY, destWidth, src, srcOffset, format) {
|
||||
function make565(src, offset) {
|
||||
return src[offset + 0] + src[offset + 1] * 256;
|
||||
}
|
||||
function make8888From565(c) {
|
||||
return [
|
||||
Math.floor(((c >> 11) & 0x1F) * 255 / 31),
|
||||
Math.floor(((c >> 5) & 0x3F) * 255 / 63),
|
||||
Math.floor(((c >> 0) & 0x1F) * 255 / 31),
|
||||
255
|
||||
];
|
||||
}
|
||||
function mix(mult, c0, c1, div) {
|
||||
var r = [];
|
||||
for (var ii = 0; ii < c0.length; ++ii) {
|
||||
r[ii] = Math.floor((c0[ii] * mult + c1[ii]) / div);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
var colorOffset =
|
||||
srcOffset + ((format == ext.COMPRESSED_RGBA_S3TC_DXT5_EXT) ? 8 : 0);
|
||||
var color0 = make565(src, colorOffset + 0);
|
||||
var color1 = make565(src, colorOffset + 2);
|
||||
var c0gtc1 = color0 > color1 || format == ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
var rgba0 = make8888From565(color0);
|
||||
var rgba1 = make8888From565(color1);
|
||||
var colors = [
|
||||
rgba0,
|
||||
rgba1,
|
||||
c0gtc1 ? mix(2, rgba0, rgba1, 3) : mix(1, rgba0, rgba1, 2),
|
||||
c0gtc1 ? mix(2, rgba1, rgba0, 3) : [0, 0, 0, 255]
|
||||
];
|
||||
|
||||
// yea I know there is a lot of math in this inner loop.
|
||||
// so sue me.
|
||||
for (var yy = 0; yy < 4; ++yy) {
|
||||
var pixels = src[colorOffset + 4 + yy];
|
||||
for (var xx = 0; xx < 4; ++xx) {
|
||||
var dstOff = ((destY + yy) * destWidth + destX + xx) * 4;
|
||||
var code = (pixels >> (xx * 2)) & 0x3;
|
||||
var srcColor = colors[code];
|
||||
var alpha;
|
||||
switch (format) {
|
||||
case ext.COMPRESSED_RGB_S3TC_DXT1_EXT:
|
||||
alpha = 255;
|
||||
break;
|
||||
case ext.COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
alpha = (code == 3 && !c0gtc1) ? 0 : 255;
|
||||
break;
|
||||
case ext.COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
{
|
||||
var alpha0 = src[srcOffset + 0];
|
||||
var alpha1 = src[srcOffset + 1];
|
||||
var alphaOff = Math.floor(yy / 2) * 3 + 2;
|
||||
var alphaBits =
|
||||
src[srcOffset + alphaOff + 0] +
|
||||
src[srcOffset + alphaOff + 1] * 256 +
|
||||
src[srcOffset + alphaOff + 2] * 65536;
|
||||
var alphaShift = (yy % 2) * 12 + xx * 3;
|
||||
var alphaCode = (alphaBits >> alphaShift) & 0x7;
|
||||
if (alpha0 > alpha1) {
|
||||
switch (alphaCode) {
|
||||
case 0:
|
||||
alpha = alpha0;
|
||||
break;
|
||||
case 1:
|
||||
alpha = alpha1;
|
||||
break;
|
||||
default:
|
||||
alpha = ((8 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 7;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (alphaCode) {
|
||||
case 0:
|
||||
alpha = alpha0;
|
||||
break;
|
||||
case 1:
|
||||
alpha = alpha1;
|
||||
break;
|
||||
case 6:
|
||||
alpha = 0;
|
||||
break;
|
||||
case 7:
|
||||
alpha = 255;
|
||||
break;
|
||||
default:
|
||||
alpha = ((6 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw "bad format";
|
||||
}
|
||||
destBuffer[dstOff + 0] = srcColor[0];
|
||||
destBuffer[dstOff + 1] = srcColor[1];
|
||||
destBuffer[dstOff + 2] = srcColor[2];
|
||||
destBuffer[dstOff + 3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uncompressDXT(width, height, data, format) {
|
||||
if (width % 4 || height % 4) throw "bad width or height";
|
||||
|
||||
var dest = new Uint8Array(width * height * 4);
|
||||
var blocksAcross = width / 4;
|
||||
var blocksDown = height / 4;
|
||||
var blockSize = (format == ext.COMPRESSED_RGBA_S3TC_DXT5_EXT ? 16 : 8);
|
||||
for (var yy = 0; yy < blocksDown; ++yy) {
|
||||
for (var xx = 0; xx < blocksAcross; ++xx) {
|
||||
uncompressDXTBlock(
|
||||
dest, xx * 4, yy * 4, width, data,
|
||||
(yy * blocksAcross + xx) * blockSize, format);
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
function testDXTTexture(test) {
|
||||
var uncompressedData = uncompressDXT(
|
||||
test.width, test.height, test.data, test.format);
|
||||
|
||||
var inner = 4; //test.width == 4 ? 4 : 1
|
||||
for (var ii = 0; ii < inner; ++ii) {
|
||||
var width = test.width - ii;
|
||||
var height = test.height - ii;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
gl.viewport(0, 0, width, height);
|
||||
debug("testing " + formatToString(test.format) + " " +
|
||||
width + "x" + height);
|
||||
var tex = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||
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);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
ext.compressedTexImage2D(
|
||||
gl.TEXTURE_2D, 0, test.format, width, height, 0,
|
||||
new Uint8Array(test.data));
|
||||
glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
|
||||
wtu.drawQuad(gl);
|
||||
compareRect(
|
||||
width, height, test.channels, test.width, test.height, uncompressedData,
|
||||
test.data, test.format);
|
||||
}
|
||||
|
||||
gl.compressedTexImage2D(
|
||||
gl.TEXTURE_2D, 0, test.format, width + 1, height, 0,
|
||||
new Uint8Array(test.data));
|
||||
glErrorShouldBe(
|
||||
gl, gl.INVALID_OPERATION, "data size does not match dimensions");
|
||||
|
||||
|
||||
// TODO: test compressedTexSubImage2D
|
||||
// TODO: test invalid width, height, xoffset, yoffset
|
||||
}
|
||||
|
||||
function insertImg(element, caption, img) {
|
||||
var div = document.createElement("div");
|
||||
div.appendChild(img);
|
||||
var label = document.createElement("div");
|
||||
label.appendChild(document.createTextNode(caption));
|
||||
div.appendChild(label);
|
||||
element.appendChild(div);
|
||||
}
|
||||
|
||||
function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) {
|
||||
var scale = 8;
|
||||
var c = document.createElement("canvas");
|
||||
c.width = imageWidth * scale;
|
||||
c.height = imageHeight * scale;
|
||||
var ctx = c.getContext("2d");
|
||||
for (var yy = 0; yy < imageWidth; ++yy) {
|
||||
for (var xx = 0; xx < imageHeight; ++xx) {
|
||||
var offset = (yy * dataWidth + xx) * 4;
|
||||
ctx.fillStyle = "rgba(" +
|
||||
data[offset + 0] + "," +
|
||||
data[offset + 1] + "," +
|
||||
data[offset + 2] + "," +
|
||||
(alpha ? data[offset + 3] / 255 : 1) + ")";
|
||||
ctx.fillRect(xx * scale, yy * scale, scale, scale);
|
||||
}
|
||||
}
|
||||
var img = document.createElement("img");
|
||||
img.src = c.toDataURL();
|
||||
return img;
|
||||
}
|
||||
function compareRect(
|
||||
actualWidth, actualHeight, actualChannels,
|
||||
dataWidth, dataHeight, expectedData,
|
||||
testData, testFormat) {
|
||||
var actual = new Uint8Array(actualWidth * actualHeight * 4);
|
||||
gl.readPixels(
|
||||
0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual);
|
||||
|
||||
var div = document.createElement("div");
|
||||
div.className = "testimages";
|
||||
insertImg(div, "expected", makeImage(
|
||||
actualWidth, actualHeight, dataWidth, expectedData,
|
||||
actualChannels == 4));
|
||||
insertImg(div, "actual", makeImage(
|
||||
actualWidth, actualHeight, actualWidth, actual,
|
||||
actualChannels == 4));
|
||||
div.appendChild(document.createElement('br'));
|
||||
document.getElementById("console").appendChild(div);
|
||||
|
||||
var failed = false;
|
||||
for (var yy = 0; yy < actualHeight; ++yy) {
|
||||
for (var xx = 0; xx < actualWidth; ++xx) {
|
||||
var actualOffset = (yy * actualWidth + xx) * 4;
|
||||
var expectedOffset = (yy * dataWidth + xx) * 4;
|
||||
var expected = [
|
||||
expectedData[expectedOffset + 0],
|
||||
expectedData[expectedOffset + 1],
|
||||
expectedData[expectedOffset + 2],
|
||||
(actualChannels == 3 ? 255 : expectedData[expectedOffset + 3])
|
||||
];
|
||||
for (var jj = 0; jj < 4; ++jj) {
|
||||
if (actual[actualOffset + jj] != expected[jj]) {
|
||||
failed = true;
|
||||
var was = actual[actualOffset + 0].toString();
|
||||
for (j = 1; j < 4; ++j) {
|
||||
was += "," + actual[actualOffset + j];
|
||||
}
|
||||
testFailed('at (' + xx + ', ' + yy +
|
||||
') expected: ' + expected + ' was ' + was);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!failed) {
|
||||
testPassed("texture rendered correctly");
|
||||
}
|
||||
}
|
||||
|
||||
function testPVRTCTextures() {
|
||||
testFailed("PVRTC test not yet implemented");
|
||||
}
|
||||
|
||||
debug("");
|
||||
successfullyParsed = true;
|
||||
</script>
|
||||
<script src="../../resources/js-test-post.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -25,6 +25,7 @@ GLSLGenerator.runFeatureTest({
|
|||
args: "$(type) p1, $(type) p2",
|
||||
baseArgs: "value$(field)",
|
||||
testFunc: "$(func)($(type),$(type))",
|
||||
fragmentTolerance: 1,
|
||||
emuFuncs: [
|
||||
{ type: "float",
|
||||
code: [
|
||||
|
|
|
@ -25,6 +25,7 @@ GLSLGenerator.runFeatureTest({
|
|||
args: "$(type) value",
|
||||
baseArgs: "value$(field)",
|
||||
testFunc: "$(func)($(type))",
|
||||
fragmentTolerance: 1,
|
||||
emuFuncs: [
|
||||
{ type: "float",
|
||||
code: [
|
||||
|
|
|
@ -24,6 +24,7 @@ GLSLGenerator.runFeatureTest({
|
|||
args: "$(type) value, float divisor",
|
||||
baseArgs: "value$(field), divisor",
|
||||
testFunc: "$(func)($(arg0), float)",
|
||||
fragmentTolerance: 1,
|
||||
emuFunc: ["float $(func)_base(float value, float divisor) {",
|
||||
" return value - divisor * floor(value / divisor);",
|
||||
"}"].join("\n"),
|
||||
|
|
|
@ -32,6 +32,7 @@ var fragmentShader = wtu.loadStandardFragmentShader(gl);
|
|||
assertMsg(fragmentShader, "fragment shader loaded");
|
||||
|
||||
var program = gl.createProgram();
|
||||
shouldBeTrue("program != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.attachShader(program, vertexShader)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.attachShader(program, fragmentShader)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.linkProgram(program)");
|
||||
|
@ -56,13 +57,18 @@ debug("");
|
|||
debug("texture deletion");
|
||||
|
||||
var fbo = gl.createFramebuffer(), fbo2 = gl.createFramebuffer(), fbo3 = gl.createFramebuffer();
|
||||
shouldBeTrue("fbo != null");
|
||||
shouldBeTrue("fbo2 != null");
|
||||
shouldBeTrue("fbo3 != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
|
||||
var tex = gl.createTexture();
|
||||
shouldBeTrue("tex != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, tex)");
|
||||
shouldBe("gl.getParameter(gl.TEXTURE_BINDING_2D)", "tex");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)");
|
||||
shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", "tex");
|
||||
shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)", "gl.TEXTURE");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteTexture(tex)");
|
||||
// Deleting a texture bound to the currently-bound fbo is the same as
|
||||
// detaching the textue from fbo first, then delete the texture.
|
||||
|
@ -74,6 +80,7 @@ shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, tex)");
|
|||
shouldBeNull("gl.getParameter(gl.TEXTURE_BINDING_2D)");
|
||||
|
||||
var texCubeMap = gl.createTexture();
|
||||
shouldBeTrue("texCubeMap != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_CUBE_MAP, texCubeMap)");
|
||||
shouldBe("gl.getParameter(gl.TEXTURE_BINDING_CUBE_MAP)", "texCubeMap");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteTexture(texCubeMap)");
|
||||
|
@ -83,16 +90,33 @@ shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_CUBE_MAP, texC
|
|||
shouldBeNull("gl.getParameter(gl.TEXTURE_BINDING_CUBE_MAP)");
|
||||
|
||||
var t = gl.createTexture();
|
||||
shouldBeTrue("t != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, t)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteTexture(t)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, t)");
|
||||
shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)");
|
||||
|
||||
var t2 = gl.createTexture();
|
||||
shouldBeTrue("t2 != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.activeTexture(gl.TEXTURE0)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, t2)");
|
||||
shouldBe("gl.getParameter(gl.TEXTURE_BINDING_2D)", "t2");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.activeTexture(gl.TEXTURE1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, t2)");
|
||||
shouldBe("gl.getParameter(gl.TEXTURE_BINDING_2D)", "t2");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteTexture(t2)");
|
||||
shouldBeNull("gl.getParameter(gl.TEXTURE_BINDING_2D)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.activeTexture(gl.TEXTURE0)");
|
||||
shouldBeNull("gl.getParameter(gl.TEXTURE_BINDING_2D)");
|
||||
|
||||
debug("");
|
||||
debug("renderbuffer deletion");
|
||||
|
||||
var rbo = gl.createRenderbuffer(), rbo2 = gl.createRenderbuffer(), rbo3 = gl.createRenderbuffer();
|
||||
shouldBeTrue("rbo != null");
|
||||
shouldBeTrue("rbo2 != null");
|
||||
shouldBeTrue("rbo3 != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindRenderbuffer(gl.RENDERBUFFER, rbo)");
|
||||
shouldBe("gl.getParameter(gl.RENDERBUFFER_BINDING)", "rbo");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo)");
|
||||
|
@ -111,10 +135,179 @@ shouldBe("gl.getParameter(gl.RENDERBUFFER_BINDING)", "rbo2");
|
|||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteRenderbuffer(rbo3)");
|
||||
shouldBe("gl.getParameter(gl.RENDERBUFFER_BINDING)", "rbo2");
|
||||
|
||||
debug("");
|
||||
debug("using deleted renderbuffer");
|
||||
rbo = gl.createRenderbuffer();
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindRenderbuffer(gl.RENDERBUFFER, rbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo)");
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
|
||||
// make backbuffer red
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(1,0,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
// make fbo green
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,1,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
// Bind backbuffer.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
// delete renderbuffer. It should still be attached to fbo though.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteRenderbuffer(rbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [255,0,0,255], "backbuffer should be red")');
|
||||
// Use fbo that has deleted rbo.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [0,255,0,255], "fbo should be green")');
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,0,1,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [0,0,255,255], "fbo should be blue")');
|
||||
// Bind backbuffer.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [255,0,0,255], "backbuffer should be red")');
|
||||
}
|
||||
|
||||
debug("");
|
||||
debug("renderbuffer attached twice to same framebuffer");
|
||||
rbo = gl.createRenderbuffer();
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindRenderbuffer(gl.RENDERBUFFER, rbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo)");
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
|
||||
var rbo2 = gl.createRenderbuffer();
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindRenderbuffer(gl.RENDERBUFFER, rbo2)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16)");
|
||||
// attach rbo2 at two attachment points incompatible with it
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rbo2)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rbo2)");
|
||||
shouldBeTrue("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) == rbo2");
|
||||
shouldBeTrue("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) == rbo2");
|
||||
// fbo can't be complete as rbo2 is attached at incompatible attachment points
|
||||
shouldBeFalse("gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE");
|
||||
// now we delete rbo2, which detaches it from the two attachment points where it currently is attached
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteRenderbuffer(rbo2)");
|
||||
shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)", "gl.NONE");
|
||||
shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)", "gl.NONE");
|
||||
// we should now be in the same state as before with only rbo attached, so fbo should be complete again
|
||||
shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
|
||||
shouldBeTrue("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) == rbo");
|
||||
}
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteRenderbuffer(rbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
|
||||
|
||||
|
||||
debug("");
|
||||
debug("using deleted texture");
|
||||
tex = gl.createTexture();
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, tex)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)");
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
|
||||
// make fbo green
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,1,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
// Bind backbuffer.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
// delete texture. It should still be attached to fbo though.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteTexture(tex)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 1, 1, [255,0,0,255], "backbuffer should be red")');
|
||||
// Use fbo that has deleted texture.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 1, 1, [0,255,0,255], "fbo should be green")');
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,0,1,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 1, 1, [0,0,255,255], "fbo should be blue")');
|
||||
// Bind backbuffer.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [255,0,0,255], "backbuffer should be red")');
|
||||
}
|
||||
|
||||
debug("");
|
||||
debug("using deleted renderbuffer");
|
||||
rbo = gl.createRenderbuffer();
|
||||
shouldBeTrue("rbo != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindRenderbuffer(gl.RENDERBUFFER, rbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo)");
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo)");
|
||||
shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
|
||||
// make backbuffer red
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(1,0,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
// make fbo green
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,1,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
// delete renderbuffer. It should still be attached to fbo2 though.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteRenderbuffer(rbo)");
|
||||
// fbo has no attachments
|
||||
shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
|
||||
// Use fbo2 that has deleted rbo.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [0,255,0,255], "fbo should be green")');
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,0,1,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [0,0,255,255], "fbo should be blue")');
|
||||
shouldBeTrue("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) == rbo");
|
||||
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)");
|
||||
shouldGenerateGLError(gl, gl.NONE, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)");
|
||||
shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
|
||||
// Bind backbuffer.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [255,0,0,255], "backbuffer should be red")');
|
||||
}
|
||||
|
||||
debug("");
|
||||
debug("using deleted texture");
|
||||
tex = gl.createTexture();
|
||||
shouldBeTrue("tex != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, tex)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)");
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)");
|
||||
// make fbo green
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,1,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
// delete texture. It should still be attached to fbo2 though.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteTexture(tex)");
|
||||
// fbo has no attachments
|
||||
shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
|
||||
// Use fbo that has deleted texture.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2)");
|
||||
shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 1, 1, [0,255,0,255], "fbo should be green")');
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,0,1,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 1, 1, [0,0,255,255], "fbo should be blue")');
|
||||
shouldBeTrue("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) == tex");
|
||||
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)");
|
||||
shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
|
||||
// Bind backbuffer.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [255,0,0,255], "backbuffer should be red")');
|
||||
}
|
||||
|
||||
debug("");
|
||||
debug("buffer deletion");
|
||||
|
||||
var buffer = gl.createBuffer();
|
||||
shouldBeTrue("buffer != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, buffer)");
|
||||
shouldBe("gl.getParameter(gl.ARRAY_BUFFER_BINDING)", "buffer");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteBuffer(buffer)");
|
||||
|
@ -123,7 +316,19 @@ shouldBeNull("gl.getParameter(gl.ARRAY_BUFFER_BINDING)");
|
|||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, buffer)");
|
||||
shouldBeNull("gl.getParameter(gl.ARRAY_BUFFER_BINDING)");
|
||||
|
||||
var buffer2 = gl.createBuffer();
|
||||
shouldBeTrue("buffer2 != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, buffer2)");
|
||||
shouldBe("gl.getParameter(gl.ARRAY_BUFFER_BINDING)", "buffer2");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, null)");
|
||||
shouldBeNull("gl.getParameter(gl.ARRAY_BUFFER_BINDING)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteBuffer(buffer2)");
|
||||
shouldBeFalse("gl.isBuffer(buffer2)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, buffer2)");
|
||||
shouldBeNull("gl.getParameter(gl.ARRAY_BUFFER_BINDING)");
|
||||
|
||||
var bufferElement = gl.createBuffer();
|
||||
shouldBeTrue("bufferElement != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufferElement)");
|
||||
shouldBe("gl.getParameter(gl.ELEMENT_ARRAY_BUFFER_BINDING)", "bufferElement");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteBuffer(bufferElement)");
|
||||
|
@ -133,12 +338,35 @@ shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, b
|
|||
shouldBeNull("gl.getParameter(gl.ELEMENT_ARRAY_BUFFER_BINDING)");
|
||||
|
||||
var b = gl.createBuffer();
|
||||
shouldBeTrue("b != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, b)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteBuffer(b)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, b)");
|
||||
shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW)");
|
||||
|
||||
var b1 = gl.createBuffer();
|
||||
shouldBeTrue("b1 != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, b1);");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.enableVertexAttribArray(1);");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);");
|
||||
var b2 = gl.createBuffer();
|
||||
shouldBeTrue("b2 != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindBuffer(gl.ARRAY_BUFFER, b2);");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.enableVertexAttribArray(2);");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.vertexAttribPointer(2, 4, gl.FLOAT, false, 0, 0);");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.enableVertexAttribArray(3);");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.vertexAttribPointer(3, 4, gl.FLOAT, false, 0, 0);");
|
||||
shouldBe("gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)", "b1");
|
||||
shouldBe("gl.getVertexAttrib(2, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)", "b2");
|
||||
shouldBe("gl.getVertexAttrib(3, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)", "b2");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteBuffer(b2);");
|
||||
shouldBe("gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)", "b1");
|
||||
shouldBeNull("gl.getVertexAttrib(2, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)");
|
||||
shouldBeNull("gl.getVertexAttrib(3, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteBuffer(b1);");
|
||||
shouldBeNull("gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)");
|
||||
|
||||
debug("");
|
||||
debug("framebuffer deletion");
|
||||
|
||||
|
@ -154,6 +382,41 @@ shouldBe("gl.getParameter(gl.FRAMEBUFFER_BINDING)", "fbo2");
|
|||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteFramebuffer(fbo3)");
|
||||
shouldBe("gl.getParameter(gl.FRAMEBUFFER_BINDING)", "fbo2");
|
||||
|
||||
fbo = gl.createFramebuffer();
|
||||
rbo = gl.createRenderbuffer();
|
||||
shouldBeTrue("fbo != null");
|
||||
shouldBeTrue("rbo != null");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindRenderbuffer(gl.RENDERBUFFER, rbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo)");
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
|
||||
// set backbuffer to red
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(1,0,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
// set framebuffer to green
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,1,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
// check framebuffer
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 16, 16, [0,255,0,255], "fbo should be green")');
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 16, 16, 1, 1, [0,0,0,0], "outside fbo should be black")');
|
||||
// delete framebuffer. because this was the bound fbo the backbuffer should be active now
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteFramebuffer(fbo)");
|
||||
// check backbuffer
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 300, 150, [255,0,0,255], "backbuffer should be red")');
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 300, 0, 300, 300, [0,0,0,0], "outside backbuffer should be black")');
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 150, 300, 300, [0,0,0,0], "outside backbuffer should be black")');
|
||||
// check drawing to backbuffer
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clearColor(0,1,0,1)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.clear(gl.COLOR_BUFFER_BIT)");
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 300, 150, [0,255,0,255], "fbo should be green")');
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)");
|
||||
// check again because many buggy implementations will have bound to the true backbuffer on deleteFramebuffer.
|
||||
shouldGenerateGLError(gl, gl.NO_ERROR, 'wtu.checkCanvasRect(gl, 0, 0, 300, 150, [0,255,0,255], "fbo should be green")');
|
||||
}
|
||||
|
||||
successfullyParsed = true;
|
||||
</script>
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ uniform vec4 foo;
|
|||
varying vec4 texCoord0;
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = vec4(foo.r/256.0, foo.g/256.0, foo.b/256.0, foo.a*texCoord0.z/256.0);
|
||||
gl_FragColor = vec4(foo.r/255.0, foo.g/255.0, foo.b/255.0, foo.a*texCoord0.z/255.0);
|
||||
}
|
||||
</script>
|
||||
<style>canvas{ position:absolute; }</style>
|
||||
|
|
|
@ -82,10 +82,10 @@ varying vec4 texCoord0;
|
|||
void main()
|
||||
{
|
||||
gl_FragColor = vec4(
|
||||
uniV4[0].r/256.0,
|
||||
uniV4[0].g/256.0,
|
||||
uniV4[0].b/256.0,
|
||||
uniV4[0].a*texCoord0.z/256.0);
|
||||
uniV4[0].r/255.0,
|
||||
uniV4[0].g/255.0,
|
||||
uniV4[0].b/255.0,
|
||||
uniV4[0].a*texCoord0.z/255.0);
|
||||
}
|
||||
</script>
|
||||
<style>canvas{ position:absolute; }</style>
|
||||
|
|
|
@ -87,10 +87,10 @@ varying vec4 texCoord0;
|
|||
void main()
|
||||
{
|
||||
gl_FragColor = vec4(
|
||||
uniV4.r/256.0,
|
||||
uniV4.g/256.0,
|
||||
uniV4.b/256.0,
|
||||
uniV4.a*texCoord0.z/256.0);
|
||||
uniV4.r/255.0,
|
||||
uniV4.g/255.0,
|
||||
uniV4.b/255.0,
|
||||
uniV4.a*texCoord0.z/255.0);
|
||||
}
|
||||
</script>
|
||||
<style>canvas{ position:absolute; }</style>
|
||||
|
|
|
@ -27,29 +27,43 @@ var colorBuffer;
|
|||
var width;
|
||||
var height;
|
||||
|
||||
function testAttachment(attachment, buffer, isConflicted)
|
||||
const ALLOW_COMPLETE = 0x01;
|
||||
const ALLOW_UNSUPPORTED = 0x02;
|
||||
const ALLOW_INCOMPLETE_ATTACHMENT = 0x04;
|
||||
|
||||
function CheckFramebufferForAllowedStatuses(allowedStatuses)
|
||||
{
|
||||
// If the framebuffer is in an error state for multiple reasons,
|
||||
// we can't guarantee which one will be reported.
|
||||
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
||||
var statusAllowed = ((allowedStatuses & ALLOW_COMPLETE) && (status == gl.FRAMEBUFFER_COMPLETE)) ||
|
||||
((allowedStatuses & ALLOW_UNSUPPORTED) && (status == gl.FRAMEBUFFER_UNSUPPORTED)) ||
|
||||
((allowedStatuses & ALLOW_INCOMPLETE_ATTACHMENT) && (status == gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT));
|
||||
var msg = "gl.checkFramebufferStatus(gl.FRAMEBUFFER) returned " + status;
|
||||
if (statusAllowed)
|
||||
testPassed(msg);
|
||||
else
|
||||
testFailed(msg);
|
||||
}
|
||||
|
||||
function testAttachment(attachment, buffer, allowedStatuses)
|
||||
{
|
||||
shouldBeNonNull("fbo = gl.createFramebuffer()");
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, buffer);
|
||||
glErrorShouldBe(gl, gl.NO_ERROR);
|
||||
// If the framebuffer is in an error state for multiple reasons,
|
||||
// we can't guarantee which one will be reported.
|
||||
if ((width == 0 || height == 0) && !isConflicted) {
|
||||
// Zero-sized renderbuffers are supposed to result in an incomplete attachment.
|
||||
// However, certain combination may violate implementation specific restrictions.
|
||||
shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT || gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_UNSUPPORTED");
|
||||
} else if (isConflicted) {
|
||||
shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_UNSUPPORTED");
|
||||
CheckFramebufferForAllowedStatuses(allowedStatuses);
|
||||
if ((allowedStatuses & ALLOW_COMPLETE) == 0) {
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION);
|
||||
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(width * height * 4));
|
||||
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION);
|
||||
}
|
||||
gl.deleteFramebuffer(fbo);
|
||||
}
|
||||
|
||||
function testAttachments(attachment0, buffer0, attachment1, buffer1, isConflicted)
|
||||
function testAttachments(attachment0, buffer0, attachment1, buffer1, allowedStatuses)
|
||||
{
|
||||
shouldBeNonNull("fbo = gl.createFramebuffer()");
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
||||
|
@ -58,27 +72,20 @@ function testAttachments(attachment0, buffer0, attachment1, buffer1, isConflicte
|
|||
glErrorShouldBe(gl, gl.NO_ERROR);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment1, gl.RENDERBUFFER, buffer1);
|
||||
glErrorShouldBe(gl, gl.NO_ERROR);
|
||||
// If the framebuffer is in an error state for multiple reasons,
|
||||
// we can't guarantee which one will be reported.
|
||||
if ((width == 0 || height == 0) && !isConflicted) {
|
||||
// Zero-sized renderbuffers are supposed to result in an incomplete attachment.
|
||||
// However, certain combination may violate implementation specific restrictions.
|
||||
shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT || gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_UNSUPPORTED");
|
||||
} else if (isConflicted) {
|
||||
shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_UNSUPPORTED");
|
||||
}
|
||||
CheckFramebufferForAllowedStatuses(allowedStatuses);
|
||||
gl.deleteFramebuffer(fbo);
|
||||
}
|
||||
|
||||
function testColorRenderbuffer(internalformat)
|
||||
function testColorRenderbuffer(internalformat, allowedStatuses)
|
||||
{
|
||||
shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
|
||||
gl.renderbufferStorage(gl.RENDERBUFFER, internalformat, width, height);
|
||||
glErrorShouldBe(gl, gl.NO_ERROR);
|
||||
testAttachment(gl.COLOR_ATTACHMENT0, colorBuffer, false);
|
||||
testAttachment(gl.COLOR_ATTACHMENT0, colorBuffer, allowedStatuses);
|
||||
}
|
||||
|
||||
function testDepthStencilRenderbuffer()
|
||||
function testDepthStencilRenderbuffer(allowedStatuses)
|
||||
{
|
||||
shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
|
||||
|
@ -103,7 +110,7 @@ function testDepthStencilRenderbuffer()
|
|||
shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_STENCIL_SIZE) > 0");
|
||||
}
|
||||
glErrorShouldBe(gl, gl.NO_ERROR);
|
||||
testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, false);
|
||||
testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatuses);
|
||||
}
|
||||
|
||||
description("Test framebuffer object attachment behaviors");
|
||||
|
@ -134,49 +141,59 @@ for (width = 0; width <= 2; width += 2)
|
|||
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
|
||||
glErrorShouldBe(gl, gl.NO_ERROR);
|
||||
|
||||
var allowedStatusForGoodCase
|
||||
= (width == 0 || height == 0) ? ALLOW_INCOMPLETE_ATTACHMENT : ALLOW_COMPLETE;
|
||||
|
||||
// some cases involving stencil seem to be implementation-dependent
|
||||
var allowedStatusForImplDependentCase = allowedStatusForGoodCase | ALLOW_UNSUPPORTED;
|
||||
|
||||
debug("Attach depth using DEPTH_ATTACHMENT");
|
||||
testAttachment(gl.DEPTH_ATTACHMENT, depthBuffer, false);
|
||||
testAttachment(gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForGoodCase);
|
||||
debug("Attach depth using STENCIL_ATTACHMENT");
|
||||
testAttachment(gl.STENCIL_ATTACHMENT, depthBuffer, true);
|
||||
testAttachment(gl.STENCIL_ATTACHMENT, depthBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
|
||||
debug("Attach depth using DEPTH_STENCIL_ATTACHMENT");
|
||||
testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthBuffer, true);
|
||||
testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
|
||||
debug("Attach stencil using STENCIL_ATTACHMENT");
|
||||
testAttachment(gl.STENCIL_ATTACHMENT, stencilBuffer, false);
|
||||
testAttachment(gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForImplDependentCase);
|
||||
debug("Attach stencil using DEPTH_ATTACHMENT");
|
||||
testAttachment(gl.DEPTH_ATTACHMENT, stencilBuffer, true);
|
||||
testAttachment(gl.DEPTH_ATTACHMENT, stencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
|
||||
debug("Attach stencil using DEPTH_STENCIL_ATTACHMENT");
|
||||
testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, stencilBuffer, true);
|
||||
testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, stencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
|
||||
debug("Attach depthStencil using DEPTH_STENCIL_ATTACHMENT");
|
||||
testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, false);
|
||||
testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForGoodCase);
|
||||
debug("Attach depthStencil using DEPTH_ATTACHMENT");
|
||||
testAttachment(gl.DEPTH_ATTACHMENT, depthStencilBuffer, true);
|
||||
testAttachment(gl.DEPTH_ATTACHMENT, depthStencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
|
||||
debug("Attach depthStencil using STENCIL_ATTACHMENT");
|
||||
testAttachment(gl.STENCIL_ATTACHMENT, depthStencilBuffer, true);
|
||||
testAttachment(gl.STENCIL_ATTACHMENT, depthStencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
|
||||
|
||||
var allowedStatusForConflictedAttachment
|
||||
= (width == 0 || height == 0) ? ALLOW_UNSUPPORTED | ALLOW_INCOMPLETE_ATTACHMENT
|
||||
: ALLOW_UNSUPPORTED;
|
||||
|
||||
debug("Attach depth, then stencil, causing conflict");
|
||||
testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.STENCIL_ATTACHMENT, stencilBuffer, true);
|
||||
testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForConflictedAttachment);
|
||||
debug("Attach stencil, then depth, causing conflict");
|
||||
testAttachments(gl.STENCIL_ATTACHMENT, stencilBuffer, gl.DEPTH_ATTACHMENT, depthBuffer, true);
|
||||
testAttachments(gl.STENCIL_ATTACHMENT, stencilBuffer, gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForConflictedAttachment);
|
||||
debug("Attach depth, then depthStencil, causing conflict");
|
||||
testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, true);
|
||||
testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForConflictedAttachment);
|
||||
debug("Attach depthStencil, then depth, causing conflict");
|
||||
testAttachments(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, gl.DEPTH_ATTACHMENT, depthBuffer, true);
|
||||
testAttachments(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForConflictedAttachment);
|
||||
debug("Attach stencil, then depthStencil, causing conflict");
|
||||
testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, true);
|
||||
testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForConflictedAttachment);
|
||||
debug("Attach depthStencil, then stencil, causing conflict");
|
||||
testAttachments(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, gl.STENCIL_ATTACHMENT, stencilBuffer, true);
|
||||
testAttachments(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForConflictedAttachment);
|
||||
|
||||
debug("Attach color renderbuffer with internalformat == RGBA4");
|
||||
testColorRenderbuffer(gl.RGBA4);
|
||||
testColorRenderbuffer(gl.RGBA4, allowedStatusForGoodCase);
|
||||
|
||||
debug("Attach color renderbuffer with internalformat == RGB5_A1");
|
||||
testColorRenderbuffer(gl.RGB5_A1);
|
||||
testColorRenderbuffer(gl.RGB5_A1, allowedStatusForGoodCase);
|
||||
|
||||
debug("Attach color renderbuffer with internalformat == RGB565");
|
||||
testColorRenderbuffer(gl.RGB565);
|
||||
testColorRenderbuffer(gl.RGB565, allowedStatusForGoodCase);
|
||||
|
||||
debug("Create and attach depthStencil renderbuffer");
|
||||
testDepthStencilRenderbuffer();
|
||||
testDepthStencilRenderbuffer(allowedStatusForGoodCase);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,23 +283,35 @@ function testUsingIncompleteFramebuffer() {
|
|||
debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
|
||||
testRenderingAndReading();
|
||||
|
||||
shouldBeNonNull("fbo = gl.createFramebuffer()");
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
||||
shouldBeNonNull("fbo2 = gl.createFramebuffer()");
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2);
|
||||
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
|
||||
debug("");
|
||||
debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
|
||||
testRenderingAndReading();
|
||||
|
||||
shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
|
||||
gl.framebufferRenderbuffer(
|
||||
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
|
||||
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0);
|
||||
debug("");
|
||||
debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
|
||||
testRenderingAndReading();
|
||||
|
||||
function testRenderingAndReading() {
|
||||
glErrorShouldBe(gl, gl.NO_ERROR);
|
||||
wtu.drawQuad(gl);
|
||||
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "drawArrays with incomplete framebuffer");
|
||||
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
|
||||
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "readPixels from incomplete framebuffer");
|
||||
// copyTexImage and copyTexSubImage can be either INVALID_FRAMEBUFFER_OPERATION because
|
||||
// the framebuffer is invalid OR INVALID_OPERATION because in the case of no attachments
|
||||
// the framebuffer is not of a compatible type.
|
||||
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
|
||||
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "copyTexImage2D from incomplete framebuffer");
|
||||
glErrorShouldBe(gl, [gl.INVALID_FRAMEBUFFER_OPERATION, gl.INVALID_OPERATION], "copyTexImage2D from incomplete framebuffer");
|
||||
gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 1, 1, 0);
|
||||
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "copyTexSubImage2D from incomplete framebuffer");
|
||||
glErrorShouldBe(gl, [gl.INVALID_FRAMEBUFFER_OPERATION, gl.INVALID_OPERATION], "copyTexSubImage2D from incomplete framebuffer");
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "clear with incomplete framebuffer");
|
||||
}
|
||||
|
|
|
@ -6,4 +6,4 @@ gl-scissor-test.html
|
|||
more-than-65536-indices.html
|
||||
point-size.html
|
||||
triangle.html
|
||||
|
||||
line-loop-tri-fan.html
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
<!--
|
||||
Copyright (C) 2011 Opera Software ASA. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY OPERA SOFTWARE ASA. ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
<!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 vec2 pos;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(pos, 0, 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="fshader" type="x-shader/x-fragment">
|
||||
precision mediump float;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = vec4(0, 1, 0, 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Check a single 32-bit RGBA pixel.
|
||||
function checkPixel(buf, index, correct) {
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
if (buf[index + i] != correct[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the line loop by reading the pixels and making sure just the edge
|
||||
// pixels are green and the rest are black.
|
||||
function checkLineLoop(gl, w) {
|
||||
var buf = new Uint8Array(w * w * 4);
|
||||
gl.readPixels(0, 0, w, w, gl.RGBA, gl.UNSIGNED_BYTE, buf);
|
||||
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;
|
||||
}
|
||||
if (!checkPixel(buf, j, correct)) {
|
||||
isCorrect = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isCorrect) {
|
||||
testPassed("Line loop was drawn correctly.");
|
||||
} else {
|
||||
testFailed("Line loop was drawn incorrectly.");
|
||||
}
|
||||
}
|
||||
|
||||
// Check the tri fan by reading the pixels and making sure they are all green.
|
||||
function checkTriFan(gl, w) {
|
||||
buf = new Uint8Array(w * w * 4);
|
||||
gl.readPixels(0, 0, w, w, gl.RGBA, gl.UNSIGNED_BYTE, buf);
|
||||
var filled = true;
|
||||
for (var j = 0; j < w * w * 4; j += 4) {
|
||||
if (!checkPixel(buf, j, [0,255,0,255])) {
|
||||
filled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (filled) {
|
||||
testPassed("Triangle fan was drawn correctly.");
|
||||
} else {
|
||||
testFailed("Triangle fan was drawn incorrectly.");
|
||||
}
|
||||
}
|
||||
|
||||
function runTest()
|
||||
{
|
||||
var gl = initWebGL('testbed', 'vshader', 'fshader', ['pos'], [0, 0, 0, 1], 1, { antialias: false });
|
||||
if (!gl) {
|
||||
testFailed('initWebGL(..) failed');
|
||||
return;
|
||||
}
|
||||
var w = document.getElementById('testbed').width;
|
||||
|
||||
gl.enableVertexAttribArray(0);
|
||||
|
||||
//---------- LINE_LOOP----------
|
||||
var d = 1/w;
|
||||
var vertices = new Float32Array([-1+d, -1+d, 1-d, -1+d, 1-d, 1-d, -1+d, 1-d]);
|
||||
var vertBuf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
||||
var indBuf = gl.createBuffer();
|
||||
var indices = new Uint16Array([0, 1, 2, 3]);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indBuf);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||
|
||||
debug('Draw a square using a line loop and verify that it draws all four sides and nothing else.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawArrays(gl.LINE_LOOP, 0, vertices.length / 2);
|
||||
checkLineLoop(gl, w);
|
||||
|
||||
debug('Draw a square using an indexed line loop and verify that it draws all four sides and nothing else.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawElements(gl.LINE_LOOP, indices.length, gl.UNSIGNED_SHORT, 0);
|
||||
checkLineLoop(gl, w);
|
||||
|
||||
vertices = new Float32Array([0, 0, 0, 0, 0, 0, -1+d, -1+d, 1-d, -1+d, 1-d, 1-d, -1+d, 1-d]);
|
||||
vertBuf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
||||
indBuf = gl.createBuffer();
|
||||
indices = new Uint16Array([0, 1, 2, 3, 4, 5, 6]);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indBuf);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||
|
||||
debug('Draw a square using a line loop with a vertex buffer offset and verify that it draws all four sides and nothing else.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawArrays(gl.LINE_LOOP, 3, vertices.length / 2 - 3);
|
||||
checkLineLoop(gl, w);
|
||||
|
||||
debug('Draw a square using an indexed line loop with an index buffer offset and verify that it draws all four sides and nothing else.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawElements(gl.LINE_LOOP, indices.length - 3, gl.UNSIGNED_SHORT, 3 * 2);
|
||||
checkLineLoop(gl, w);
|
||||
|
||||
//---------- LINE_LOOP UBYTE ----------
|
||||
var degenVerts = new Array(252 * 2);
|
||||
for (var j = 0; j < 252 * 2; ++j) {
|
||||
degenVerts[j] = -1+d;
|
||||
}
|
||||
degenVerts = degenVerts.concat([-1+d, -1+d, 1-d, -1+d, 1-d, 1-d, -1+d, 1-d]);
|
||||
vertices = new Float32Array(degenVerts);
|
||||
vertBuf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
||||
indBuf = gl.createBuffer();
|
||||
var degenInd = new Array(252);
|
||||
for (var j = 0; j < 252; ++j) {
|
||||
degenInd[j] = j;
|
||||
}
|
||||
degenInd = degenInd.concat([252, 253, 254, 255]);
|
||||
indices = new Uint8Array(degenInd);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indBuf);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||
|
||||
debug('Draw a square using an ubyte indexed line loop with 256 indices and verify that it draws all four sides and nothing else.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawElements(gl.LINE_LOOP, indices.length, gl.UNSIGNED_BYTE, 0);
|
||||
checkLineLoop(gl, w);
|
||||
|
||||
|
||||
//---------- TRIANGLE_FAN ----------
|
||||
vertices = new Float32Array([0, 0, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1]);
|
||||
vertBuf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
||||
indices = new Uint16Array([0,1,2,3,4,5]);
|
||||
indBuf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indBuf);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||
|
||||
debug('Draw a filled square using a triangle fan and verify that it fills the entire canvas.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawArrays(gl.TRIANGLE_FAN, 0, vertices.length / 2);
|
||||
checkTriFan(gl, w);
|
||||
|
||||
debug('Draw a filled square using an indexed triangle fan and verify that it fills the entire canvas.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawElements(gl.TRIANGLE_FAN, indices.length, gl.UNSIGNED_SHORT, 0);
|
||||
checkTriFan(gl, w);
|
||||
|
||||
vertices = new Float32Array([1, 1, 1, 1, 1, 1, 0, 0, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1]);
|
||||
vertBuf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
||||
indices = new Uint16Array([0,1,2,3,4,5,6,7,8]);
|
||||
indBuf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indBuf);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
||||
|
||||
debug('Draw a filled square using a triangle fan with a vertex buffer offset and verify that it fills the entire canvas.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawArrays(gl.TRIANGLE_FAN, 3, vertices.length / 2 - 3);
|
||||
checkTriFan(gl, w);
|
||||
|
||||
debug('Draw a filled square using an indexed triangle fan with an index buffer offset and verify that it fills the entire canvas.');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawElements(gl.TRIANGLE_FAN, indices.length - 3, gl.UNSIGNED_SHORT, 3 * 2);
|
||||
checkTriFan(gl, w);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="testbed" width="10px" height="10px" style="width:50px; height:50px"></canvas>
|
||||
<div id="description"></div>
|
||||
<div id="console"></div>
|
||||
<script>
|
||||
description('Verify that LINE_LOOP and TRIANGLE_FAN works correctly.');
|
||||
runTest();
|
||||
successfullyParsed = true;
|
||||
</script>
|
||||
<script src="../../resources/js-test-post.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -223,7 +223,10 @@ var runFeatureTest = function(params) {
|
|||
|
||||
var wtu = WebGLTestUtils;
|
||||
var gridRes = params.gridRes;
|
||||
var tolerance = params.tolerance || 0;
|
||||
var vertexTolerance = params.tolerance || 0;
|
||||
var fragmentTolerance = vertexTolerance;
|
||||
if ('fragmentTolerance' in params)
|
||||
fragmentTolerance = params.fragmentTolerance || 0;
|
||||
|
||||
description("Testing GLSL feature: " + params.feature);
|
||||
|
||||
|
@ -252,13 +255,15 @@ var runFeatureTest = function(params) {
|
|||
input: "color",
|
||||
output: "vColor",
|
||||
vertexShaderTemplate: vertexShaderTemplate,
|
||||
fragmentShaderTemplate: baseFragmentShader
|
||||
fragmentShaderTemplate: baseFragmentShader,
|
||||
tolerance: vertexTolerance
|
||||
},
|
||||
{ type: "fragment",
|
||||
input: "vColor",
|
||||
output: "gl_FragColor",
|
||||
vertexShaderTemplate: baseVertexShader,
|
||||
fragmentShaderTemplate: fragmentShaderTemplate
|
||||
fragmentShaderTemplate: fragmentShaderTemplate,
|
||||
tolerance: fragmentTolerance
|
||||
}
|
||||
];
|
||||
for (var ss = 0; ss < shaderInfos.length; ++ss) {
|
||||
|
@ -328,7 +333,7 @@ var runFeatureTest = function(params) {
|
|||
}
|
||||
var testImg = makeImage(canvas);
|
||||
|
||||
reportResults(refData, refImg, testData, testImg);
|
||||
reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,7 +369,7 @@ var runFeatureTest = function(params) {
|
|||
console.appendChild(div);
|
||||
}
|
||||
|
||||
function reportResults(refData, refImage, testData, testImage) {
|
||||
function reportResults(refData, refImage, testData, testImage, tolerance) {
|
||||
var same = true;
|
||||
for (var yy = 0; yy < height; ++yy) {
|
||||
for (var xx = 0; xx < width; ++xx) {
|
||||
|
@ -487,6 +492,18 @@ return {
|
|||
* tests:
|
||||
* The code for each test. It is assumed the tests are for
|
||||
* float, vec2, vec3, vec4 in that order.
|
||||
*
|
||||
* tolerance: (optional)
|
||||
* Allow some tolerance in the comparisons. The tolerance is applied to
|
||||
* both vertex and fragment shaders. The default tolerance is 0, meaning
|
||||
* the values have to be identical.
|
||||
*
|
||||
* fragmentTolerance: (optional)
|
||||
* Specify a tolerance which only applies to fragment shaders. The
|
||||
* fragment-only tolerance will override the shared tolerance for
|
||||
* fragment shaders if both are specified. Fragment shaders usually
|
||||
* use mediump float precision so they sometimes require higher tolerance
|
||||
* than vertex shaders which use highp.
|
||||
*/
|
||||
runFeatureTest: runFeatureTest,
|
||||
|
||||
|
|
|
@ -130,18 +130,33 @@ function shouldGenerateGLError(ctx, glErrors, evalStr) {
|
|||
/**
|
||||
* Tests that the first error GL returns is the specified error.
|
||||
* @param {!WebGLContext} gl The WebGLContext to use.
|
||||
* @param {number} glError The expected gl error.
|
||||
* @param {number|!Array.<number>} glError The expected gl
|
||||
* error. Multiple errors can be passed in using an
|
||||
* array.
|
||||
* @param {string} opt_msg Optional additional message.
|
||||
*/
|
||||
function glErrorShouldBe(gl, glError, opt_msg) {
|
||||
function glErrorShouldBe(gl, glErrors, opt_msg) {
|
||||
if (!glErrors.length) {
|
||||
glErrors = [glErrors];
|
||||
}
|
||||
opt_msg = opt_msg || "";
|
||||
var err = gl.getError();
|
||||
if (err != glError) {
|
||||
testFailed("getError expected: " + getGLErrorAsString(gl, glError) +
|
||||
". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
|
||||
var ndx = glErrors.indexOf(err);
|
||||
if (ndx < 0) {
|
||||
if (glErrors.length == 1) {
|
||||
testFailed("getError expected: " + getGLErrorAsString(gl, glErrors[0]) +
|
||||
". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
|
||||
} else {
|
||||
var errs = [];
|
||||
for (var ii = 0; ii < glErrors.length; ++ii) {
|
||||
errs.push(getGLErrorAsString(gl, glErrors[ii]));
|
||||
}
|
||||
testFailed("getError expected one of: [" + errs.join(", ") +
|
||||
"]. Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
|
||||
}
|
||||
} else {
|
||||
testPassed("getError was expected value: " +
|
||||
getGLErrorAsString(gl, glError) + " : " + opt_msg);
|
||||
getGLErrorAsString(gl, err) + " : " + opt_msg);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -361,18 +376,18 @@ var getBasePathForResources = function() {
|
|||
|
||||
function loadStandardVertexShader(context) {
|
||||
return loadShader(
|
||||
context,
|
||||
getBasePathForResources() + "vertexShader.vert",
|
||||
context.VERTEX_SHADER,
|
||||
true);
|
||||
context,
|
||||
getBasePathForResources() + "vertexShader.vert",
|
||||
context.VERTEX_SHADER,
|
||||
true);
|
||||
}
|
||||
|
||||
function loadStandardFragmentShader(context) {
|
||||
return loadShader(
|
||||
context,
|
||||
getBasePathForResources() + "fragmentShader.frag",
|
||||
context.FRAGMENT_SHADER,
|
||||
true);
|
||||
context,
|
||||
getBasePathForResources() + "fragmentShader.frag",
|
||||
context.FRAGMENT_SHADER,
|
||||
true);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -61,8 +61,8 @@ else {
|
|||
debug("");
|
||||
debug("Check default values");
|
||||
shouldBe('context.getParameter(context.ACTIVE_TEXTURE)', 'context.TEXTURE0');
|
||||
shouldBe('(context.getParameter(context.ALIASED_LINE_WIDTH_RANGE)[0] == 1) || (context.getParameter(context.ALIASED_LINE_WIDTH_RANGE)[1] == 1)', 'true');
|
||||
shouldBe('(context.getParameter(context.ALIASED_POINT_SIZE_RANGE)[0] == 1) || (context.getParameter(context.ALIASED_POINT_SIZE_RANGE)[1] == 1)', 'true');
|
||||
shouldBe('(context.getParameter(context.ALIASED_LINE_WIDTH_RANGE)[0] <= 1) && (context.getParameter(context.ALIASED_LINE_WIDTH_RANGE)[0] > 0) && (context.getParameter(context.ALIASED_LINE_WIDTH_RANGE)[1] >= 1)', 'true');
|
||||
shouldBe('(context.getParameter(context.ALIASED_POINT_SIZE_RANGE)[0] <= 1) && (context.getParameter(context.ALIASED_POINT_SIZE_RANGE)[0] > 0) && (context.getParameter(context.ALIASED_POINT_SIZE_RANGE)[1] >= 1)', 'true');
|
||||
shouldBe('context.getParameter(context.ARRAY_BUFFER_BINDING)', 'null');
|
||||
shouldBe('context.getParameter(context.BLEND)', 'false');
|
||||
shouldBe('context.getParameter(context.BLEND_COLOR)', '[0, 0, 0, 0]');
|
||||
|
|
|
@ -22,6 +22,7 @@ description("Test texImage2D conversions.");
|
|||
var wtu = WebGLTestUtils;
|
||||
var canvas = document.getElementById("example");
|
||||
var gl = wtu.create3DContext(canvas);
|
||||
gl.disable(gl.DITHER);
|
||||
var program = wtu.setupTexturedQuad(gl);
|
||||
|
||||
glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
|
||||
|
|
|
@ -46,6 +46,7 @@ function init()
|
|||
|
||||
var canvas = document.getElementById("example");
|
||||
gl = wtu.create3DContext(canvas);
|
||||
gl.disable(gl.DITHER);
|
||||
var program = wtu.setupTexturedQuad(gl);
|
||||
|
||||
gl.clearColor(0,0,0,1);
|
||||
|
|
|
@ -35,6 +35,7 @@ description('Tests texSubImage2D upload path from Uint8Array');
|
|||
var wtu = WebGLTestUtils;
|
||||
var canvas = document.getElementById("example");
|
||||
var gl = wtu.create3DContext(canvas);
|
||||
gl.disable(gl.DITHER);
|
||||
var program = wtu.setupProgram(
|
||||
gl,
|
||||
[wtu.setupSimpleTextureVertexShader(gl),
|
||||
|
|
|
@ -211,12 +211,17 @@ function checkRect(x, y, width, height, color) {
|
|||
function fillLevel(target, level, size, color) {
|
||||
var numPixels = size * size;
|
||||
var pixels = new Uint8Array(numPixels * 4);
|
||||
for (var jj = 0; jj < numPixels; ++jj) {
|
||||
var pixelRow = new Uint8Array(size * 4);
|
||||
for (var jj = 0; jj < size; ++jj) {
|
||||
var off = jj * 4;
|
||||
pixels[off + 0] = color[0];
|
||||
pixels[off + 1] = color[1];
|
||||
pixels[off + 2] = color[2];
|
||||
pixels[off + 3] = color[3];
|
||||
pixelRow[off + 0] = color[0];
|
||||
pixelRow[off + 1] = color[1];
|
||||
pixelRow[off + 2] = color[2];
|
||||
pixelRow[off + 3] = color[3];
|
||||
}
|
||||
for (var jj = 0; jj < size; ++jj) {
|
||||
var off = jj * size * 4;
|
||||
pixels.set(pixelRow, off);
|
||||
}
|
||||
gl.texImage2D(
|
||||
target, level, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE,
|
||||
|
|
|
@ -14,7 +14,7 @@ found in the LICENSE file.
|
|||
<script src="../resources/webgl-test-utils.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="example" width="256" height="256" style="width: 40px; height: 40px;"></canvas>
|
||||
<canvas id="example" width="32" height="32" style="width: 40px; height: 40px;"></canvas>
|
||||
<div id="description"></div>
|
||||
<div id="console"></div>
|
||||
<script id="vshader" type="x-shader/x-vertex">
|
||||
|
@ -149,14 +149,29 @@ function checkTexture(width, height, cubeMap) {
|
|||
|
||||
function fillLevel(level, width, height, color, opt_cubemap) {
|
||||
var numPixels = width * height;
|
||||
var pixels = new Uint8Array(numPixels * 4);
|
||||
for (var jj = 0; jj < numPixels; ++jj) {
|
||||
var pixels = null;
|
||||
var largeDim = Math.max(width, height);
|
||||
var smallDim = Math.min(width, height);
|
||||
|
||||
var pixelRow = new Uint8Array(largeDim * 4);
|
||||
for (var jj = 0; jj < largeDim; ++jj) {
|
||||
var off = jj * 4;
|
||||
pixels[off + 0] = color[0];
|
||||
pixels[off + 1] = color[1];
|
||||
pixels[off + 2] = color[2];
|
||||
pixels[off + 3] = color[3];
|
||||
pixelRow[off + 0] = color[0];
|
||||
pixelRow[off + 1] = color[1];
|
||||
pixelRow[off + 2] = color[2];
|
||||
pixelRow[off + 3] = color[3];
|
||||
}
|
||||
|
||||
if (largeDim == numPixels) {
|
||||
pixels = pixelRow;
|
||||
} else {
|
||||
var pixels = new Uint8Array(numPixels * 4);
|
||||
for (var jj = 0; jj < smallDim; ++jj) {
|
||||
var off = jj * largeDim * 4;
|
||||
pixels.set(pixelRow, off);
|
||||
}
|
||||
}
|
||||
|
||||
var targets = opt_cubemap ? [
|
||||
gl.TEXTURE_CUBE_MAP_POSITIVE_X,
|
||||
gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||
|
|
|
@ -519,18 +519,14 @@ function testConstructionWithOutOfRangeValues(type, name) {
|
|||
}, "Construction of " + name + " with out-of-range offset");
|
||||
}
|
||||
|
||||
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");
|
||||
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");
|
||||
}
|
||||
}
|
||||
shouldThrowIndexSizeErr(function() {
|
||||
var buffer = new ArrayBuffer(4);
|
||||
|
@ -895,7 +891,7 @@ function runTests() {
|
|||
testCase.expectedValues);
|
||||
testConstructionWithNullBuffer(type, name);
|
||||
testConstructionWithOutOfRangeValues(type, name);
|
||||
testConstructionWithNegativeOutOfRangeValues(type, name);
|
||||
testConstructionWithNegativeOutOfRangeValues(type, name, testCase.elementSizeInBytes);
|
||||
testConstructionWithUnalignedOffset(type, name, testCase.elementSizeInBytes);
|
||||
testConstructionWithUnalignedLength(type, name, testCase.elementSizeInBytes);
|
||||
testConstructionOfHugeArray(type, name, testCase.elementSizeInBytes);
|
||||
|
|
|
@ -95,6 +95,15 @@ locationArray0 = contextA.getUniformLocation(programS, "u_array[0]");
|
|||
shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.uniform1i(locationSx, 3)");
|
||||
shouldBe("contextA.getUniform(programS, locationSx)", "3");
|
||||
|
||||
// getUniformLocation should return a different object everytime, should not cache and return the same object
|
||||
debug("Testing that getUniformLocation returns a different object everytime");
|
||||
locationSx = contextA.getUniformLocation(programS, "u_struct.x");
|
||||
locationSx2 = contextA.getUniformLocation(programS, "u_struct.x");
|
||||
shouldBeFalse("locationSx === locationSx2");
|
||||
locationSx.foo = {};
|
||||
locationSx2.foo = {};
|
||||
shouldBeFalse("locationSx.foo === locationSx2.foo");
|
||||
|
||||
successfullyParsed = true;
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# HG changeset patch
|
||||
# Parent 587913950733649a7cbd184a195b08cbb86b2fed
|
||||
diff --git a/content/canvas/test/webgl/conformance/more/00_test_list.txt b/content/canvas/test/webgl/conformance/more/00_test_list.txt
|
||||
--- a/content/canvas/test/webgl/conformance/more/00_test_list.txt
|
||||
+++ b/content/canvas/test/webgl/conformance/more/00_test_list.txt
|
||||
@@ -1,12 +1,15 @@
|
||||
conformance/constants.html
|
||||
conformance/getContext.html
|
||||
conformance/methods.html
|
||||
-conformance/quickCheckAPI.html
|
||||
+#this test causes whichever comes after to intermittently time out.
|
||||
+#forcing a GC run doesn't solve this issue. Could be something about using a random amount of memory that
|
||||
+#can be too high, causing e.g. swapping.
|
||||
+#conformance/quickCheckAPI.html
|
||||
conformance/webGLArrays.html
|
||||
functions/bindBuffer.html
|
||||
functions/bindBufferBadArgs.html
|
||||
functions/bindFramebufferLeaveNonZero.html
|
||||
functions/bufferData.html
|
||||
functions/bufferDataBadArgs.html
|
||||
functions/bufferSubData.html
|
||||
functions/bufferSubDataBadArgs.html
|
|
@ -1,6 +1,5 @@
|
|||
# HG changeset patch
|
||||
# Parent 95e5d92098ad52788348e609e261305d4b05b551
|
||||
|
||||
# Parent 9c1a90f789e3d43455cb18a9a911627c80c0d9ac
|
||||
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
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
conformance/context/context-lost-restored.html
|
||||
conformance/context/premultiplyalpha-test.html
|
||||
conformance/glsl/misc/glsl-long-variable-names.html
|
||||
conformance/glsl/misc/shader-with-256-character-identifier.frag.html
|
||||
|
@ -11,3 +12,4 @@ 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/misc/object-deletion-behaviour.html
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
conformance/context/context-lost-restored.html
|
||||
conformance/context/premultiplyalpha-test.html
|
||||
conformance/glsl/misc/glsl-function-nodes.html
|
||||
conformance/glsl/misc/glsl-long-variable-names.html
|
||||
|
@ -9,3 +10,4 @@ conformance/more/functions/uniformiBadArgs.html
|
|||
conformance/glsl/misc/attrib-location-length-limits.html
|
||||
conformance/glsl/misc/uniform-location-length-limits.html
|
||||
conformance/renderbuffers/framebuffer-object-attachment.html
|
||||
conformance/misc/object-deletion-behaviour.html
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
conformance/context/context-lost-restored.html
|
||||
conformance/context/premultiplyalpha-test.html
|
||||
conformance/glsl/functions/glsl-function-atan.html
|
||||
conformance/glsl/functions/glsl-function-atan-xy.html
|
||||
conformance/glsl/functions/glsl-function-mod-gentype.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
|
||||
|
@ -11,3 +11,4 @@ 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/renderbuffers/framebuffer-object-attachment.html
|
||||
conformance/misc/object-deletion-behaviour.html
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
# HG changeset patch
|
||||
# Parent 2dbd71999fe8a8da476ab6cc97716f7b7c294fb8
|
||||
# User Doug Sherk <dsherk@mozilla.com>
|
||||
Bug 693703: added additional logging information for mochitests, incl. image reference differences r=bjacob
|
||||
|
||||
Added some code to print to dump output of WebGL mochitest failures. Also added
|
||||
special code to handle incorrect reference images. It will now provide the user
|
||||
with a way to compare the reference and actual drawings.
|
||||
|
||||
# Parent 4ed0bad4933ba69927ee5f75cf67093d3e99566a
|
||||
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
|
||||
@@ -115,17 +115,18 @@ function init()
|
||||
@@ -117,17 +117,18 @@ function init()
|
||||
var bufFunction = new Uint8Array(width * height * 4);
|
||||
var bufMacro = new Uint8Array(width * height * 4);
|
||||
|
||||
|
@ -148,7 +141,7 @@ diff --git a/content/canvas/test/webgl/resources/js-test-pre.js b/content/canvas
|
|||
+
|
||||
+ testFailed(msg);
|
||||
+
|
||||
+ var data = 'REFTEST TEST-UNEXPECTED-FAIL | ' + msg + ' | image comparison (==)\n' +
|
||||
+ var data = 'REFTEST TEST-DEBUG-INFO | ' + msg + ' | image comparison (==)\n' +
|
||||
+ 'REFTEST IMAGE 1 (TEST): ' + testData + '\n' +
|
||||
+ 'REFTEST IMAGE 2 (REFERENCE): ' + refData;
|
||||
+ dump('FAIL: ' + data + '\n');
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# HG changeset patch
|
||||
# Parent 041265c6ac29c43856a8df4d4581cb6a04b95a68
|
||||
|
||||
# Parent bf9d7872738cdb7cf425e6dd060ae62882487e1a
|
||||
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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# HG changeset patch
|
||||
# Parent 0e44920c4e5e697f86e0a42b909c5a032d70a1a4
|
||||
# Parent 41137626edf2a358f2be1b7ed3f83211230ab4f5
|
||||
|
||||
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
|
||||
|
|
|
@ -83,6 +83,10 @@ DIRS += \
|
|||
workers \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_B2G_RIL #{
|
||||
DIRS += telephony
|
||||
endif #}
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += tests
|
||||
ifneq (,$(filter gtk2 cocoa windows android qt os2,$(MOZ_WIDGET_TOOLKIT)))
|
||||
|
|
|
@ -1641,6 +1641,7 @@ jsid nsDOMClassInfo::sURL_id = JSID_VOID;
|
|||
jsid nsDOMClassInfo::sKeyPath_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sAutoIncrement_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sUnique_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sMultiEntry_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sOnload_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sOnerror_id = JSID_VOID;
|
||||
|
||||
|
@ -1904,6 +1905,7 @@ nsDOMClassInfo::DefineStaticJSVals(JSContext *cx)
|
|||
SET_JSID_TO_STRING(sKeyPath_id, cx, "keyPath");
|
||||
SET_JSID_TO_STRING(sAutoIncrement_id, cx, "autoIncrement");
|
||||
SET_JSID_TO_STRING(sUnique_id, cx, "unique");
|
||||
SET_JSID_TO_STRING(sMultiEntry_id, cx, "multiEntry");
|
||||
SET_JSID_TO_STRING(sOnload_id, cx, "onload");
|
||||
SET_JSID_TO_STRING(sOnerror_id, cx, "onerror");
|
||||
|
||||
|
@ -4902,6 +4904,7 @@ nsDOMClassInfo::ShutDown()
|
|||
sKeyPath_id = JSID_VOID;
|
||||
sAutoIncrement_id = JSID_VOID;
|
||||
sUnique_id = JSID_VOID;
|
||||
sMultiEntry_id = JSID_VOID;
|
||||
sOnload_id = JSID_VOID;
|
||||
sOnerror_id = JSID_VOID;
|
||||
|
||||
|
|
|
@ -295,6 +295,7 @@ public:
|
|||
static jsid sKeyPath_id;
|
||||
static jsid sAutoIncrement_id;
|
||||
static jsid sUnique_id;
|
||||
static jsid sMultiEntry_id;
|
||||
static jsid sOnload_id;
|
||||
static jsid sOnerror_id;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ DOM_SRCDIRS = \
|
|||
dom/src/offline \
|
||||
dom/src/geolocation \
|
||||
dom/src/notification \
|
||||
dom/telephony \
|
||||
dom/workers \
|
||||
content/xbl/src \
|
||||
content/xul/document/src \
|
||||
|
|
|
@ -97,7 +97,8 @@ DatabaseInfo::~DatabaseInfo()
|
|||
IndexInfo::IndexInfo()
|
||||
: id(LL_MININT),
|
||||
unique(false),
|
||||
autoIncrement(false)
|
||||
autoIncrement(false),
|
||||
multiEntry(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(IndexInfo);
|
||||
}
|
||||
|
@ -107,7 +108,8 @@ IndexInfo::IndexInfo(const IndexInfo& aOther)
|
|||
name(aOther.name),
|
||||
keyPath(aOther.keyPath),
|
||||
unique(aOther.unique),
|
||||
autoIncrement(aOther.autoIncrement)
|
||||
autoIncrement(aOther.autoIncrement),
|
||||
multiEntry(aOther.multiEntry)
|
||||
{
|
||||
MOZ_COUNT_CTOR(IndexInfo);
|
||||
}
|
||||
|
|
|
@ -121,6 +121,7 @@ struct IndexInfo
|
|||
nsString keyPath;
|
||||
bool unique;
|
||||
bool autoIncrement;
|
||||
bool multiEntry;
|
||||
};
|
||||
|
||||
struct ObjectStoreInfo
|
||||
|
@ -149,7 +150,8 @@ struct IndexUpdateInfo
|
|||
~IndexUpdateInfo();
|
||||
#endif
|
||||
|
||||
IndexInfo info;
|
||||
PRInt64 indexId;
|
||||
bool indexUnique;
|
||||
Key value;
|
||||
};
|
||||
|
||||
|
|
|
@ -387,11 +387,8 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
|
|||
|
||||
DatabaseInfo* databaseInfo = Info();
|
||||
|
||||
if (databaseInfo->ContainsStoreName(aName)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
|
||||
}
|
||||
|
||||
nsString keyPath;
|
||||
keyPath.SetIsVoid(true);
|
||||
bool autoIncrement = false;
|
||||
|
||||
if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) {
|
||||
|
@ -437,8 +434,17 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
|
|||
autoIncrement = !!boolVal;
|
||||
}
|
||||
|
||||
if (!IDBObjectStore::IsValidKeyPath(aCx, keyPath)) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
if (databaseInfo->ContainsStoreName(aName)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
|
||||
}
|
||||
|
||||
if (!keyPath.IsVoid()) {
|
||||
if (keyPath.IsEmpty() && autoIncrement) {
|
||||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
}
|
||||
if (!IDBObjectStore::IsValidKeyPath(aCx, keyPath)) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoPtr<ObjectStoreInfo> newInfo(new ObjectStoreInfo());
|
||||
|
@ -498,6 +504,9 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName)
|
|||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
info->RemoveObjectStore(aName);
|
||||
|
||||
transaction->ReleaseCachedObjectStore(aName);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -723,8 +732,8 @@ CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
|||
{
|
||||
nsCOMPtr<mozIStorageStatement> stmt =
|
||||
mTransaction->GetCachedStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO object_store (id, name, key_path, auto_increment) "
|
||||
"VALUES (:id, :name, :key_path, :auto_increment)"
|
||||
"INSERT INTO object_store (id, auto_increment, name, key_path) "
|
||||
"VALUES (:id, :auto_increment, :name, :key_path)"
|
||||
));
|
||||
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
|
@ -734,15 +743,17 @@ CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
|||
mObjectStore->Id());
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
|
||||
mObjectStore->IsAutoIncrement() ? 1 : 0);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name());
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
|
||||
mObjectStore->KeyPath());
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
|
||||
mObjectStore->IsAutoIncrement() ? 1 : 0);
|
||||
rv = mObjectStore->HasKeyPath() ?
|
||||
stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
|
||||
mObjectStore->KeyPath()) :
|
||||
stmt->BindNullByName(NS_LITERAL_CSTRING("key_path"));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = stmt->Execute();
|
||||
|
|
|
@ -247,8 +247,18 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
|
|||
|
||||
info->id = stmt->AsInt64(1);
|
||||
|
||||
rv = stmt->GetString(2, info->keyPath);
|
||||
PRInt32 columnType;
|
||||
nsresult rv = stmt->GetTypeOfIndex(2, &columnType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
|
||||
info->keyPath.SetIsVoid(true);
|
||||
}
|
||||
else {
|
||||
NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_TEXT,
|
||||
"Should be a string");
|
||||
rv = stmt->GetString(2, info->keyPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
info->autoIncrement = !!stmt->AsInt32(3);
|
||||
info->databaseId = aDatabaseId;
|
||||
|
@ -263,7 +273,7 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
|
|||
|
||||
// Load index information
|
||||
rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT object_store_id, id, name, key_path, unique_index, "
|
||||
"SELECT object_store_id, id, name, key_path, unique_index, multientry, "
|
||||
"object_store_autoincrement "
|
||||
"FROM object_store_index"
|
||||
), getter_AddRefs(stmt));
|
||||
|
@ -297,7 +307,8 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
indexInfo->unique = !!stmt->AsInt32(4);
|
||||
indexInfo->autoIncrement = !!stmt->AsInt32(5);
|
||||
indexInfo->multiEntry = !!stmt->AsInt32(5);
|
||||
indexInfo->autoIncrement = !!stmt->AsInt32(6);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -318,6 +318,7 @@ IDBIndex::Create(IDBObjectStore* aObjectStore,
|
|||
index->mName = aIndexInfo->name;
|
||||
index->mKeyPath = aIndexInfo->keyPath;
|
||||
index->mUnique = aIndexInfo->unique;
|
||||
index->mMultiEntry = aIndexInfo->multiEntry;
|
||||
index->mAutoIncrement = aIndexInfo->autoIncrement;
|
||||
|
||||
return index.forget();
|
||||
|
@ -396,6 +397,15 @@ IDBIndex::GetUnique(bool* aUnique)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBIndex::GetMultiEntry(bool* aMultiEntry)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
*aMultiEntry = mMultiEntry;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IDBIndex::GetObjectStore(nsIIDBObjectStore** aObjectStore)
|
||||
{
|
||||
|
|
|
@ -87,6 +87,11 @@ public:
|
|||
return mUnique;
|
||||
}
|
||||
|
||||
bool IsMultiEntry() const
|
||||
{
|
||||
return mMultiEntry;
|
||||
}
|
||||
|
||||
bool IsAutoIncrement() const
|
||||
{
|
||||
return mAutoIncrement;
|
||||
|
@ -110,6 +115,7 @@ private:
|
|||
nsString mName;
|
||||
nsString mKeyPath;
|
||||
bool mUnique;
|
||||
bool mMultiEntry;
|
||||
bool mAutoIncrement;
|
||||
};
|
||||
|
||||
|
|
|
@ -97,9 +97,6 @@ public:
|
|||
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
||||
}
|
||||
|
||||
nsresult UpdateIndexes(mozIStorageConnection* aConnection,
|
||||
PRInt64 aObjectDataId);
|
||||
|
||||
private:
|
||||
// In-params.
|
||||
nsRefPtr<IDBObjectStore> mObjectStore;
|
||||
|
@ -422,15 +419,13 @@ typedef nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> KeyPathTokenizer;
|
|||
|
||||
inline
|
||||
nsresult
|
||||
GetKeyFromValue(JSContext* aCx,
|
||||
jsval aVal,
|
||||
const nsAString& aKeyPath,
|
||||
Key& aKey)
|
||||
GetJSValFromKeyPath(JSContext* aCx,
|
||||
jsval aVal,
|
||||
const nsAString& aKeyPath,
|
||||
jsval& aKey)
|
||||
{
|
||||
NS_ASSERTION(aCx, "Null pointer!");
|
||||
// aVal can be primitive iff the key path is empty.
|
||||
NS_ASSERTION(!JSVAL_IS_PRIMITIVE(aVal) || aKeyPath.IsEmpty(),
|
||||
"Why are we here!?");
|
||||
NS_ASSERTION(IDBObjectStore::IsValidKeyPath(aCx, aKeyPath),
|
||||
"This will explode!");
|
||||
|
||||
|
@ -438,25 +433,39 @@ GetKeyFromValue(JSContext* aCx,
|
|||
|
||||
jsval intermediate = aVal;
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
nsString token(tokenizer.nextToken());
|
||||
const nsDependentSubstring& token = tokenizer.nextToken();
|
||||
|
||||
if (!token.Length()) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
NS_ASSERTION(!token.IsEmpty(), "Should be a valid keypath");
|
||||
|
||||
const jschar* keyPathChars = token.get();
|
||||
const jschar* keyPathChars = token.BeginReading();
|
||||
const size_t keyPathLen = token.Length();
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(intermediate)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
intermediate = JSVAL_VOID;
|
||||
break;
|
||||
}
|
||||
|
||||
JSBool ok = JS_GetUCProperty(aCx, JSVAL_TO_OBJECT(intermediate),
|
||||
keyPathChars, keyPathLen, &intermediate);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
}
|
||||
|
||||
aKey = intermediate;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(aKey.SetFromJSVal(aCx, intermediate))) {
|
||||
inline
|
||||
nsresult
|
||||
GetKeyFromValue(JSContext* aCx,
|
||||
jsval aVal,
|
||||
const nsAString& aKeyPath,
|
||||
Key& aKey)
|
||||
{
|
||||
jsval key;
|
||||
nsresult rv = GetJSValFromKeyPath(aCx, aVal, aKeyPath, key);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (NS_FAILED(aKey.SetFromJSVal(aCx, key))) {
|
||||
aKey.Unset();
|
||||
}
|
||||
|
||||
|
@ -574,82 +583,63 @@ IDBObjectStore::IsValidKeyPath(JSContext* aCx,
|
|||
|
||||
// static
|
||||
nsresult
|
||||
IDBObjectStore::GetKeyPathValueFromStructuredData(const PRUint8* aData,
|
||||
PRUint32 aDataLength,
|
||||
const nsAString& aKeyPath,
|
||||
JSContext* aCx,
|
||||
Key& aValue)
|
||||
IDBObjectStore::AppendIndexUpdateInfo(PRInt64 aIndexID,
|
||||
const nsAString& aKeyPath,
|
||||
bool aUnique,
|
||||
bool aMultiEntry,
|
||||
JSContext* aCx,
|
||||
jsval aObject,
|
||||
nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
|
||||
{
|
||||
NS_ASSERTION(aData, "Null pointer!");
|
||||
NS_ASSERTION(aDataLength, "Empty data!");
|
||||
NS_ASSERTION(aCx, "Null pointer!");
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
jsval clone;
|
||||
if (!JS_ReadStructuredClone(aCx, reinterpret_cast<const uint64*>(aData),
|
||||
aDataLength, JS_STRUCTURED_CLONE_VERSION,
|
||||
&clone, NULL, NULL)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(clone) && !aKeyPath.IsEmpty()) {
|
||||
// This isn't an object, so just leave the key unset.
|
||||
aValue.Unset();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = GetKeyFromValue(aCx, clone, aKeyPath, aValue);
|
||||
jsval key;
|
||||
nsresult rv = GetJSValFromKeyPath(aCx, aObject, aKeyPath, key);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
IDBObjectStore::GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo,
|
||||
JSContext* aCx,
|
||||
jsval aObject,
|
||||
nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
|
||||
{
|
||||
JSObject* cloneObj = nsnull;
|
||||
|
||||
PRUint32 count = aObjectStoreInfo->indexes.Length();
|
||||
if (count) {
|
||||
if (!aUpdateInfoArray.SetCapacity(count)) {
|
||||
NS_ERROR("Out of memory!");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (aMultiEntry && !JSVAL_IS_PRIMITIVE(key) &&
|
||||
JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(key))) {
|
||||
JSObject* array = JSVAL_TO_OBJECT(key);
|
||||
jsuint arrayLength;
|
||||
if (!JS_GetArrayLength(aCx, array, &arrayLength)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
for (PRUint32 indexesIndex = 0; indexesIndex < count; indexesIndex++) {
|
||||
const IndexInfo& indexInfo = aObjectStoreInfo->indexes[indexesIndex];
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(aObject) && !indexInfo.keyPath.IsEmpty()) {
|
||||
continue;
|
||||
for (jsuint arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
|
||||
jsval arrayItem;
|
||||
if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
Key value;
|
||||
nsresult rv = GetKeyFromValue(aCx, aObject, indexInfo.keyPath, value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (value.IsUnset()) {
|
||||
if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) ||
|
||||
value.IsUnset()) {
|
||||
// Not a value we can do anything with, ignore it.
|
||||
continue;
|
||||
}
|
||||
|
||||
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
|
||||
updateInfo->info = indexInfo;
|
||||
updateInfo->indexId = aIndexID;
|
||||
updateInfo->indexUnique = aUnique;
|
||||
updateInfo->value = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
aUpdateInfoArray.Clear();
|
||||
Key value;
|
||||
if (NS_FAILED(value.SetFromJSVal(aCx, key)) ||
|
||||
value.IsUnset()) {
|
||||
// Not a value we can do anything with, ignore it.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
|
||||
updateInfo->indexId = aIndexID;
|
||||
updateInfo->indexUnique = aUnique;
|
||||
updateInfo->value = value;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
// static
|
||||
nsresult
|
||||
IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
|
||||
PRInt64 aObjectStoreId,
|
||||
|
@ -659,21 +649,13 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
|
|||
PRInt64 aObjectDataId,
|
||||
const nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (aAutoIncrement) {
|
||||
NS_ASSERTION(aObjectDataId != LL_MININT, "Bad objectData id!");
|
||||
}
|
||||
else {
|
||||
NS_ASSERTION(aObjectDataId == LL_MININT, "Bad objectData id!");
|
||||
}
|
||||
#endif
|
||||
|
||||
PRUint32 indexCount = aUpdateInfoArray.Length();
|
||||
NS_ASSERTION(!aAutoIncrement || aObjectDataId != LL_MININT,
|
||||
"Bad objectData id!");
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv;
|
||||
|
||||
if (!aAutoIncrement) {
|
||||
if (aObjectDataId == LL_MININT) {
|
||||
stmt = aTransaction->GetCachedStatement(
|
||||
"SELECT id "
|
||||
"FROM object_data "
|
||||
|
@ -735,25 +717,24 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
for (PRUint32 indexIndex = 0; indexIndex < indexCount; indexIndex++) {
|
||||
const IndexUpdateInfo& updateInfo = aUpdateInfoArray[indexIndex];
|
||||
|
||||
NS_ASSERTION(updateInfo.info.autoIncrement == aAutoIncrement, "Huh?!");
|
||||
PRUint32 infoCount = aUpdateInfoArray.Length();
|
||||
for (PRUint32 i = 0; i < infoCount; i++) {
|
||||
const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i];
|
||||
|
||||
// Insert new values.
|
||||
stmt = aTransaction->IndexDataInsertStatement(aAutoIncrement,
|
||||
updateInfo.info.unique);
|
||||
updateInfo.indexUnique);
|
||||
NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE);
|
||||
|
||||
mozStorageStatementScoper scoper4(stmt);
|
||||
|
||||
rv = stmt->BindInt64ByName(indexId, updateInfo.info.id);
|
||||
rv = stmt->BindInt64ByName(indexId, updateInfo.indexId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->BindInt64ByName(objectDataId, aObjectDataId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!updateInfo.info.autoIncrement) {
|
||||
if (!aAutoIncrement) {
|
||||
rv = aObjectStoreKey.BindToStatement(stmt, objectDataKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -762,6 +743,23 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
if (rv == NS_ERROR_STORAGE_CONSTRAINT && updateInfo.indexUnique) {
|
||||
// If we're inserting multiple entries for the same unique index, then
|
||||
// we might have failed to insert due to colliding with another entry for
|
||||
// the same index in which case we should ignore it.
|
||||
|
||||
for (PRInt32 j = (PRInt32)i - 1;
|
||||
j >= 0 && aUpdateInfoArray[j].indexId == updateInfo.indexId;
|
||||
--j) {
|
||||
if (updateInfo.value == aUpdateInfoArray[j].value) {
|
||||
// We found a key with the same value for the same index. So we
|
||||
// must have had a collision with a value we just inserted.
|
||||
rv = NS_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -906,24 +904,20 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
|
|||
|
||||
// Return DATA_ERR if a key was passed in and this objectStore uses inline
|
||||
// keys.
|
||||
if (!JSVAL_IS_VOID(aKeyVal) && !mKeyPath.IsEmpty()) {
|
||||
if (!JSVAL_IS_VOID(aKeyVal) && HasKeyPath()) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
||||
}
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
if (mKeyPath.IsEmpty()) {
|
||||
if (!HasKeyPath()) {
|
||||
// Out-of-line keys must be passed in.
|
||||
rv = aKey.SetFromJSVal(aCx, aKeyVal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
else if (!mAutoIncrement) {
|
||||
// Inline keys live on the object. Make sure that the value passed in is an
|
||||
// object.
|
||||
if (JSVAL_IS_PRIMITIVE(aValue)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
||||
}
|
||||
|
||||
rv = GetKeyFromValue(aCx, aValue, mKeyPath, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -940,28 +934,116 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
|
|||
NS_ERROR("This should never fail!");
|
||||
}
|
||||
|
||||
rv = GetIndexUpdateInfo(info, aCx, aValue, aUpdateInfoArray);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
PRUint32 count = info->indexes.Length();
|
||||
aUpdateInfoArray.SetCapacity(count); // Pretty good estimate
|
||||
for (PRUint32 indexesIndex = 0; indexesIndex < count; indexesIndex++) {
|
||||
const IndexInfo& indexInfo = info->indexes[indexesIndex];
|
||||
|
||||
const jschar* keyPathChars =
|
||||
reinterpret_cast<const jschar*>(mKeyPath.get());
|
||||
const size_t keyPathLen = mKeyPath.Length();
|
||||
JSBool ok = JS_FALSE;
|
||||
rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath,
|
||||
indexInfo.unique, indexInfo.multiEntry,
|
||||
aCx, aValue, aUpdateInfoArray);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!mKeyPath.IsEmpty() && aKey.IsUnset()) {
|
||||
NS_ASSERTION(mAutoIncrement, "Should have bailed earlier!");
|
||||
nsString targetObjectPropName;
|
||||
JSObject* targetObject = nsnull;
|
||||
|
||||
JSObject* obj = JS_NewObject(aCx, &gDummyPropClass, nsnull, nsnull);
|
||||
NS_ENSURE_TRUE(obj, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
rv = NS_OK;
|
||||
if (mAutoIncrement && HasKeyPath()) {
|
||||
NS_ASSERTION(aKey.IsUnset(), "Shouldn't have gotten the key yet!");
|
||||
|
||||
jsval key = OBJECT_TO_JSVAL(obj);
|
||||
|
||||
ok = JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(aValue), keyPathChars,
|
||||
keyPathLen, key, nsnull, nsnull,
|
||||
JSPROP_ENUMERATE);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
if (JSVAL_IS_PRIMITIVE(aValue)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
||||
}
|
||||
|
||||
// From this point on we have to try to remove the property.
|
||||
KeyPathTokenizer tokenizer(mKeyPath, '.');
|
||||
NS_ASSERTION(tokenizer.hasMoreTokens(),
|
||||
"Shouldn't have empty keypath and autoincrement");
|
||||
|
||||
JSObject* obj = JSVAL_TO_OBJECT(aValue);
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsDependentSubstring& token = tokenizer.nextToken();
|
||||
|
||||
NS_ASSERTION(!token.IsEmpty(), "Should be a valid keypath");
|
||||
|
||||
const jschar* keyPathChars = token.BeginReading();
|
||||
const size_t keyPathLen = token.Length();
|
||||
|
||||
JSBool hasProp;
|
||||
if (!targetObject) {
|
||||
// We're still walking the chain of existing objects
|
||||
|
||||
JSBool ok = JS_HasUCProperty(aCx, obj, keyPathChars, keyPathLen,
|
||||
&hasProp);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
if (hasProp) {
|
||||
// Get if the property exists...
|
||||
jsval intermediate;
|
||||
JSBool ok = JS_GetUCProperty(aCx, obj, keyPathChars, keyPathLen,
|
||||
&intermediate);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
if (tokenizer.hasMoreTokens()) {
|
||||
// ...and walk to it if there are more steps...
|
||||
if (JSVAL_IS_PRIMITIVE(intermediate)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
||||
}
|
||||
obj = JSVAL_TO_OBJECT(intermediate);
|
||||
}
|
||||
else {
|
||||
// ...otherwise use it as key
|
||||
aKey.SetFromJSVal(aCx, intermediate);
|
||||
if (aKey.IsUnset()) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the property doesn't exist, fall into below path of starting
|
||||
// to define properties
|
||||
targetObject = obj;
|
||||
targetObjectPropName = token;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetObject) {
|
||||
// We have started inserting new objects or are about to just insert
|
||||
// the first one.
|
||||
if (tokenizer.hasMoreTokens()) {
|
||||
// If we're not at the end, we need to add a dummy object to the chain.
|
||||
JSObject* dummy = JS_NewObject(aCx, nsnull, nsnull, nsnull);
|
||||
if (!dummy) {
|
||||
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(),
|
||||
token.Length(),
|
||||
OBJECT_TO_JSVAL(dummy), nsnull, nsnull,
|
||||
JSPROP_ENUMERATE)) {
|
||||
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
obj = dummy;
|
||||
}
|
||||
else {
|
||||
JSObject* dummy = JS_NewObject(aCx, &gDummyPropClass, nsnull, nsnull);
|
||||
if (!dummy) {
|
||||
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(),
|
||||
token.Length(), OBJECT_TO_JSVAL(dummy),
|
||||
nsnull, nsnull, JSPROP_ENUMERATE)) {
|
||||
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSStructuredCloneCallbacks callbacks = {
|
||||
|
@ -979,13 +1061,15 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
|
|||
rv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (targetObject) {
|
||||
// If this fails, we lose, and the web page sees a magical property
|
||||
// appear on the object :-(
|
||||
jsval succeeded;
|
||||
ok = JS_DeleteUCProperty2(aCx, JSVAL_TO_OBJECT(aValue), keyPathChars,
|
||||
keyPathLen, &succeeded);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
if (!JS_DeleteUCProperty2(aCx, targetObject,
|
||||
targetObjectPropName.get(),
|
||||
targetObjectPropName.Length(), &succeeded)) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
NS_ASSERTION(JSVAL_IS_BOOLEAN(succeeded), "Wtf?");
|
||||
NS_ENSURE_TRUE(JSVAL_TO_BOOLEAN(succeeded),
|
||||
NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
@ -1372,6 +1456,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
|
|||
NS_ASSERTION(mTransaction->IsOpen(), "Impossible!");
|
||||
|
||||
bool unique = false;
|
||||
bool multiEntry = false;
|
||||
|
||||
// Get optional arguments.
|
||||
if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) {
|
||||
|
@ -1395,6 +1480,17 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
|
|||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
unique = !!boolVal;
|
||||
|
||||
if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sMultiEntry_id, &val)) {
|
||||
NS_WARNING("JS_GetPropertyById failed!");
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
if (!JS_ValueToBoolean(aCx, val, &boolVal)) {
|
||||
NS_WARNING("JS_ValueToBoolean failed!");
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
multiEntry = !!boolVal;
|
||||
}
|
||||
|
||||
DatabaseInfo* databaseInfo = mTransaction->Database()->Info();
|
||||
|
@ -1409,6 +1505,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
|
|||
indexInfo->name = aName;
|
||||
indexInfo->keyPath = aKeyPath;
|
||||
indexInfo->unique = unique;
|
||||
indexInfo->multiEntry = multiEntry;
|
||||
indexInfo->autoIncrement = mAutoIncrement;
|
||||
|
||||
// Don't leave this in the list if we fail below!
|
||||
|
@ -1530,6 +1627,14 @@ IDBObjectStore::DeleteIndex(const nsAString& aName)
|
|||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
info->indexes.RemoveElementAt(index);
|
||||
|
||||
for (PRUint32 i = 0; i < mCreatedIndexes.Length(); i++) {
|
||||
if (mCreatedIndexes[i]->Name() == aName) {
|
||||
mCreatedIndexes.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2160,8 +2265,9 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
|||
nsCOMPtr<mozIStorageStatement> stmt =
|
||||
mTransaction->GetCachedStatement(
|
||||
"INSERT INTO object_store_index (id, name, key_path, unique_index, "
|
||||
"object_store_id, object_store_autoincrement) "
|
||||
"VALUES (:id, :name, :key_path, :unique, :osid, :os_auto_increment)"
|
||||
"multientry, object_store_id, object_store_autoincrement) "
|
||||
"VALUES (:id, :name, :key_path, :unique, :multientry, :osid, "
|
||||
":os_auto_increment)"
|
||||
);
|
||||
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
|
@ -2182,6 +2288,10 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
|||
mIndex->IsUnique() ? 1 : 0);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"),
|
||||
mIndex->IsMultiEntry() ? 1 : 0);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
|
||||
mIndex->ObjectStore()->Id());
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
@ -2238,69 +2348,69 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
|
|||
mIndex->ObjectStore()->Id());
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
NS_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
bool hasResult;
|
||||
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
nsCOMPtr<mozIStorageStatement> insertStmt =
|
||||
mTransaction->IndexDataInsertStatement(mIndex->IsAutoIncrement(),
|
||||
mIndex->IsUnique());
|
||||
NS_ENSURE_TRUE(insertStmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
if (!hasResult) {
|
||||
// Bail early if we have no data to avoid creating the below runtime
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozStorageStatementScoper scoper2(insertStmt);
|
||||
ThreadLocalJSRuntime* tlsEntry =
|
||||
reinterpret_cast<ThreadLocalJSRuntime*>(PR_GetThreadPrivate(sTLSIndex));
|
||||
|
||||
rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
|
||||
mIndex->Id());
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
if (!tlsEntry) {
|
||||
tlsEntry = ThreadLocalJSRuntime::Create();
|
||||
NS_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_data_id"),
|
||||
stmt->AsInt64(0));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
PR_SetThreadPrivate(sTLSIndex, tlsEntry);
|
||||
}
|
||||
|
||||
if (!mIndex->IsAutoIncrement()) {
|
||||
NS_NAMED_LITERAL_CSTRING(objectDataKey, "object_data_key");
|
||||
|
||||
Key key;
|
||||
rv = key.SetFromStatement(stmt, 2);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv =
|
||||
key.BindToStatement(insertStmt, NS_LITERAL_CSTRING("object_data_key"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
JSContext* cx = tlsEntry->Context();
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
do {
|
||||
const PRUint8* data;
|
||||
PRUint32 dataLength;
|
||||
rv = stmt->GetSharedBlob(1, &dataLength, &data);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
NS_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
ThreadLocalJSRuntime* tlsEntry =
|
||||
reinterpret_cast<ThreadLocalJSRuntime*>(PR_GetThreadPrivate(sTLSIndex));
|
||||
|
||||
if (!tlsEntry) {
|
||||
tlsEntry = ThreadLocalJSRuntime::Create();
|
||||
NS_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
PR_SetThreadPrivate(sTLSIndex, tlsEntry);
|
||||
jsval clone;
|
||||
if (!JS_ReadStructuredClone(cx, reinterpret_cast<const uint64*>(data),
|
||||
dataLength, JS_STRUCTURED_CLONE_VERSION,
|
||||
&clone, NULL, NULL)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
nsTArray<IndexUpdateInfo> updateInfo;
|
||||
rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(),
|
||||
mIndex->KeyPath(),
|
||||
mIndex->IsUnique(),
|
||||
mIndex->IsMultiEntry(),
|
||||
tlsEntry->Context(),
|
||||
clone, updateInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt64 objectDataID = stmt->AsInt64(0);
|
||||
|
||||
Key key;
|
||||
rv = IDBObjectStore::GetKeyPathValueFromStructuredData(data, dataLength,
|
||||
mIndex->KeyPath(),
|
||||
tlsEntry->Context(),
|
||||
key);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (key.IsUnset()) {
|
||||
continue;
|
||||
if (!mIndex->IsAutoIncrement()) {
|
||||
rv = key.SetFromStatement(stmt, 2);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
key.SetFromInteger(objectDataID);
|
||||
}
|
||||
|
||||
rv = key.BindToStatement(insertStmt, NS_LITERAL_CSTRING("value"));
|
||||
rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(),
|
||||
key, mIndex->IsAutoIncrement(),
|
||||
false, objectDataID, updateInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = insertStmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
}
|
||||
} while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -76,17 +76,13 @@ public:
|
|||
IsValidKeyPath(JSContext* aCx, const nsAString& aKeyPath);
|
||||
|
||||
static nsresult
|
||||
GetKeyPathValueFromStructuredData(const PRUint8* aData,
|
||||
PRUint32 aDataLength,
|
||||
const nsAString& aKeyPath,
|
||||
JSContext* aCx,
|
||||
Key& aValue);
|
||||
|
||||
static nsresult
|
||||
GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo,
|
||||
JSContext* aCx,
|
||||
jsval aObject,
|
||||
nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
|
||||
AppendIndexUpdateInfo(PRInt64 aIndexID,
|
||||
const nsAString& aKeyPath,
|
||||
bool aUnique,
|
||||
bool aMultiEntry,
|
||||
JSContext* aCx,
|
||||
jsval aObject,
|
||||
nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
|
||||
|
||||
static nsresult
|
||||
UpdateIndexes(IDBTransaction* aTransaction,
|
||||
|
@ -145,6 +141,11 @@ public:
|
|||
return mKeyPath;
|
||||
}
|
||||
|
||||
const bool HasKeyPath() const
|
||||
{
|
||||
return !mKeyPath.IsVoid();
|
||||
}
|
||||
|
||||
IDBTransaction* Transaction()
|
||||
{
|
||||
return mTransaction;
|
||||
|
|
|
@ -213,6 +213,17 @@ IDBTransaction::OnRequestFinished()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
IDBTransaction::ReleaseCachedObjectStore(const nsAString& aName)
|
||||
{
|
||||
for (PRUint32 i = 0; i < mCreatedObjectStores.Length(); i++) {
|
||||
if (mCreatedObjectStores[i]->Name() == aName) {
|
||||
mCreatedObjectStores.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IDBTransaction::SetTransactionListener(IDBTransactionListener* aListener)
|
||||
{
|
||||
|
@ -406,7 +417,7 @@ IDBTransaction::IndexDataInsertStatement(bool aAutoIncrement,
|
|||
);
|
||||
}
|
||||
return GetCachedStatement(
|
||||
"INSERT INTO ai_index_data "
|
||||
"INSERT OR IGNORE INTO ai_index_data "
|
||||
"(index_id, ai_object_data_id, value) "
|
||||
"VALUES (:index_id, :object_data_id, :value)"
|
||||
);
|
||||
|
@ -419,7 +430,7 @@ IDBTransaction::IndexDataInsertStatement(bool aAutoIncrement,
|
|||
);
|
||||
}
|
||||
return GetCachedStatement(
|
||||
"INSERT INTO index_data ("
|
||||
"INSERT OR IGNORE INTO index_data ("
|
||||
"index_id, object_data_id, object_data_key, value) "
|
||||
"VALUES (:index_id, :object_data_id, :object_data_key, :value)"
|
||||
);
|
||||
|
|
|
@ -103,6 +103,8 @@ public:
|
|||
void OnNewRequest();
|
||||
void OnRequestFinished();
|
||||
|
||||
void ReleaseCachedObjectStore(const nsAString& aName);
|
||||
|
||||
void SetTransactionListener(IDBTransactionListener* aListener);
|
||||
|
||||
bool StartSavepoint();
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#define DB_SCHEMA_VERSION 6
|
||||
#define DB_SCHEMA_VERSION 8
|
||||
|
||||
#define BEGIN_INDEXEDDB_NAMESPACE \
|
||||
namespace mozilla { namespace dom { namespace indexedDB {
|
||||
|
|
|
@ -122,9 +122,9 @@ CreateTables(mozIStorageConnection* aDBConn)
|
|||
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE object_store ("
|
||||
"id INTEGER PRIMARY KEY, "
|
||||
"name TEXT NOT NULL, "
|
||||
"key_path TEXT NOT NULL, "
|
||||
"auto_increment INTEGER NOT NULL DEFAULT 0, "
|
||||
"name TEXT NOT NULL, "
|
||||
"key_path TEXT, "
|
||||
"UNIQUE (name)"
|
||||
");"
|
||||
));
|
||||
|
@ -165,6 +165,7 @@ CreateTables(mozIStorageConnection* aDBConn)
|
|||
"name TEXT NOT NULL, "
|
||||
"key_path TEXT NOT NULL, "
|
||||
"unique_index INTEGER NOT NULL, "
|
||||
"multientry INTEGER NOT NULL DEFAULT 0, "
|
||||
"object_store_autoincrement INTERGER NOT NULL, "
|
||||
"PRIMARY KEY (id), "
|
||||
"UNIQUE (object_store_id, name), "
|
||||
|
@ -753,6 +754,149 @@ UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
|
||||
{
|
||||
mozStorageTransaction transaction(aConnection, false,
|
||||
mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
|
||||
// Turn off foreign key constraints before we do anything here.
|
||||
nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"PRAGMA foreign_keys = OFF;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TEMPORARY TABLE temp_upgrade ("
|
||||
"id, "
|
||||
"name, "
|
||||
"key_path, "
|
||||
"auto_increment, "
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO temp_upgrade "
|
||||
"SELECT id, name, key_path, auto_increment "
|
||||
"FROM object_store;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP TABLE object_store;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE object_store ("
|
||||
"id INTEGER PRIMARY KEY, "
|
||||
"auto_increment INTEGER NOT NULL DEFAULT 0, "
|
||||
"name TEXT NOT NULL, "
|
||||
"key_path TEXT, "
|
||||
"UNIQUE (name)"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO object_store "
|
||||
"SELECT id, auto_increment, name, nullif(key_path, '') "
|
||||
"FROM temp_upgrade;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP TABLE temp_upgrade;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->SetSchemaVersion(7);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = transaction.Commit();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
|
||||
{
|
||||
mozStorageTransaction transaction(aConnection, false,
|
||||
mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
|
||||
// Turn off foreign key constraints before we do anything here.
|
||||
nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"PRAGMA foreign_keys = OFF;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TEMPORARY TABLE temp_upgrade ("
|
||||
"id, "
|
||||
"object_store_id, "
|
||||
"name, "
|
||||
"key_path, "
|
||||
"unique_index, "
|
||||
"object_store_autoincrement, "
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO temp_upgrade "
|
||||
"SELECT id, object_store_id, name, key_path, "
|
||||
"unique_index, object_store_autoincrement, "
|
||||
"FROM object_store_index;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP TABLE object_store_index;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE TABLE object_store_index ("
|
||||
"id INTEGER, "
|
||||
"object_store_id INTEGER NOT NULL, "
|
||||
"name TEXT NOT NULL, "
|
||||
"key_path TEXT NOT NULL, "
|
||||
"unique_index INTEGER NOT NULL, "
|
||||
"multientry INTEGER NOT NULL, "
|
||||
"object_store_autoincrement INTERGER NOT NULL, "
|
||||
"PRIMARY KEY (id), "
|
||||
"UNIQUE (object_store_id, name), "
|
||||
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
|
||||
"CASCADE"
|
||||
");"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO object_store_index "
|
||||
"SELECT id, object_store_id, name, key_path, "
|
||||
"unique_index, 0, object_store_autoincrement, "
|
||||
"FROM temp_upgrade;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP TABLE temp_upgrade;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aConnection->SetSchemaVersion(8);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = transaction.Commit();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateDatabaseConnection(const nsAString& aName,
|
||||
nsIFile* aDBFile,
|
||||
|
@ -804,7 +948,7 @@ CreateDatabaseConnection(const nsAString& aName,
|
|||
}
|
||||
else if (schemaVersion != DB_SCHEMA_VERSION) {
|
||||
// This logic needs to change next time we change the schema!
|
||||
PR_STATIC_ASSERT(DB_SCHEMA_VERSION == 6);
|
||||
PR_STATIC_ASSERT(DB_SCHEMA_VERSION == 8);
|
||||
|
||||
#define UPGRADE_SCHEMA_CASE(_from, _to) \
|
||||
if (schemaVersion == _from) { \
|
||||
|
@ -819,6 +963,8 @@ CreateDatabaseConnection(const nsAString& aName,
|
|||
|
||||
UPGRADE_SCHEMA_CASE(4, 5)
|
||||
UPGRADE_SCHEMA_CASE(5, 6)
|
||||
UPGRADE_SCHEMA_CASE(6, 7)
|
||||
UPGRADE_SCHEMA_CASE(7, 8)
|
||||
|
||||
#undef UPGRADE_SCHEMA_CASE
|
||||
|
||||
|
|
|
@ -60,8 +60,8 @@ public:
|
|||
bool aForDeletion)
|
||||
: HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
|
||||
mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
|
||||
mForDeletion(aForDeletion), mCurrentVersion(0),
|
||||
mDataVersion(DB_SCHEMA_VERSION), mDatabaseId(0), mLastObjectStoreId(0),
|
||||
mForDeletion(aForDeletion), mDatabaseId(nsnull), mCurrentVersion(0),
|
||||
mDataVersion(DB_SCHEMA_VERSION), mLastObjectStoreId(0),
|
||||
mLastIndexId(0), mState(eCreated), mResultCode(NS_OK)
|
||||
{
|
||||
NS_ASSERTION(!aForDeletion || !aRequestedVersion,
|
||||
|
|
|
@ -47,7 +47,7 @@ interface nsIIDBRequest;
|
|||
* http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBIndex for more
|
||||
* information.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(1da60889-3db4-4f66-9fd7-b78c1e7969b7)]
|
||||
[scriptable, builtinclass, uuid(fcb9a158-833e-4aa9-ab19-ab90cbb50afc)]
|
||||
interface nsIIDBIndex : nsISupports
|
||||
{
|
||||
readonly attribute DOMString name;
|
||||
|
@ -58,6 +58,8 @@ interface nsIIDBIndex : nsISupports
|
|||
|
||||
readonly attribute boolean unique;
|
||||
|
||||
readonly attribute boolean multiEntry;
|
||||
|
||||
readonly attribute nsIIDBObjectStore objectStore;
|
||||
|
||||
[implicit_jscontext]
|
||||
|
|
|
@ -84,6 +84,7 @@ TEST_FILES = \
|
|||
test_indexes_bad_values.html \
|
||||
test_key_requirements.html \
|
||||
test_leaving_page.html \
|
||||
test_multientry.html \
|
||||
test_objectCursors.html \
|
||||
test_objectStore_inline_autoincrement_key_added_on_put.html \
|
||||
test_objectStore_remove_values.html \
|
||||
|
|
|
@ -18,151 +18,195 @@
|
|||
// Test object stores
|
||||
|
||||
const name = window.location.pathname;
|
||||
const objectStoreInfo = [
|
||||
{ name: "a", options: { keyPath: "id"} },
|
||||
{ name: "b", options: { keyPath: "foo.id"} },
|
||||
{ name: "c", options: { keyPath: ""} },
|
||||
{ name: "d", options: { keyPath: "foo..id"}, exception: true },
|
||||
{ name: "e", options: { keyPath: "foo."}, exception: true },
|
||||
{ name: "f", options: { keyPath: "foo.bar" } },
|
||||
{ name: "g", options: { keyPath: "fo o" }, exception: true},
|
||||
{ name: "h", options: { keyPath: "foo " }, exception: true},
|
||||
{ name: "i", options: { keyPath: "foo[bar]" }, exception: true },
|
||||
{ name: "j", options: { keyPath: "$('id').stuff" }, exception: true },
|
||||
{ name: "k", options: { keyPath: "foo.2.bar" }, exception: true }
|
||||
const keyPaths = [
|
||||
{ keyPath: "id", value: { id: 5 }, key: 5 },
|
||||
{ keyPath: "id", value: { id: "14", iid: 12 }, key: "14" },
|
||||
{ keyPath: "id", value: { iid: "14", id: 12 }, key: 12 },
|
||||
{ keyPath: "id", value: {} },
|
||||
{ keyPath: "id", value: { id: {} } },
|
||||
{ keyPath: "id", value: { id: /x/ } },
|
||||
{ keyPath: "id", value: 2 },
|
||||
{ keyPath: "id", value: undefined },
|
||||
{ keyPath: "foo.id", value: { foo: { id: 7 } }, key: 7 },
|
||||
{ keyPath: "foo.id", value: { id: 7, foo: { id: "asdf" } }, key: "asdf" },
|
||||
{ keyPath: "foo.id", value: { foo: { id: undefined } } },
|
||||
{ keyPath: "foo.id", value: { foo: 47 } },
|
||||
{ keyPath: "foo.id", value: {} },
|
||||
{ keyPath: "", value: "foopy", key: "foopy" },
|
||||
{ keyPath: "", value: 2, key: 2 },
|
||||
{ keyPath: "", value: undefined },
|
||||
{ keyPath: "", value: { id: 12 } },
|
||||
{ keyPath: "", value: /x/ },
|
||||
{ keyPath: "foo.bar", value: { baz: 1, foo: { baz2: 2, bar: "xo" } }, key: "xo" },
|
||||
{ keyPath: "foo.bar.baz", value: { foo: { bar: { bazz: 16, baz: 17 } } }, key: 17 },
|
||||
{ keyPath: "foo..id", exception: true },
|
||||
{ keyPath: "foo.", exception: true },
|
||||
{ keyPath: "fo o", exception: true },
|
||||
{ keyPath: "foo ", exception: true },
|
||||
{ keyPath: "foo[bar]",exception: true },
|
||||
{ keyPath: "$('id').stuff", exception: true },
|
||||
{ keyPath: "foo.2.bar", exception: true },
|
||||
{ keyPath: "foo. .bar", exception: true },
|
||||
{ keyPath: ".bar", exception: true },
|
||||
];
|
||||
|
||||
const indexInfo = [
|
||||
{ name: "1", keyPath: "id" },
|
||||
{ name: "2", keyPath: "foo..id", exception: true },
|
||||
{ name: "3", keyPath: "foo.", exception: true },
|
||||
{ name: "4", keyPath: "foo.baz" },
|
||||
{ name: "5", keyPath: "fo o", exception: true },
|
||||
{ name: "6", keyPath: "foo ", exception: true },
|
||||
{ name: "7", keyPath: "foo[bar]", exception: true },
|
||||
{ name: "8", keyPath: "$('id').stuff", exception: true },
|
||||
{ name: "9", keyPath: "foo.2.bar", exception: true },
|
||||
{ name: "10", keyPath: "foo.bork" },
|
||||
];
|
||||
|
||||
let request = mozIndexedDB.open(name, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
let openRequest = mozIndexedDB.open(name, 1);
|
||||
openRequest.onerror = errorHandler;
|
||||
openRequest.onupgradeneeded = grabEventAndContinueHandler;
|
||||
openRequest.onsuccess = unexpectedSuccessHandler;
|
||||
let event = yield;
|
||||
let db = event.target.result;
|
||||
|
||||
for (let i = 0; i < objectStoreInfo.length; i++) {
|
||||
let info = objectStoreInfo[i];
|
||||
try {
|
||||
let objectStore = info.hasOwnProperty("options") ?
|
||||
db.createObjectStore(info.name, info.options) :
|
||||
db.createObjectStore(info.name);
|
||||
ok(!info.hasOwnProperty("exception"), "expected exception behavior observed");
|
||||
} catch (e) {
|
||||
ok(info.hasOwnProperty("exception"), "expected exception behavior observed");
|
||||
ok(e instanceof DOMException, "Got a DOM Exception");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "expect a syntax error");
|
||||
let stores = {};
|
||||
|
||||
// Test creating object stores and inserting data
|
||||
for (let i = 0; i < keyPaths.length; i++) {
|
||||
let info = keyPaths[i];
|
||||
let test = " for objectStore test " + JSON.stringify(info);
|
||||
if (!stores[info.keyPath]) {
|
||||
try {
|
||||
let objectStore = db.createObjectStore(info.keyPath, { keyPath: info.keyPath });
|
||||
ok(!("exception" in info), "shouldn't throw" + test);
|
||||
is(objectStore.keyPath, info.keyPath, "correct keyPath property" + test);
|
||||
stores[info.keyPath] = objectStore;
|
||||
} catch (e) {
|
||||
ok("exception" in info, "should throw" + test);
|
||||
ok(e instanceof DOMException, "Got a DOM Exception" + test);
|
||||
is(e.code, DOMException.SYNTAX_ERR, "expect a syntax error" + test);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let store = stores[info.keyPath];
|
||||
|
||||
try {
|
||||
request = store.add(info.value);
|
||||
ok("key" in info, "successfully created request to insert value" + test);
|
||||
} catch (e) {
|
||||
ok(!("key" in info), "threw when attempted to insert" + test);
|
||||
ok(e instanceof IDBDatabaseException, "Got a IDBDatabaseException" + test);
|
||||
is(e.code, IDBDatabaseException.DATA_ERR, "expect a DATA_ERR error" + test);
|
||||
continue;
|
||||
}
|
||||
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
let e = yield;
|
||||
is(e.type, "success", "inserted successfully" + test);
|
||||
is(e.target, request, "expected target" + test);
|
||||
is(request.result, info.key, "found correct key" + test);
|
||||
|
||||
store.clear().onsuccess = grabEventAndContinueHandler;
|
||||
yield;
|
||||
}
|
||||
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
yield;
|
||||
// Attempt to create indexes and insert data
|
||||
let store = db.createObjectStore("indexStore");
|
||||
let indexes = {};
|
||||
for (let i = 0; i < keyPaths.length; i++) {
|
||||
let test = " for index test " + JSON.stringify(info);
|
||||
let info = keyPaths[i];
|
||||
if (!indexes[info.keyPath]) {
|
||||
try {
|
||||
let index = store.createIndex(info.keyPath, info.keyPath);
|
||||
ok(!("exception" in info), "shouldn't throw" + test);
|
||||
is(index.keyPath, info.keyPath, "index has correct keyPath property" + test);
|
||||
indexes[info.keyPath] = index;
|
||||
} catch (e) {
|
||||
ok("exception" in info, "should throw" + test);
|
||||
ok(e instanceof DOMException, "Got a DOM Exception" + test);
|
||||
is(e.code, DOMException.SYNTAX_ERR, "expect a syntax error" + test);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let index = indexes[info.keyPath];
|
||||
|
||||
let trans = db.transaction(["f"], IDBTransaction.READ_WRITE);
|
||||
let objectStore = trans.objectStore("f");
|
||||
request = store.add(info.value, 1);
|
||||
if ("key" in info) {
|
||||
index.getKey(info.key).onsuccess = grabEventAndContinueHandler;
|
||||
e = yield;
|
||||
is(e.target.result, 1, "found value when reading" + test);
|
||||
}
|
||||
else {
|
||||
index.count().onsuccess = grabEventAndContinueHandler;
|
||||
e = yield;
|
||||
is(e.target.result, 0, "should be empty" + test);
|
||||
}
|
||||
|
||||
objectStore.put({foo: {baz: -1, bar: 72, bork: true}});
|
||||
objectStore.put({foo: {baz: 2, bar: 1, bork: false}});
|
||||
store.clear().onsuccess = grabEventAndContinueHandler;
|
||||
yield;
|
||||
}
|
||||
|
||||
// Autoincrement and complex key paths
|
||||
let aitests = [{ v: {}, k: 1, res: { foo: { id: 1 }} },
|
||||
{ v: { value: "x" }, k: 2, res: { value: "x", foo: { id: 2 }} },
|
||||
{ v: { value: "x", foo: {} }, k: 3, res: { value: "x", foo: { id: 3 }} },
|
||||
{ v: { v: "x", foo: { x: "y" } }, k: 4, res: { v: "x", foo: { x: "y", id: 4 }} },
|
||||
{ v: { value: 2, foo: { id: 10 }}, k: 10 },
|
||||
{ v: { value: 2 }, k: 11, res: { value: 2, foo: { id: 11 }} },
|
||||
{ v: true, },
|
||||
{ v: { value: 2, foo: 12 }, },
|
||||
{ v: { foo: { id: true }}, },
|
||||
{ v: { foo: { x: 5, id: {} }}, },
|
||||
{ v: undefined, },
|
||||
{ v: { foo: undefined }, },
|
||||
{ v: { foo: { id: undefined }}, },
|
||||
{ v: null, },
|
||||
{ v: { foo: null }, },
|
||||
{ v: { foo: { id: null }}, },
|
||||
];
|
||||
|
||||
store = db.createObjectStore("gen", { keyPath: "foo.id", autoIncrement: true });
|
||||
for (let i = 0; i < aitests.length; ++i) {
|
||||
let info = aitests[i];
|
||||
let test = " for autoIncrement test " + JSON.stringify(info);
|
||||
|
||||
let preValue = JSON.stringify(info.v);
|
||||
if ("k" in info) {
|
||||
store.add(info.v).onsuccess = grabEventAndContinueHandler;
|
||||
is(JSON.stringify(info.v), preValue, "put didn't modify value" + test);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
store.add(info.v);
|
||||
ok(false, "should throw" + test);
|
||||
}
|
||||
catch(e) {
|
||||
ok(true, "did throw" + test);
|
||||
ok(e instanceof IDBDatabaseException, "Got a IDBDatabaseException" + test);
|
||||
is(e.code, IDBDatabaseException.DATA_ERR, "expect a DATA_ERR" + test);
|
||||
|
||||
is(JSON.stringify(info.v), preValue, "failing put didn't modify value" + test);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let e = yield;
|
||||
is(e.target.result, info.k, "got correct return key" + test);
|
||||
|
||||
store.get(info.k).onsuccess = grabEventAndContinueHandler;
|
||||
e = yield;
|
||||
is(JSON.stringify(e.target.result), JSON.stringify(info.res || info.v),
|
||||
"expected value stored" + test);
|
||||
}
|
||||
|
||||
// Can't handle autoincrement and empty keypath
|
||||
try {
|
||||
objectStore.put({foo: {}});
|
||||
ok(false, "Should have thrown!");
|
||||
} catch (e) {
|
||||
ok(true, "Putting an object without the key should throw");
|
||||
store = db.createObjectStore("storefail", { keyPath: "", autoIncrement: true });
|
||||
ok(false, "Should have thrown when creating empty-keypath autoincrement store");
|
||||
}
|
||||
catch(e) {
|
||||
ok(true, "Did throw when creating empty-keypath autoincrement store");
|
||||
ok(e instanceof DOMException, "Got a DOMException when creating empty-keypath autoincrement store");
|
||||
is(e.code, DOMException.INVALID_ACCESS_ERR, "expect a INVALID_ACCESS_ERR when creating empty-keypath autoincrement store");
|
||||
}
|
||||
|
||||
trans.onerror = errorHandler;
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
|
||||
openRequest.onsuccess = grabEventAndContinueHandler;
|
||||
yield;
|
||||
|
||||
let trans = db.transaction(["f"], IDBTransaction.READ);
|
||||
let objectStore = trans.objectStore("f");
|
||||
let request = objectStore.openCursor();
|
||||
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
let event = yield;
|
||||
|
||||
let cursor = event.target.result;
|
||||
is(cursor.value.foo.baz, 2, "got things in the right order");
|
||||
|
||||
cursor.continue();
|
||||
|
||||
let event = yield
|
||||
|
||||
let cursor = event.target.result;
|
||||
is(cursor.value.foo.baz, -1, "got things in the right order");
|
||||
|
||||
db.close();
|
||||
|
||||
// Test indexes
|
||||
|
||||
let request = mozIndexedDB.open(name, 2);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
|
||||
let event = yield;
|
||||
let db = event.target.result;
|
||||
|
||||
let trans = event.target.transaction;
|
||||
let objectStore = trans.objectStore("f");
|
||||
|
||||
let indexes = [];
|
||||
for (let i = 0; i < indexInfo.length; i++) {
|
||||
let info = indexInfo[i];
|
||||
try {
|
||||
indexes[i] = info.hasOwnProperty("options") ?
|
||||
objectStore.createIndex(info.name, info.keyPath, info.options) :
|
||||
objectStore.createIndex(info.name, info.keyPath);
|
||||
ok(!info.hasOwnProperty("exception"), "expected exception behavior observed");
|
||||
} catch (e) {
|
||||
ok(info.hasOwnProperty("exception"), "expected exception behavior observed");
|
||||
ok(e instanceof DOMException, "Got a DOM Exception");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "expect a syntax error");
|
||||
}
|
||||
}
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
yield;
|
||||
|
||||
let trans = db.transaction(["f"], IDBTransaction.READ);
|
||||
let objectStore = trans.objectStore("f");
|
||||
|
||||
let request = objectStore.index("4").openCursor();
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
let event = yield;
|
||||
|
||||
let cursor = event.target.result;
|
||||
|
||||
is(cursor.value.foo.bar, 72, "got things in the right order");
|
||||
|
||||
cursor.continue();
|
||||
yield;
|
||||
|
||||
is(cursor.value.foo.bar, 1, "got things in the right order");
|
||||
|
||||
let request = objectStore.index("10").openCursor();
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
let event = yield;
|
||||
|
||||
is(event.target.result, null, "should have no results");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
|
||||
is(objectStore.name, name, "Bad name");
|
||||
is(objectStore.keyPath, info.options && info.options.keyPath ?
|
||||
info.options.keyPath : "",
|
||||
info.options.keyPath : null,
|
||||
"Bad keyPath");
|
||||
if(objectStore.indexNames.length, 0, "Bad indexNames");
|
||||
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
// Test object stores
|
||||
|
||||
let openRequest = mozIndexedDB.open(window.location.pathname, 1);
|
||||
openRequest.onerror = errorHandler;
|
||||
openRequest.onupgradeneeded = grabEventAndContinueHandler;
|
||||
openRequest.onsuccess = unexpectedSuccessHandler;
|
||||
let event = yield;
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
let tests =
|
||||
[{ add: { x: 1, id: 1 },
|
||||
indexes:[{ v: 1, k: 1 }] },
|
||||
{ add: { x: [2, 3], id: 2 },
|
||||
indexes:[{ v: 1, k: 1 },
|
||||
{ v: 2, k: 2 },
|
||||
{ v: 3, k: 2 }] },
|
||||
{ put: { x: [2, 4], id: 1 },
|
||||
indexes:[{ v: 2, k: 1 },
|
||||
{ v: 2, k: 2 },
|
||||
{ v: 3, k: 2 },
|
||||
{ v: 4, k: 1 }] },
|
||||
{ add: { x: [5, 6, 5, -2, 3], id: 3 },
|
||||
indexes:[{ v:-2, k: 3 },
|
||||
{ v: 2, k: 1 },
|
||||
{ v: 2, k: 2 },
|
||||
{ v: 3, k: 2 },
|
||||
{ v: 3, k: 3 },
|
||||
{ v: 4, k: 1 },
|
||||
{ v: 5, k: 3 },
|
||||
{ v: 6, k: 3 }] },
|
||||
{ delete: IDBKeyRange.bound(1, 3),
|
||||
indexes:[] },
|
||||
{ put: { x: ["food", {}, false, undefined, /x/, [73, false]], id: 2 },
|
||||
indexes:[{ v: "food", k: 2 }] },
|
||||
{ add: { x: [{}, /x/, -12, "food", null, [false], undefined], id: 3 },
|
||||
indexes:[{ v: -12, k: 3 },
|
||||
{ v: "food", k: 2 },
|
||||
{ v: "food", k: 3 }] },
|
||||
{ put: { x: [], id: 2 },
|
||||
indexes:[{ v: -12, k: 3 },
|
||||
{ v: "food", k: 3 }] },
|
||||
{ put: { x: { y: 3 }, id: 3 },
|
||||
indexes:[] },
|
||||
{ add: { x: false, id: 7 },
|
||||
indexes:[] },
|
||||
{ delete: IDBKeyRange.lowerBound(0),
|
||||
indexes:[] },
|
||||
];
|
||||
|
||||
let store = db.createObjectStore("mystore", { keyPath: "id" });
|
||||
let index = store.createIndex("myindex", "x", { multiEntry: true });
|
||||
is(index.multiEntry, true, "index created with multiEntry");
|
||||
|
||||
let i;
|
||||
for (i = 0; i < tests.length; ++i) {
|
||||
let test = tests[i];
|
||||
let testName = " for " + JSON.stringify(test);
|
||||
let req;
|
||||
if (test.add) {
|
||||
req = store.add(test.add);
|
||||
}
|
||||
else if (test.put) {
|
||||
req = store.put(test.put);
|
||||
}
|
||||
else if (test.delete) {
|
||||
req = store.delete(test.delete);
|
||||
}
|
||||
else {
|
||||
ok(false, "borked test");
|
||||
}
|
||||
req.onsuccess = grabEventAndContinueHandler;
|
||||
let e = yield;
|
||||
|
||||
req = index.openKeyCursor();
|
||||
req.onsuccess = grabEventAndContinueHandler;
|
||||
for (let j = 0; j < test.indexes.length; ++j) {
|
||||
e = yield;
|
||||
is(req.result.key, test.indexes[j].v, "found expected index key at index " + j + testName);
|
||||
is(req.result.primaryKey, test.indexes[j].k, "found expected index primary key at index " + j + testName);
|
||||
req.result.continue();
|
||||
}
|
||||
e = yield;
|
||||
is(req.result, undefined, "exhausted indexes");
|
||||
|
||||
let tempIndex = store.createIndex("temp index", "x", { multiEntry: true });
|
||||
req = tempIndex.openKeyCursor();
|
||||
req.onsuccess = grabEventAndContinueHandler;
|
||||
for (let j = 0; j < test.indexes.length; ++j) {
|
||||
e = yield;
|
||||
is(req.result.key, test.indexes[j].v, "found expected temp index key at index " + j + testName);
|
||||
is(req.result.primaryKey, test.indexes[j].k, "found expected temp index primary key at index " + j + testName);
|
||||
req.result.continue();
|
||||
}
|
||||
e = yield;
|
||||
is(req.result, undefined, "exhausted temp index");
|
||||
store.deleteIndex("temp index");
|
||||
}
|
||||
|
||||
// Unique indexes
|
||||
tests =
|
||||
[{ add: { x: 1, id: 1 },
|
||||
indexes:[{ v: 1, k: 1 }] },
|
||||
{ add: { x: [2, 3], id: 2 },
|
||||
indexes:[{ v: 1, k: 1 },
|
||||
{ v: 2, k: 2 },
|
||||
{ v: 3, k: 2 }] },
|
||||
{ put: { x: [2, 4], id: 3 },
|
||||
fail: true },
|
||||
{ put: { x: [1, 4], id: 1 },
|
||||
indexes:[{ v: 1, k: 1 },
|
||||
{ v: 2, k: 2 },
|
||||
{ v: 3, k: 2 },
|
||||
{ v: 4, k: 1 }] },
|
||||
{ add: { x: [5, 0, 5, 5, 5], id: 3 },
|
||||
indexes:[{ v: 0, k: 3 },
|
||||
{ v: 1, k: 1 },
|
||||
{ v: 2, k: 2 },
|
||||
{ v: 3, k: 2 },
|
||||
{ v: 4, k: 1 },
|
||||
{ v: 5, k: 3 }] },
|
||||
{ delete: IDBKeyRange.bound(1, 2),
|
||||
indexes:[{ v: 0, k: 3 },
|
||||
{ v: 5, k: 3 }] },
|
||||
{ add: { x: [0, 6], id: 8 },
|
||||
fail: true },
|
||||
{ add: { x: 5, id: 8 },
|
||||
fail: true },
|
||||
{ put: { x: 0, id: 8 },
|
||||
fail: true },
|
||||
];
|
||||
|
||||
store.deleteIndex("myindex");
|
||||
index = store.createIndex("myindex", "x", { multiEntry: true, unique: true });
|
||||
is(index.multiEntry, true, "index created with multiEntry");
|
||||
|
||||
let i;
|
||||
let indexes;
|
||||
for (i = 0; i < tests.length; ++i) {
|
||||
let test = tests[i];
|
||||
let testName = " for " + JSON.stringify(test);
|
||||
let req;
|
||||
if (test.add) {
|
||||
req = store.add(test.add);
|
||||
}
|
||||
else if (test.put) {
|
||||
req = store.put(test.put);
|
||||
}
|
||||
else if (test.delete) {
|
||||
req = store.delete(test.delete);
|
||||
}
|
||||
else {
|
||||
ok(false, "borked test");
|
||||
}
|
||||
|
||||
if (!test.fail) {
|
||||
req.onsuccess = grabEventAndContinueHandler;
|
||||
let e = yield;
|
||||
indexes = test.indexes;
|
||||
}
|
||||
else {
|
||||
req.onsuccess = unexpectedSuccessHandler;
|
||||
req.onerror = grabEventAndContinueHandler;
|
||||
ok(true, "waiting for error");
|
||||
let e = yield;
|
||||
ok(true, "got error: " + e.type);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
let e;
|
||||
req = index.openKeyCursor();
|
||||
req.onsuccess = grabEventAndContinueHandler;
|
||||
for (let j = 0; j < indexes.length; ++j) {
|
||||
e = yield;
|
||||
is(req.result.key, indexes[j].v, "found expected index key at index " + j + testName);
|
||||
is(req.result.primaryKey, indexes[j].k, "found expected index primary key at index " + j + testName);
|
||||
req.result.continue();
|
||||
}
|
||||
e = yield;
|
||||
is(req.result, undefined, "exhausted indexes");
|
||||
|
||||
let tempIndex = store.createIndex("temp index", "x", { multiEntry: true, unique: true });
|
||||
req = tempIndex.openKeyCursor();
|
||||
req.onsuccess = grabEventAndContinueHandler;
|
||||
for (let j = 0; j < indexes.length; ++j) {
|
||||
e = yield;
|
||||
is(req.result.key, indexes[j].v, "found expected temp index key at index " + j + testName);
|
||||
is(req.result.primaryKey, indexes[j].k, "found expected temp index primary key at index " + j + testName);
|
||||
req.result.continue();
|
||||
}
|
||||
e = yield;
|
||||
is(req.result, undefined, "exhausted temp index");
|
||||
store.deleteIndex("temp index");
|
||||
}
|
||||
|
||||
|
||||
openRequest.onsuccess = grabEventAndContinueHandler;
|
||||
yield;
|
||||
|
||||
let trans = db.transaction(["mystore"], IDBTransaction.READ_WRITE);
|
||||
store = trans.objectStore("mystore");
|
||||
index = store.index("myindex");
|
||||
is(index.multiEntry, true, "index still is multiEntry");
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
yield;
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -32,14 +32,30 @@
|
|||
|
||||
is(objectStore.indexNames.length, 0, "Correct indexNames list");
|
||||
|
||||
objectStore.createIndex(indexName, "foo");
|
||||
let index = objectStore.createIndex(indexName, "foo");
|
||||
|
||||
is(objectStore.indexNames.length, 1, "Correct indexNames list");
|
||||
is(objectStore.indexNames.item(0), indexName, "Correct name");
|
||||
is(objectStore.index(indexName), index, "Correct instance");
|
||||
|
||||
objectStore.deleteIndex(indexName);
|
||||
|
||||
is(objectStore.indexNames.length, 0, "Correct indexNames list");
|
||||
try {
|
||||
objectStore.index(indexName);
|
||||
ok(false, "should have thrown");
|
||||
}
|
||||
catch(ex) {
|
||||
ok(ex instanceof IDBDatabaseException, "Got a IDBDatabaseException");
|
||||
is(ex.code, IDBDatabaseException.NOT_FOUND_ERR, "expect a NOT_FOUND_ERR");
|
||||
}
|
||||
|
||||
let index2 = objectStore.createIndex(indexName, "foo");
|
||||
isnot(index, index2, "New instance should be created");
|
||||
|
||||
is(objectStore.indexNames.length, 1, "Correct recreacted indexNames list");
|
||||
is(objectStore.indexNames.item(0), indexName, "Correct recreacted name");
|
||||
is(objectStore.index(indexName), index2, "Correct instance");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче