Merge mozilla-central and mozilla-inbound

This commit is contained in:
Marco Bonardo 2011-11-03 15:56:28 +01:00
commit c21fb656cf
203 changed files with 6119 additions and 2102 deletions

41
.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
# .gitignore - List of filenames git should ignore
# Filenames that should be ignored wherever they appear
*~
*.pyc
*.pyo
TAGS
tags
ID
.DS_Store*
# Vim swap files.
.*.sw[a-z]
# User files that may appear at the root
.mozconfig
mozconfig
configure
config.cache
config.log
# Empty marker file that's generated when we check out NSS
security/manager/.nss.checkout
# Build directories
obj/*
# Build directories for js shell
*/_DBG.OBJ/
*/_OPT.OBJ/
# SpiderMonkey configury
js/src/configure
js/src/autom4te.cache
# SpiderMonkey test result logs
js/src/tests/results-*.html
js/src/tests/results-*.txt
# Java HTML5 parser classes
parser/html/java/htmlparser/
parser/html/java/javaparser/

View File

@ -59,7 +59,7 @@ interface nsIDOMNode;
* if (NS_SUCCEEDED(rv))
* rv = observerService->AddObserver(this, "accessible-event", PR_TRUE);
*/
[scriptable, uuid(fd1378c5-c606-4a5e-a321-8e7fc107e5cf)]
[scriptable, uuid(7f66a33a-9ed7-4fd4-87a8-e431b0f43368)]
interface nsIAccessibleEvent : nsISupports
{
/**
@ -280,7 +280,12 @@ interface nsIAccessibleEvent : nsISupports
const unsigned long EVENT_DOCUMENT_CONTENT_CHANGED = 0x002B;
const unsigned long EVENT_PROPERTY_CHANGED = 0x002C;
const unsigned long EVENT_SELECTION_CHANGED = 0x002D;
/**
* A slide changed in a presentation document or a page boundary was
* crossed in a word processing document.
*/
const unsigned long EVENT_PAGE_CHANGED = 0x002D;
/**
* A text object's attributes changed.
@ -436,16 +441,10 @@ interface nsIAccessibleEvent : nsISupports
*/
const unsigned long EVENT_OBJECT_ATTRIBUTE_CHANGED = 0x0055;
/**
* A slide changed in a presentation document or a page boundary was
* crossed in a word processing document.
*/
const unsigned long EVENT_PAGE_CHANGED = 0x0056;
/**
* Help make sure event map does not get out-of-line.
*/
const unsigned long EVENT_LAST_ENTRY = 0x0057;
const unsigned long EVENT_LAST_ENTRY = 0x0056;
/**
* The type of event, based on the enumerated event values

View File

@ -1089,10 +1089,24 @@ nsAccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
}
} break;
case nsIAccessibleEvent::EVENT_SELECTION_CHANGED:
MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
g_signal_emit_by_name(atkObj, "selection_changed");
break;
case nsIAccessibleEvent::EVENT_SELECTION:
case nsIAccessibleEvent::EVENT_SELECTION_ADD:
case nsIAccessibleEvent::EVENT_SELECTION_REMOVE:
{
// XXX: dupe events may be fired
MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent);
g_signal_emit_by_name(nsAccessibleWrap::GetAtkObject(selChangeEvent->Widget()),
"selection_changed");
break;
}
case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
{
MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
g_signal_emit_by_name(atkObj, "selection_changed");
break;
}
case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_SELECTION_CHANGED\n"));

View File

@ -330,6 +330,28 @@ AccCaretMoveEvent::CreateXPCOMObject()
}
////////////////////////////////////////////////////////////////////////////////
// AccSelChangeEvent
////////////////////////////////////////////////////////////////////////////////
AccSelChangeEvent::
AccSelChangeEvent(nsAccessible* aWidget, nsAccessible* aItem,
SelChangeType aSelChangeType) :
AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange),
mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
mPreceedingCount(0), mPackedEvent(nsnull)
{
if (aSelChangeType == eSelectionAdd) {
if (mWidget->GetSelectedItem(1))
mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
else
mEventType = nsIAccessibleEvent::EVENT_SELECTION;
} else {
mEventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
}
}
////////////////////////////////////////////////////////////////////////////////
// AccTableChangeEvent
////////////////////////////////////////////////////////////////////////////////

View File

@ -82,6 +82,9 @@ public:
// will be processed.
eCoalesceOfSameType,
// eCoalesceSelectionChange: coalescence of selection change events.
eCoalesceSelectionChange,
// eRemoveDupes : For repeat events, only the newest event in queue
// will be emitted.
eRemoveDupes,
@ -125,6 +128,7 @@ public:
eHideEvent,
eShowEvent,
eCaretMoveEvent,
eSelectionChangeEvent,
eTableChangeEvent
};
@ -327,10 +331,37 @@ private:
/**
* Accessible widget selection change event.
*/
class AccSelectionChangeEvent : public AccEvent
class AccSelChangeEvent : public AccEvent
{
public:
enum SelChangeType {
eSelectionAdd,
eSelectionRemove
};
AccSelChangeEvent(nsAccessible* aWidget, nsAccessible* aItem,
SelChangeType aSelChangeType);
virtual ~AccSelChangeEvent() { }
// AccEvent
static const EventGroup kEventGroup = eSelectionChangeEvent;
virtual unsigned int GetEventGroups() const
{
return AccEvent::GetEventGroups() | (1U << eSelectionChangeEvent);
}
// AccSelChangeEvent
nsAccessible* Widget() const { return mWidget; }
private:
nsRefPtr<nsAccessible> mWidget;
nsRefPtr<nsAccessible> mItem;
SelChangeType mSelChangeType;
PRUint32 mPreceedingCount;
AccSelChangeEvent* mPackedEvent;
friend class NotificationController;
};

View File

@ -51,6 +51,10 @@
using namespace mozilla::a11y;
// Defines the number of selection add/remove events in the queue when they
// aren't packed into single selection within event.
const unsigned int kSelChangeCountToPack = 5;
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector
////////////////////////////////////////////////////////////////////////////////
@ -491,6 +495,26 @@ NotificationController::CoalesceEvents()
}
} break; // case eRemoveDupes
case AccEvent::eCoalesceSelectionChange:
{
AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
PRInt32 index = tail - 1;
for (; index >= 0; index--) {
AccEvent* thisEvent = mEvents[index];
if (thisEvent->mEventRule == tailEvent->mEventRule) {
AccSelChangeEvent* thisSelChangeEvent =
downcast_accEvent(thisEvent);
// Coalesce selection change events within same control.
if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
return;
}
}
}
} break; // eCoalesceSelectionChange
default:
break; // case eAllowDupes, eDoNotEmit
} // switch
@ -511,6 +535,86 @@ NotificationController::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
}
}
void
NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
AccSelChangeEvent* aThisEvent,
PRInt32 aThisIndex)
{
aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
// Pack all preceding events into single selection within event
// when we receive too much selection add/remove events.
if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
aTailEvent->mAccessible = aTailEvent->mWidget;
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
// Do not emit any preceding selection events for same widget if they
// weren't coalesced yet.
if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
for (PRInt32 jdx = aThisIndex - 1; jdx >= 0; jdx--) {
AccEvent* prevEvent = mEvents[jdx];
if (prevEvent->mEventRule == aTailEvent->mEventRule) {
AccSelChangeEvent* prevSelChangeEvent =
downcast_accEvent(prevEvent);
if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
}
}
}
return;
}
// Pack sequential selection remove and selection add events into
// single selection change event.
if (aTailEvent->mPreceedingCount == 1 &&
aTailEvent->mItem != aThisEvent->mItem) {
if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
aTailEvent->mPackedEvent = aThisEvent;
return;
}
if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
aThisEvent->mPackedEvent = aThisEvent;
return;
}
}
// Unpack the packed selection change event because we've got one
// more selection add/remove.
if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
if (aThisEvent->mPackedEvent) {
aThisEvent->mPackedEvent->mEventType =
aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
nsIAccessibleEvent::EVENT_SELECTION_ADD :
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
aThisEvent->mPackedEvent->mEventRule =
AccEvent::eCoalesceSelectionChange;
aThisEvent->mPackedEvent = nsnull;
}
aThisEvent->mEventType =
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
nsIAccessibleEvent::EVENT_SELECTION_ADD :
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
return;
}
// Convert into selection add since control has single selection but other
// selection events for this control are queued.
if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
}
void
NotificationController::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
AccHideEvent* aThisEvent)

View File

@ -251,11 +251,11 @@ private:
AccEvent::EEventRule aEventRule);
/**
* Do not emit one of two given reorder events fired for DOM nodes in the case
* when one DOM node is in parent chain of second one.
* Coalesce two selection change events within the same select control.
*/
void CoalesceReorderEventsFromSameTree(AccEvent* aAccEvent,
AccEvent* aDescendantAccEvent);
void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
AccSelChangeEvent* aThisEvent,
PRInt32 aThisIndex);
/**
* Coalesce text change events caused by sibling hide events.

View File

@ -473,7 +473,7 @@ static const char kEventTypeNames[][40] = {
"document attributes changed", // EVENT_DOCUMENT_ATTRIBUTES_CHANGED
"document content changed", // EVENT_DOCUMENT_CONTENT_CHANGED
"property changed", // EVENT_PROPERTY_CHANGED
"selection changed", // EVENT_SELECTION_CHANGED
"page changed", // EVENT_PAGE_CHANGED
"text attribute changed", // EVENT_TEXT_ATTRIBUTE_CHANGED
"text caret moved", // EVENT_TEXT_CARET_MOVED
"text changed", // EVENT_TEXT_CHANGED
@ -514,7 +514,6 @@ static const char kEventTypeNames[][40] = {
"hypertext changed", // EVENT_HYPERTEXT_CHANGED
"hypertext links count changed", // EVENT_HYPERTEXT_NLINKS_CHANGED
"object attribute changed", // EVENT_OBJECT_ATTRIBUTE_CHANGED
"page changed" // EVENT_PAGE_CHANGED
};
/**

View File

@ -1042,32 +1042,23 @@ nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID
return;
}
if (aAttribute == nsGkAtoms::selected ||
// ARIA or XUL selection
if ((aContent->IsXUL() && aAttribute == nsGkAtoms::selected) ||
aAttribute == nsGkAtoms::aria_selected) {
// ARIA or XUL selection
nsAccessible* item = GetAccessible(aContent);
nsAccessible* widget =
nsAccUtils::GetSelectableContainer(item, item->State());
if (widget) {
AccSelChangeEvent::SelChangeType selChangeType =
aContent->AttrValueIs(aNameSpaceID, aAttribute,
nsGkAtoms::_true, eCaseMatters) ?
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
nsAccessible *multiSelect =
nsAccUtils::GetMultiSelectableContainer(aContent);
// XXX: Multi selects are handled here only (bug 414302).
if (multiSelect) {
// Need to find the right event to use here, SELECTION_WITHIN would
// seem right but we had started using it for something else
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
multiSelect->GetNode(),
AccEvent::eAllowDupes);
static nsIContent::AttrValuesArray strings[] =
{&nsGkAtoms::_empty, &nsGkAtoms::_false, nsnull};
if (aContent->FindAttrValueIn(kNameSpaceID_None, aAttribute,
strings, eCaseMatters) >= 0) {
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_REMOVE,
aContent);
return;
}
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_ADD,
aContent);
nsRefPtr<AccEvent> event =
new AccSelChangeEvent(widget, item, selChangeType);
FireDelayedAccessibleEvent(event);
}
return;
}
if (aAttribute == nsGkAtoms::contenteditable) {
@ -1209,7 +1200,18 @@ void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
nsEventStates aStateMask)
{
if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent);
nsAccessible* item = GetAccessible(aContent);
if (item) {
nsAccessible* widget = item->ContainerWidget();
if (widget && widget->IsSelect()) {
AccSelChangeEvent::SelChangeType selChangeType =
aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
nsRefPtr<AccEvent> event = new AccSelChangeEvent(widget, item,
selChangeType);
FireDelayedAccessibleEvent(event);
}
}
}
if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {

View File

@ -473,6 +473,8 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
}
if (treeItemAccessible && eventType.EqualsLiteral("select")) {
// XXX: We shouldn't be based on DOM select event which doesn't provide us
// any context info. We should integrate into nsTreeSelection instead.
// If multiselect tree, we should fire selectionadd or selection removed
if (FocusMgr()->HasDOMFocus(targetNode)) {
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =

View File

@ -416,59 +416,7 @@ nsHTMLSelectOptionAccessible::SetSelected(bool aSelect)
nsAccessible*
nsHTMLSelectOptionAccessible::ContainerWidget() const
{
if (mParent && mParent->IsListControl()) {
nsAccessible* grandParent = mParent->Parent();
if (grandParent && grandParent->IsCombobox())
return grandParent;
return mParent;
}
return nsnull;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLSelectOptionAccessible: static methods
void
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(nsIContent *aPossibleOptionNode)
{
if (!aPossibleOptionNode ||
aPossibleOptionNode->Tag() != nsGkAtoms::option ||
!aPossibleOptionNode->IsHTML()) {
return;
}
nsAccessible *multiSelect =
nsAccUtils::GetMultiSelectableContainer(aPossibleOptionNode);
if (!multiSelect)
return;
nsAccessible *option = GetAccService()->GetAccessible(aPossibleOptionNode);
if (!option)
return;
nsRefPtr<AccEvent> selWithinEvent =
new AccEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN, multiSelect);
if (!selWithinEvent)
return;
option->GetDocAccessible()->FireDelayedAccessibleEvent(selWithinEvent);
PRUint64 state = option->State();
PRUint32 eventType;
if (state & states::SELECTED) {
eventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
}
else {
eventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
}
nsRefPtr<AccEvent> selAddRemoveEvent = new AccEvent(eventType, option);
if (selAddRemoveEvent)
option->GetDocAccessible()->FireDelayedAccessibleEvent(selAddRemoveEvent);
return mParent && mParent->IsListControl() ? mParent : nsnull;
}
////////////////////////////////////////////////////////////////////////////////
@ -738,26 +686,22 @@ nsHTMLComboboxAccessible::AreItemsOperable() const
nsAccessible*
nsHTMLComboboxAccessible::CurrentItem()
{
// No current item for collapsed combobox.
return SelectedOption(true);
return AreItemsOperable() ? mListAccessible->CurrentItem() : nsnull;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxAccessible: protected
nsAccessible*
nsHTMLComboboxAccessible::SelectedOption(bool aIgnoreIfCollapsed) const
nsHTMLComboboxAccessible::SelectedOption() const
{
nsIFrame* frame = GetFrame();
nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(frame);
if (comboboxFrame) {
if (aIgnoreIfCollapsed && !comboboxFrame->IsDroppedDown())
return nsnull;
if (!comboboxFrame)
return nsnull;
frame = comboboxFrame->GetDropDown();
}
nsIListControlFrame* listControlFrame = do_QueryFrame(frame);
nsIListControlFrame* listControlFrame =
do_QueryFrame(comboboxFrame->GetDropDown());
if (listControlFrame) {
nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
if (activeOptionNode) {
@ -858,3 +802,19 @@ void nsHTMLComboboxListAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aBo
*aBoundingFrame = frame->GetParent();
aBounds = (*aBoundingFrame)->GetRect();
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxListAccessible: Widgets
bool
nsHTMLComboboxListAccessible::IsActiveWidget() const
{
return mParent && mParent->IsActiveWidget();
}
bool
nsHTMLComboboxListAccessible::AreItemsOperable() const
{
return mParent && mParent->AreItemsOperable();
}

View File

@ -130,8 +130,6 @@ public:
// Widgets
virtual nsAccessible* ContainerWidget() const;
static void SelectionChangedIfOption(nsIContent *aPossibleOption);
protected:
// nsAccessible
virtual nsIFrame* GetBoundsFrame();
@ -219,7 +217,7 @@ protected:
/**
* Return selected option.
*/
nsAccessible* SelectedOption(bool aIgnoreIfCollapsed = false) const;
nsAccessible* SelectedOption() const;
private:
nsRefPtr<nsHTMLComboboxListAccessible> mListAccessible;
@ -246,6 +244,10 @@ public:
// nsAccessible
virtual PRUint64 NativeState();
virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
// Widgets
virtual bool IsActiveWidget() const;
virtual bool AreItemsOperable() const;
};
#endif

View File

@ -190,9 +190,7 @@ GetNativeFromGeckoAccessible(nsIAccessible *anAccessible)
NSAccessibilityTitleAttribute,
NSAccessibilityValueAttribute,
NSAccessibilitySubroleAttribute,
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
NSAccessibilityRoleDescriptionAttribute,
#endif
NSAccessibilityPositionAttribute,
NSAccessibilityEnabledAttribute,
NSAccessibilitySizeAttribute,
@ -236,10 +234,8 @@ GetNativeFromGeckoAccessible(nsIAccessible *anAccessible)
return [NSNumber numberWithBool:[self isEnabled]];
if ([attribute isEqualToString:NSAccessibilityValueAttribute])
return [self value];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
return NSAccessibilityRoleDescription([self role], nil);
#endif
if ([attribute isEqualToString: (NSString*) kInstanceDescriptionAttribute])
return [self customDescription];
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])

View File

@ -226,9 +226,7 @@ enum CheckboxValue {
if ([self isEnabled]) {
return [NSArray arrayWithObjects:NSAccessibilityPressAction,
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
NSAccessibilityShowMenuAction,
#endif
nil];
}
return nil;
@ -240,10 +238,8 @@ enum CheckboxValue {
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if ([action isEqualToString:NSAccessibilityShowMenuAction])
return @"show menu";
#endif
return [super accessibilityActionDescription:action];
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;

View File

@ -49,9 +49,7 @@ extern const NSString *kTopLevelUIElementAttribute; // NSAccessibilityTopLevel
NSAccessibilityTitleAttribute,
NSAccessibilityValueAttribute, // required
NSAccessibilitySubroleAttribute,
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
NSAccessibilityRoleDescriptionAttribute,
#endif
NSAccessibilityPositionAttribute, // required
NSAccessibilitySizeAttribute, // required
NSAccessibilityWindowAttribute, // required
@ -249,9 +247,7 @@ extern const NSString *kTopLevelUIElementAttribute; // NSAccessibilityTopLevel
NSAccessibilityTitleAttribute,
NSAccessibilityValueAttribute, // required
NSAccessibilityHelpAttribute,
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
NSAccessibilityRoleDescriptionAttribute,
#endif
NSAccessibilityPositionAttribute, // required
NSAccessibilitySizeAttribute, // required
NSAccessibilityWindowAttribute, // required
@ -281,9 +277,7 @@ extern const NSString *kTopLevelUIElementAttribute; // NSAccessibilityTopLevel
if ([self isEnabled]) {
return [NSArray arrayWithObjects:NSAccessibilityConfirmAction,
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
NSAccessibilityShowMenuAction,
#endif
nil];
}
return nil;
@ -295,10 +289,8 @@ extern const NSString *kTopLevelUIElementAttribute; // NSAccessibilityTopLevel
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if ([action isEqualToString:NSAccessibilityShowMenuAction])
return @"show menu";
#endif
if ([action isEqualToString:NSAccessibilityConfirmAction])
return @"confirm";
@ -313,10 +305,8 @@ extern const NSString *kTopLevelUIElementAttribute; // NSAccessibilityTopLevel
// both the ShowMenu and Click action do the same thing.
if ([self isEnabled]) {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if ([action isEqualToString:NSAccessibilityShowMenuAction])
[self showMenu];
#endif
if ([action isEqualToString:NSAccessibilityConfirmAction])
[self confirm];
}

View File

@ -90,7 +90,7 @@ static const PRUint32 gWinEventMap[] = {
IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_DOCUMENT_ATTRIBUTES_CHANGED
IA2_EVENT_DOCUMENT_CONTENT_CHANGED, // nsIAccessibleEvent::EVENT_DOCUMENT_CONTENT_CHANGED
kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_PROPERTY_CHANGED
kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_SELECTION_CHANGED
IA2_EVENT_PAGE_CHANGED, // nsIAccessibleEvent::IA2_EVENT_PAGE_CHANGED
IA2_EVENT_TEXT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED
IA2_EVENT_TEXT_CARET_MOVED, // nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED
IA2_EVENT_TEXT_CHANGED, // nsIAccessibleEvent::EVENT_TEXT_CHANGED
@ -131,7 +131,6 @@ static const PRUint32 gWinEventMap[] = {
IA2_EVENT_HYPERTEXT_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED
IA2_EVENT_HYPERTEXT_NLINKS_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED
IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
IA2_EVENT_PAGE_CHANGED, // nsIAccessibleEvent::EVENT_PAGE_CHANGED
kEVENT_LAST_ENTRY // nsIAccessibleEvent::EVENT_LAST_ENTRY
};

View File

@ -15,7 +15,9 @@ const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END;
const EVENT_OBJECT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_OBJECT_ATTRIBUTE_CHANGED;
const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START;
const EVENT_SELECTION = nsIAccessibleEvent.EVENT_SELECTION;
const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD;
const EVENT_SELECTION_REMOVE = nsIAccessibleEvent.EVENT_SELECTION_REMOVE;
const EVENT_SELECTION_WITHIN = nsIAccessibleEvent.EVENT_SELECTION_WITHIN;
const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;

View File

@ -82,7 +82,9 @@ _TEST_FILES =\
test_mutation.html \
test_mutation.xhtml \
test_scroll.xul \
test_selection_aria.html \
test_selection.html \
test_selection.xul \
test_statechange.html \
test_text_alg.html \
test_text.html \

View File

@ -22,39 +22,45 @@
////////////////////////////////////////////////////////////////////////////
// Invokers
function addSelection(aNode, aOption)
{
this.DOMNode = aNode;
this.optionNode = aOption;
this.eventSeq = [
new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.DOMNode)),
new invokerChecker(EVENT_SELECTION_ADD, getAccessible(this.optionNode))
];
this.invoke = function addselection_invoke() {
synthesizeMouse(this.optionNode, 1, 1, {});
};
this.getID = function addselection_getID() {
return prettyName(this.optionNode) + " added to selection";
};
}
////////////////////////////////////////////////////////////////////////////
// Do tests
//gA11yEventDumpToConsole = true; // debuggin
var gQueue = null;
//var gA11yEventDumpID = "eventdump"; // debug stuff
function doTests()
{
gQueue = new eventQueue();
var select = document.getElementById("toppings");
var option = document.getElementById("onions");
gQueue.push(new addSelection(select, option));
// open combobox
gQueue.push(new synthClick("combobox",
new invokerChecker(EVENT_FOCUS, "cb1_item1")));
gQueue.push(new synthDownKey("cb1_item1",
new invokerChecker(EVENT_SELECTION, "cb1_item2")));
// closed combobox
gQueue.push(new synthEscapeKey("combobox",
new invokerChecker(EVENT_FOCUS, "combobox")));
gQueue.push(new synthDownKey("cb1_item2",
new invokerChecker(EVENT_SELECTION, "cb1_item3")));
// listbox
gQueue.push(new synthClick("lb1_item1",
new invokerChecker(EVENT_SELECTION, "lb1_item1")));
gQueue.push(new synthDownKey("lb1_item1",
new invokerChecker(EVENT_SELECTION, "lb1_item2")));
// multiselectable listbox
gQueue.push(new synthClick("lb2_item1",
new invokerChecker(EVENT_SELECTION, "lb2_item1")));
gQueue.push(new synthDownKey("lb2_item1",
new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
{ shiftKey: true }));
gQueue.push(new synthUpKey("lb2_item2",
new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
{ shiftKey: true }));
gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -67,9 +73,9 @@
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=569653"
title="Make selection events async">
Mozilla Bug 569653
href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
title="Incorrect selection events in HTML, XUL and ARIA">
Mozilla Bug 414302
</a>
<p id="display"></p>
@ -77,18 +83,31 @@
<pre id="test">
</pre>
<p>Pizza</p>
<select id="toppings" name="toppings" multiple size=5>
<option value="mushrooms">mushrooms
<option value="greenpeppers">green peppers
<option value="onions" id="onions">onions
<option value="tomatoes">tomatoes
<option value="olives">olives
<select id="combobox">
<option id="cb1_item1" value="mushrooms">mushrooms
<option id="cb1_item2" value="greenpeppers">green peppers
<option id="cb1_item3" value="onions" id="onions">onions
<option id="cb1_item4" value="tomatoes">tomatoes
<option id="cb1_item5" value="olives">olives
</select>
<select id="listbox" size=5>
<option id="lb1_item1" value="mushrooms">mushrooms
<option id="lb1_item2" value="greenpeppers">green peppers
<option id="lb1_item3" value="onions" id="onions">onions
<option id="lb1_item4" value="tomatoes">tomatoes
<option id="lb1_item5" value="olives">olives
</select>
<p>Pizza</p>
<select id="listbox2" multiple size=5>
<option id="lb2_item1" value="mushrooms">mushrooms
<option id="lb2_item2" value="greenpeppers">green peppers
<option id="lb2_item3" value="onions" id="onions">onions
<option id="lb2_item4" value="tomatoes">tomatoes
<option id="lb2_item5" value="olives">olives
</select>
<div id="testContainer">
<iframe id="iframe"></iframe>
</div>
<div id="eventdump"></div>
</body>
</html>

View File

@ -0,0 +1,244 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Selection event tests">
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript"
src="../common.js" />
<script type="application/javascript"
src="../states.js" />
<script type="application/javascript"
src="../events.js" />
<script type="application/javascript">
function advanceTab(aTabsID, aDirection, aNextTabID)
{
this.eventSeq = [
new invokerChecker(EVENT_SELECTION, aNextTabID)
];
this.invoke = function advanceTab_invoke()
{
getNode(aTabsID).advanceSelectedTab(aDirection, true);
}
this.getID = function synthFocus_getID()
{
return "advanceTab on " + prettyName(aTabsID) + " to " + prettyName(aNextTabID);
}
}
function select4FirstItems(aID)
{
this.listboxNode = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0)),
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(1)),
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(2)),
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(3))
];
this.invoke = function select4FirstItems_invoke()
{
synthesizeKey("VK_DOWN", { shiftKey: true }); // selects two items
synthesizeKey("VK_DOWN", { shiftKey: true });
synthesizeKey("VK_DOWN", { shiftKey: true });
}
this.getID = function select4FirstItems_getID()
{
return "select 4 first items for " + prettyName(aID);
}
}
function unselect4FirstItems(aID)
{
this.listboxNode = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(3)),
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(2)),
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(1)),
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0))
];
this.invoke = function unselect4FirstItems_invoke()
{
synthesizeKey("VK_UP", { shiftKey: true });
synthesizeKey("VK_UP", { shiftKey: true });
synthesizeKey("VK_UP", { shiftKey: true });
synthesizeKey(" ", { ctrlKey: true }); // unselect first item
}
this.getID = function unselect4FirstItems_getID()
{
return "unselect 4 first items for " + prettyName(aID);
}
}
function selectAllItems(aID)
{
this.listboxNode = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
];
this.invoke = function selectAllItems_invoke()
{
synthesizeKey("VK_END", { shiftKey: true });
}
this.getID = function selectAllItems_getID()
{
return "select all items for " + prettyName(aID);
}
}
function unselectAllItemsButFirst(aID)
{
this.listboxNode = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
];
this.invoke = function unselectAllItemsButFirst_invoke()
{
synthesizeKey("VK_HOME", { shiftKey: true });
}
this.getID = function unselectAllItemsButFirst_getID()
{
return "unselect all items for " + prettyName(aID);
}
}
function unselectSelectItem(aID)
{
this.listboxNode = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0)),
new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0))
];
this.invoke = function unselectSelectItem_invoke()
{
synthesizeKey(" ", { ctrlKey: true }); // select item
synthesizeKey(" ", { ctrlKey: true }); // unselect item
}
this.getID = function unselectSelectItem_getID()
{
return "unselect and then select first item for " + prettyName(aID);
}
}
/**
* Do tests.
*/
var gQueue = null;
//gA11yEventDumpToConsole = true; // debuggin
function doTests()
{
gQueue = new eventQueue();
//////////////////////////////////////////////////////////////////////////
// tabbox
gQueue.push(new advanceTab("tabs", 1, "tab3"));
//////////////////////////////////////////////////////////////////////////
// listbox
gQueue.push(new synthClick("lb1_item1",
new invokerChecker(EVENT_SELECTION, "lb1_item1")));
gQueue.push(new synthDownKey("lb1_item1",
new invokerChecker(EVENT_SELECTION, "lb1_item2")));
//////////////////////////////////////////////////////////////////////////
// multiselectable listbox
gQueue.push(new synthClick("lb2_item1",
new invokerChecker(EVENT_SELECTION, "lb2_item1")));
gQueue.push(new synthDownKey("lb2_item1",
new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
{ shiftKey: true }));
gQueue.push(new synthUpKey("lb2_item2",
new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
{ shiftKey: true }));
gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
//////////////////////////////////////////////////////////////////////////
// selection event coalescence
// fire 4 selection_add events
gQueue.push(new select4FirstItems("listbox2"));
// fire 4 selection_remove events
gQueue.push(new unselect4FirstItems("listbox2"));
// fire selection_within event
gQueue.push(new selectAllItems("listbox2"));
// fire selection_within event
gQueue.push(new unselectAllItemsButFirst("listbox2"));
// fire selection_remove/add events
gQueue.push(new unselectSelectItem("listbox2"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
</script>
<hbox flex="1" style="overflow: auto;">
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
title="Incorrect selection events in HTML, XUL and ARIA">
Mozilla Bug 414302
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
<tabbox id="tabbox" selectedIndex="1">
<tabs id="tabs">
<tab id="tab1" label="tab1"/>
<tab id="tab2" label="tab2"/>
<tab id="tab3" label="tab3"/>
<tab id="tab4" label="tab4"/>
</tabs>
<tabpanels>
<tabpanel><!-- tabpanel First elements go here --></tabpanel>
<tabpanel><button id="b1" label="b1"/></tabpanel>
<tabpanel><button id="b2" label="b2"/></tabpanel>
<tabpanel></tabpanel>
</tabpanels>
</tabbox>
<listbox id="listbox">
<listitem id="lb1_item1" label="item1"/>
<listitem id="lb1_item2" label="item2"/>
</listbox>
<listbox id="listbox2" seltype="multiple">
<listitem id="lb2_item1" label="item1"/>
<listitem id="lb2_item2" label="item2"/>
<listitem id="lb2_item3" label="item3"/>
<listitem id="lb2_item4" label="item4"/>
<listitem id="lb2_item5" label="item5"/>
<listitem id="lb2_item6" label="item6"/>
<listitem id="lb2_item7" label="item7"/>
</listbox>
</hbox>
</window>

View File

@ -0,0 +1,112 @@
<html>
<head>
<title>ARIA selection event testing</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="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript"
src="../states.js"></script>
<script type="application/javascript">
////////////////////////////////////////////////////////////////////////////
// Invokers
function selectTreeItem(aTreeID, aItemID)
{
this.treeNode = getNode(aTreeID);
this.itemNode = getNode(aItemID);
this.eventSeq = [
new invokerChecker(EVENT_SELECTION, aItemID)
];
this.invoke = function selectTreeItem_invoke() {
var itemNode = this.treeNode.querySelector("*[aria-selected='true']");
if (itemNode)
itemNode.removeAttribute("aria-selected", "true");
this.itemNode.setAttribute("aria-selected", "true");
}
this.getID = function selectTreeItem_getID()
{
return "selectTreeItem " + prettyName(aItemID);
}
}
////////////////////////////////////////////////////////////////////////////
// Do tests
var gQueue = null;
//var gA11yEventDumpID = "eventdump"; // debug stuff
function doTests()
{
gQueue = new eventQueue();
gQueue.push(new selectTreeItem("tree", "treeitem1"));
gQueue.push(new selectTreeItem("tree", "treeitem1a"));
gQueue.push(new selectTreeItem("tree", "treeitem1a1"));
gQueue.push(new selectTreeItem("tree2", "tree2item1"));
gQueue.push(new selectTreeItem("tree2", "tree2item1a"));
gQueue.push(new selectTreeItem("tree2", "tree2item1a1"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=569653"
title="Make selection events async">
Mozilla Bug 569653
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="tree" role="tree">
<div id="treeitem1" role="treeitem">Canada
<div id="treeitem1a" role="treeitem">- Ontario
<div id="treeitem1a1" role="treeitem">-- Toronto</div>
</div>
<div id="treeitem1b" role="treeitem">- Manitoba</div>
</div>
<div id="treeitem2" role="treeitem">Germany</div>
<div id="treeitem3" role="treeitem">Russia</div>
</div>
<div id="tree2" role="tree" aria-multiselectable="true">
<div id="tree2item1" role="treeitem">Canada
<div id="tree2item1a" role="treeitem">- Ontario
<div id="tree2item1a1" role="treeitem">-- Toronto</div>
</div>
<div id="tree2item1b" role="treeitem">- Manitoba</div>
</div>
<div id="tree2item2" role="treeitem">Germany</div>
<div id="tree2item3" role="treeitem">Russia</div>
</div>
<div id="eventdump"></div>
</body>
</html>

View File

@ -1,7 +1,4 @@
# Don't use the standard mozconfig. We don't want universal for a debug build.
#. $topsrcdir/build/macosx/universal/mozconfig
ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.5.sdk
. $topsrcdir/build/macosx/mozconfig.leopard
ac_add_options --enable-debug
ac_add_options --enable-trace-malloc

View File

@ -584,6 +584,7 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
if loc.scheme == "https" and "nocert" not in loc.options:
customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
redirRE = re.compile("^redir=(?P<redirhost>[0-9a-zA-Z_ .]+)")
for option in loc.options:
match = customCertRE.match(option)
if match:
@ -597,6 +598,12 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
sslTunnelConfig.write("clientauth:%s:%s:%s:%s\n" %
(loc.host, loc.port, self.sslPort, clientauth))
match = redirRE.match(option)
if match:
redirhost = match.group("redirhost")
sslTunnelConfig.write("redirhost:%s:%s:%s:%s\n" %
(loc.host, loc.port, self.sslPort, redirhost))
sslTunnelConfig.close()
# Pre-create the certification database for the profile
@ -739,52 +746,52 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
def killPid(self, pid):
os.kill(pid, signal.SIGKILL)
def dumpScreen(self, utilityPath):
self.haveDumpedScreen = True;
def dumpScreen(self, utilityPath):
self.haveDumpedScreen = True;
# Need to figure out what tool and whether it write to a file or stdout
if self.UNIXISH:
utility = [os.path.join(utilityPath, "screentopng")]
imgoutput = 'stdout'
elif self.IS_MAC:
utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
imgoutput = 'file'
elif self.IS_WIN32:
self.log.info("If you fixed bug 589668, you'd get a screenshot here")
return
# Need to figure out what tool and whether it write to a file or stdout
if self.UNIXISH:
utility = [os.path.join(utilityPath, "screentopng")]
imgoutput = 'stdout'
elif self.IS_MAC:
utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
imgoutput = 'file'
elif self.IS_WIN32:
self.log.info("If you fixed bug 589668, you'd get a screenshot here")
return
# Run the capture correctly for the type of capture
try:
if imgoutput == 'file':
tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
os.close(tmpfd)
dumper = self.Process(utility + [imgfilename])
elif imgoutput == 'stdout':
dumper = self.Process(utility, bufsize=-1,
stdout=subprocess.PIPE, close_fds=True)
except OSError, err:
self.log.info("Failed to start %s for screenshot: %s",
utility[0], err.strerror)
return
# Run the capture correctly for the type of capture
try:
if imgoutput == 'file':
tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
os.close(tmpfd)
dumper = self.Process(utility + [imgfilename])
elif imgoutput == 'stdout':
dumper = self.Process(utility, bufsize=-1,
stdout=subprocess.PIPE, close_fds=True)
except OSError, err:
self.log.info("Failed to start %s for screenshot: %s",
utility[0], err.strerror)
return
# Check whether the capture utility ran successfully
dumper_out, dumper_err = dumper.communicate()
if dumper.returncode != 0:
self.log.info("%s exited with code %d", utility, dumper.returncode)
return
# Check whether the capture utility ran successfully
dumper_out, dumper_err = dumper.communicate()
if dumper.returncode != 0:
self.log.info("%s exited with code %d", utility, dumper.returncode)
return
try:
if imgoutput == 'stdout':
image = dumper_out
elif imgoutput == 'file':
with open(imgfilename) as imgfile:
image = imgfile.read()
except IOError, err:
self.log.info("Failed to read image from %s", imgoutput)
try:
if imgoutput == 'stdout':
image = dumper_out
elif imgoutput == 'file':
with open(imgfilename) as imgfile:
image = imgfile.read()
except IOError, err:
self.log.info("Failed to read image from %s", imgoutput)
import base64
encoded = base64.b64encode(image)
self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
import base64
encoded = base64.b64encode(image)
self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
def killAndGetStack(self, proc, utilityPath, debuggerInfo):
"""Kill the process, preferrably in a way that gets us a stack trace."""

View File

@ -52,7 +52,7 @@
# number is the default for the protocol.
#
# Unrecognized options are ignored. Recognized options are "primary" and
# "privileged", "nocert", "cert=some_cert_nickname".
# "privileged", "nocert", "cert=some_cert_nickname", "redir=hostname".
#
# "primary" denotes a location which is the canonical location of
# the server; this location is the one assumed for requests which don't
@ -71,6 +71,12 @@
# directory. When new certificate is added to this dir pgo/ssltunnel
# must be builded then.
#
# "redir=hostname" tells the pgo server is only used for https://
# hosts while processing the CONNECT tunnel request. It responds
# to the CONNECT with a 302 and redirection to the hostname instead
# of connecting to the real back end and replying with a 200. This
# mode exists primarily to ensure we don't allow a proxy to do that.
#
#
# This is the primary location from which tests run.
@ -175,3 +181,10 @@ http://malware.example.com:80
# Bug 483437, 484111
https://www.bank1.com:443 privileged,cert=escapeattack1
https://www.bank2.com:443 privileged,cert=escapeattack2
#
# CONNECT for redirproxy results in a 302 redirect to
# test1.example.com
#
https://redirproxy.example.com:443 privileged,redir=test1.example.com

View File

@ -207,20 +207,36 @@ nsPrincipal::GetOrigin(char **aOrigin)
bool isChrome;
nsresult rv = origin->SchemeIs("chrome", &isChrome);
if (NS_SUCCEEDED(rv) && !isChrome) {
rv = origin->GetHostPort(hostPort);
rv = origin->GetAsciiHost(hostPort);
// Some implementations return an empty string, treat it as no support
// for asciiHost by that implementation.
if (hostPort.IsEmpty())
rv = NS_ERROR_FAILURE;
}
PRInt32 port;
if (NS_SUCCEEDED(rv) && !isChrome) {
rv = origin->GetPort(&port);
}
if (NS_SUCCEEDED(rv) && !isChrome) {
if (port != -1) {
hostPort.AppendLiteral(":");
hostPort.AppendInt(port, 10);
}
nsCAutoString scheme;
rv = origin->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
*aOrigin = ToNewCString(scheme + NS_LITERAL_CSTRING("://") + hostPort);
}
else {
// Some URIs (e.g., nsSimpleURI) don't support host. Just
// Some URIs (e.g., nsSimpleURI) don't support asciiHost. Just
// get the full spec.
nsCAutoString spec;
rv = origin->GetSpec(spec);
// XXX nsMozIconURI and nsJARURI don't implement this correctly, they
// both fall back to GetSpec. That needs to be fixed.
rv = origin->GetAsciiSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
*aOrigin = ToNewCString(spec);
}

View File

@ -4266,7 +4266,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
_USE_SYSTEM_NSS=1 )
if test -n "$_USE_SYSTEM_NSS"; then
AM_PATH_NSS(3.12.10, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
AM_PATH_NSS(3.13.1, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
fi
if test -n "$MOZ_NATIVE_NSS"; then

View File

@ -218,6 +218,14 @@ nsAttrAndChildArray::InsertChildAt(nsIContent* aChild, PRUint32 aPos)
void
nsAttrAndChildArray::RemoveChildAt(PRUint32 aPos)
{
// Just store the return value of TakeChildAt in an nsCOMPtr to
// trigger a release.
nsCOMPtr<nsIContent> child = TakeChildAt(aPos);
}
already_AddRefed<nsIContent>
nsAttrAndChildArray::TakeChildAt(PRUint32 aPos)
{
NS_ASSERTION(aPos < ChildCount(), "out-of-bounds");
@ -232,9 +240,10 @@ nsAttrAndChildArray::RemoveChildAt(PRUint32 aPos)
}
child->mPreviousSibling = child->mNextSibling = nsnull;
NS_RELEASE(child);
memmove(pos, pos + 1, (childCount - aPos - 1) * sizeof(nsIContent*));
SetChildCount(childCount - 1);
return child;
}
PRInt32

View File

@ -95,6 +95,9 @@ public:
}
nsresult InsertChildAt(nsIContent* aChild, PRUint32 aPos);
void RemoveChildAt(PRUint32 aPos);
// Like RemoveChildAt but hands the reference to the child being
// removed back to the caller instead of just releasing it.
already_AddRefed<nsIContent> TakeChildAt(PRUint32 aPos);
PRInt32 IndexOfChild(nsINode* aPossibleChild) const;
PRUint32 AttrCount() const;

View File

@ -5813,7 +5813,9 @@ nsContentUtils::IsFullScreenApiEnabled()
bool nsContentUtils::IsRequestFullScreenAllowed()
{
return !sTrustedFullScreenOnly || nsEventStateManager::IsHandlingUserInput();
return !sTrustedFullScreenOnly ||
nsEventStateManager::IsHandlingUserInput() ||
IsCallerChrome();
}
bool

View File

@ -8606,6 +8606,14 @@ nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
NS_ENSURE_ARG_POINTER(aFullScreen);
*aFullScreen = false;
if (nsContentUtils::IsCallerChrome() &&
nsContentUtils::IsFullScreenApiEnabled()) {
// Chrome code can always use the full-screen API, provided it's not
// explicitly disabled.
*aFullScreen = true;
return NS_OK;
}
if (!nsContentUtils::IsFullScreenApiEnabled() ||
nsContentUtils::HasPluginWithUncontrolledEventDispatch(this) ||
!IsVisible()) {

View File

@ -4234,10 +4234,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
// Once we have XPCOMGC we shouldn't need to call UnbindFromTree.
// We could probably do a non-deep unbind here when IsInDoc is false
// for better performance.
tmp->mAttrsAndChildren.ChildAt(childCount)->UnbindFromTree();
tmp->mAttrsAndChildren.RemoveChildAt(childCount);
// Hold a strong ref to the node when we remove it, because we may be
// the last reference to it. We need to call TakeChildAt() and
// update mFirstChild before calling UnbindFromTree, since this last
// can notify various observers and they should really see consistent
// tree state.
nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
if (childCount == 0) {
tmp->mFirstChild = nsnull;
}
child->UnbindFromTree();
}
tmp->mFirstChild = nsnull;
}
}

View File

@ -70,6 +70,7 @@ _CHROME_FILES = \
test_bug571390.xul \
test_bug574596.html \
test_bug683852.xul \
test_bug599295.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,81 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=599295
-->
<head>
<title>Test for Bug 599295</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=599295">Mozilla Bug 599295</a>
<style type="text/css">
#link1 a { -moz-user-select:none; }
</style>
<div id="link1"><a href="http://www.mozilla.org/">link1</a></div>
<div id="link2"><a href="http://www.mozilla.org/">link2</a></div>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 599295 **/
/* Do not allow a response to a CONNECT method, used to establish an
SSL tunnel over an HTTP proxy, to contain a redirect */
const BinaryInputStream =
Components.Constructor("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream");
var listener = {
_httpstatus : 0,
onStartRequest: function(request, context) {
request.QueryInterface(Components.interfaces.nsIHttpChannel);
_httpstatus = request.responseStatus;
},
onDataAvailable: function(request, context, stream, offset, count) {
new BinaryInputStream(stream).readByteArray(count);
},
onStopRequest: function(request, context, status) {
/* testing here that the redirect was not followed. If it was followed
we would see a http status of 200 and status of NS_OK */
is(_httpstatus, 302, "http status 302");
is(status, Components.results.NS_ERROR_CONNECTION_REFUSED, "raised refused");
SimpleTest.finish();
}
};
function runTest() {
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI("https://redirproxy.example.com/test", "", null);
var channel = ios.newChannelFromURI(uri);
/* Previously, necko would allow a 302 as part of a CONNECT response
if the LOAD_DOCUMENT_URI flag was set and the original document
URI had not yet been changed. */
channel.loadFlags |= Components.interfaces.nsIChannel.LOAD_DOCUMENT_URI;
channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal);
channel.documentURI = uri;
channel.asyncOpen(listener, null);
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTest);
</script>
</pre>
</body>
</html>

View File

@ -115,15 +115,12 @@
#include "mozilla/ipc/DocumentRendererParent.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/PathHelpers.h"
#ifdef XP_WIN
#include "gfxWindowsPlatform.h"
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// windows.h (included by chromium code) defines this, in its infinite wisdom
#undef DrawText
@ -440,6 +437,9 @@ public:
STYLE_FILL,
STYLE_MAX
};
nsresult LineTo(const Point& aPoint);
nsresult BezierTo(const Point& aCP1, const Point& aCP2, const Point& aCP3);
protected:
nsresult InitializeWithTarget(DrawTarget *surface, PRInt32 width, PRInt32 height);
@ -988,15 +988,15 @@ PRUint8 (*nsCanvasRenderingContext2DAzure::sPremultiplyTable)[256] = nsnull;
nsresult
NS_NewCanvasRenderingContext2DAzure(nsIDOMCanvasRenderingContext2D** aResult)
{
#ifndef XP_WIN
return NS_ERROR_NOT_AVAILABLE;
#else
#ifdef XP_WIN
if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() !=
gfxWindowsPlatform::RENDER_DIRECT2D ||
!gfxWindowsPlatform::GetPlatform()->DWriteEnabled()) {
return NS_ERROR_NOT_AVAILABLE;
}
#elif !defined(XP_MACOSX) && !defined(ANDROID)
return NS_ERROR_NOT_AVAILABLE;
#endif
nsRefPtr<nsIDOMCanvasRenderingContext2D> ctx = new nsCanvasRenderingContext2DAzure();
if (!ctx)
@ -1004,7 +1004,6 @@ NS_NewCanvasRenderingContext2DAzure(nsIDOMCanvasRenderingContext2D** aResult)
*aResult = ctx.forget().get();
return NS_OK;
#endif
}
nsCanvasRenderingContext2DAzure::nsCanvasRenderingContext2DAzure()
@ -1253,7 +1252,7 @@ nsCanvasRenderingContext2DAzure::SetDimensions(PRInt32 width, PRInt32 height)
if (layerManager) {
target = layerManager->CreateDrawTarget(size, format);
} else {
target = Factory::CreateDrawTarget(BACKEND_DIRECT2D, size, format);
target = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(size, format);
}
}
@ -1293,7 +1292,7 @@ nsCanvasRenderingContext2DAzure::InitializeWithTarget(DrawTarget *target, PRInt3
*/
if (!target)
{
mTarget = Factory::CreateDrawTarget(BACKEND_DIRECT2D, IntSize(1, 1), FORMAT_B8G8R8A8);
mTarget = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(IntSize(1, 1), FORMAT_B8G8R8A8);
} else {
mValid = true;
}
@ -2315,10 +2314,16 @@ nsCanvasRenderingContext2DAzure::LineTo(float x, float y)
EnsureWritablePath();
return LineTo(Point(x, y));;
}
nsresult
nsCanvasRenderingContext2DAzure::LineTo(const Point& aPoint)
{
if (mPathBuilder) {
mPathBuilder->LineTo(Point(x, y));
mPathBuilder->LineTo(aPoint);
} else {
mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y));
mDSPathBuilder->LineTo(mTarget->GetTransform() * aPoint);
}
return NS_OK;
@ -2354,13 +2359,21 @@ nsCanvasRenderingContext2DAzure::BezierCurveTo(float cp1x, float cp1y,
EnsureWritablePath();
return BezierTo(Point(cp1x, cp1y), Point(cp2x, cp2y), Point(x, y));
}
nsresult
nsCanvasRenderingContext2DAzure::BezierTo(const Point& aCP1,
const Point& aCP2,
const Point& aCP3)
{
if (mPathBuilder) {
mPathBuilder->BezierTo(Point(cp1x, cp1y), Point(cp2x, cp2y), Point(x, y));
mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
} else {
Matrix transform = mTarget->GetTransform();
mDSPathBuilder->BezierTo(transform * Point(cp1x, cp1y),
transform * Point(cp2x, cp2y),
transform * Point(x, y));
mDSPathBuilder->BezierTo(transform * aCP1,
transform * aCP2,
transform * aCP3);
}
return NS_OK;
@ -2461,83 +2474,7 @@ nsCanvasRenderingContext2DAzure::Arc(float x, float y,
EnsureWritablePath();
// We convert to Bezier curve here, since we need to be able to write in
// device space, but a transformed arc is no longer representable by an arc.
Point startPoint(x + cos(startAngle) * r, y + sin(startAngle) * r);
if (mPathBuilder) {
mPathBuilder->LineTo(startPoint);
} else {
mDSPathBuilder->LineTo(mTarget->GetTransform() * startPoint);
}
// Clockwise we always sweep from the smaller to the larger angle, ccw
// it's vice versa.
if (!ccw && (endAngle < startAngle)) {
Float correction = ceil((startAngle - endAngle) / (2.0f * M_PI));
endAngle += correction * 2.0f * M_PI;
} else if (ccw && (startAngle < endAngle)) {
Float correction = ceil((endAngle - startAngle) / (2.0f * M_PI));
startAngle += correction * 2.0f * M_PI;
}
// Sweeping more than 2 * pi is a full circle.
if (!ccw && (endAngle - startAngle > 2 * M_PI)) {
endAngle = startAngle + 2.0f * M_PI;
} else if (ccw && (startAngle - endAngle > 2.0f * M_PI)) {
endAngle = startAngle - 2.0f * M_PI;
}
// Calculate the total arc we're going to sweep.
Float arcSweepLeft = abs(endAngle - startAngle);
Float sweepDirection = ccw ? -1.0f : 1.0f;
Float currentStartAngle = startAngle;
while (arcSweepLeft > 0) {
// We guarantee here the current point is the start point of the next
// curve segment.
Float currentEndAngle;
if (arcSweepLeft > M_PI / 2.0f) {
currentEndAngle = currentStartAngle + M_PI / 2.0f * sweepDirection;
} else {
currentEndAngle = currentStartAngle + arcSweepLeft * sweepDirection;
}
Point currentStartPoint(x + cos(currentStartAngle) * r,
y + sin(currentStartAngle) * r);
Point currentEndPoint(x + cos(currentEndAngle) * r,
y + sin(currentEndAngle) * r);
// Calculate kappa constant for partial curve. The sign of angle in the
// tangent will actually ensure this is negative for a counter clockwise
// sweep, so changing signs later isn't needed.
Float kappa = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f) * r;
Point tangentStart(-sin(currentStartAngle), cos(currentStartAngle));
Point cp1 = currentStartPoint;
cp1 += tangentStart * kappa;
Point revTangentEnd(sin(currentEndAngle), -cos(currentEndAngle));
Point cp2 = currentEndPoint;
cp2 += revTangentEnd * kappa;
if (mPathBuilder) {
mPathBuilder->BezierTo(cp1, cp2, currentEndPoint);
} else {
mDSPathBuilder->BezierTo(mTarget->GetTransform() * cp1,
mTarget->GetTransform() * cp2,
mTarget->GetTransform() * currentEndPoint);
}
arcSweepLeft -= M_PI / 2.0f;
currentStartAngle = currentEndAngle;
}
ArcToBezier(this, Point(x, y), r, startAngle, endAngle, ccw);
return NS_OK;
}

View File

@ -1,4 +1,4 @@
This is a local copy of the WebGL conformance suite, SVN revision 15892
This is a local copy of the WebGL conformance suite, SVN revision 15981
The canonical location for this testsuite is:

View File

@ -33,8 +33,8 @@
</script>
<script>
description("tests that turning on attribs that have no buffer bound fails to draw");
gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
gl.viewport(0, 0, 50, 50);
var vertexObject = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);

View File

@ -27,7 +27,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<html>
<head>
<meta charset="utf-8">
<title>Rendering Test</title>
<title>vertexattribpointer offsets test</title>
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../resources/js-test-pre.js"></script>
<script src="../resources/webgl-test.js"> </script>
@ -74,6 +74,7 @@ There is supposed to be an example drawing here, but it's not important.
if (window.initNonKhronosFramework) {
window.initNonKhronosFramework(false);
}
description("test vertexattribpointer offsets work");
wtu = WebGLTestUtils;
gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);

View File

@ -19,7 +19,7 @@ found in the LICENSE file.
<div id="description"></div>
<div id="console"></div>
<script>
debug("Tests that resources can not be shared.");
description("Tests that resources can not be shared.");
debug("");
var gl1 = create3DContext(document.getElementById("example1"));

View File

@ -2,5 +2,7 @@ functions/00_test_list.txt
implicit/00_test_list.txt
misc/00_test_list.txt
reserved/00_test_list.txt
# commented out for version 1.0.1 of the conforamnce tests.
#samplers/00_test_list.txt
variables/00_test_list.txt

View File

@ -1,3 +1,5 @@
attrib-location-length-limits.html
embedded-struct-definitions-forbidden.html
#glsl-2types-of-textures-on-same-unit.html
glsl-function-nodes.html
glsl-long-variable-names.html
@ -9,6 +11,7 @@ shader-with-_webgl-identifier.vert.html
shader-with-arbitrary-indexing.frag.html
shader-with-arbitrary-indexing.vert.html
shader-with-attrib-array.vert.html
shader-with-attrib-struct.vert.html
shader-with-clipvertex.vert.html
shader-with-default-precision.frag.html
shader-with-default-precision.vert.html
@ -49,3 +52,6 @@ shader-with-version-130.vert.html
shader-with-webgl-identifier.vert.html
shader-without-precision.frag.html
shared.html
struct-nesting-exceeds-maximum.html
struct-nesting-under-maximum.html
uniform-location-length-limits.html

View File

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL attrib location length tests</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test.js"> </script>
<script src="../../resources/webgl-test-utils.js"> </script>
</head>
<body>
<canvas id="example" width="50" height="50">
There is supposed to be an example drawing here, but it's not important.
</canvas>
<div id="description">Verify limits on the lengths of attribute locations per WebGL spec, "Maximum Uniform and Attribute Location Lengths".</div>
<div id="console"></div>
<script id="goodVertexShader" type="x-shader/x-vertex">
// A vertex shader where the needed attrib location is exactly 256 characters.
attribute vec4 vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
void main()
{
gl_Position = vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
}
</script>
<script id="badVertexShader" type="x-shader/x-vertex">
// A vertex shader where the needed attrib location is 257 characters.
attribute vec4 vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567;
void main()
{
gl_Position = vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script>
if (window.initNonKhronosFramework) {
window.initNonKhronosFramework(false);
}
description("test attrib location length limit");
var wtu = WebGLTestUtils;
var gl = wtu.create3DContext(document.getElementById("example"));
debug("Test attrib location underneath the length limit");
var program = wtu.loadProgramFromScript(gl, "goodVertexShader", "fragmentShader");
shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true');
var attribLoc = gl.getAttribLocation(program, "vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456");
if (attribLoc == -1) {
testFailed("attrib location was -1, should not be");
} else {
testPassed("attrib location should not be -1");
}
wtu.glErrorShouldBe(gl, gl.NONE);
debug("Test attrib location over the length limit");
debug("Shader compilation should fail");
shouldBe('wtu.loadShaderFromScript(gl, "badVertexShader", gl.VERTEX_SHADER, function (err) {})', 'null');
wtu.glErrorShouldBe(gl, gl.NONE);
debug("Attempt to bind too-long attrib location should produce error");
program = gl.createProgram();
gl.bindAttribLocation(program, 0, "vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567");
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
debug("Attempt to fetch too-long attrib location should produce error");
program = wtu.loadStandardProgram(gl);
shouldBe('gl.getAttribLocation(program, "vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567")', '-1');
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
successfullyParsed = true;
</script>
<script src="../../../resources/js-test-post.js"></script>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!--
Copyright (c) 2011 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL GLSL Conformance Tests</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test-utils.js"></script>
<script src="../../resources/glsl-conformance-test.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script id="vertexShader" type="text/something-not-javascript">
// embedded structure definitions are forbidden per GLSL ES section 4.1.8, "Structures", and should fail
struct nesting1 {
struct nesting2 {
vec4 vector;
} field2;
};
uniform nesting1 uniform1;
void main()
{
gl_Position = uniform1.field2.vector;
}
</script>
<script>
GLSLConformanceTester.runTest();
successfullyParsed = true;
</script>
</body>
</html>

View File

@ -49,10 +49,10 @@ function init()
window.initNonKhronosFramework(false);
}
debug("Tests that using 2 types of textures on the same texture unit");
debug("and referencing them both in the same program fails as per");
debug("OpenGL ES 2.0.24 spec section 2.10.4, Samplers subsection.");
debug("");
description(
"Tests that using 2 types of textures on the same texture unit" +
"and referencing them both in the same program fails as per" +
"OpenGL ES 2.0.24 spec section 2.10.4, Samplers subsection.");
var canvas2d = document.getElementById("canvas2d");
var ctx2d = canvas2d.getContext("2d");

View File

@ -112,6 +112,8 @@ function init()
window.initNonKhronosFramework(false);
}
description("tests function nodes");
var bufFunction = new Uint8Array(width * height * 4);
var bufMacro = new Uint8Array(width * height * 4);

View File

@ -11,7 +11,7 @@
<canvas id="example" width="50" height="50">
There is supposed to be an example drawing here, but it's not important.
</canvas>
<div id="description">Verify that shader long variable names works fine if they are within 256 characters.</div>
<div id="description"></div>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">
attribute vec4 vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
@ -53,6 +53,8 @@
window.initNonKhronosFramework(false);
}
description("Verify that shader long variable names works fine if they are within 256 characters.");
gl = initWebGL("example", "vshader", "fshader", [ "vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"], [ 0, 0, 0, 1 ], 1);
var prog = gl.getParameter(gl.CURRENT_PROGRAM);

View File

@ -0,0 +1,38 @@
<!--
Copyright (c) 2011 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL GLSL Conformance Tests</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test-utils.js"></script>
<script src="../../resources/glsl-conformance-test.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script id="vertexShader" type="text/something-not-javascript">
// vertex shader that uses attribute struct should fail per GLSL ES section 4.4.3, "Attribute", p. 30
struct UserType {
attribute vec4 position;
};
attribute UserType userAttr;
void main()
{
gl_Position = userAttr.position;
}
</script>
<script>
GLSLConformanceTester.runTest();
successfullyParsed = true;
</script>
</body>
</html>

View File

@ -0,0 +1,48 @@
<!--
Copyright (c) 2011 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL GLSL Conformance Tests</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test-utils.js"></script>
<script src="../../resources/glsl-conformance-test.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script id="vertexShader" type="text/something-not-javascript">
// shader with too-deep struct nesting should fail per WebGL spec
struct nesting4 {
vec4 vector;
};
struct nesting3 {
nesting4 field4;
};
struct nesting2 {
nesting3 field3;
};
struct nesting1 {
nesting2 field2;
};
uniform nesting1 uniform1;
void main()
{
gl_Position = uniform1.field2.field3.field4.vector;
}
</script>
<script>
GLSLConformanceTester.runTest();
successfullyParsed = true;
</script>
</body>
</html>

View File

@ -0,0 +1,44 @@
<!--
Copyright (c) 2011 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL GLSL Conformance Tests</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test-utils.js"></script>
<script src="../../resources/glsl-conformance-test.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script id="vertexShader" type="text/something-not-javascript">
// shader with struct nesting less than maximum in WebGL spec should succeed
struct nesting3 {
vec4 vector;
};
struct nesting2 {
nesting3 field3;
};
struct nesting1 {
nesting2 field2;
};
uniform nesting1 uniform1;
void main()
{
gl_Position = uniform1.field2.field3.vector;
}
</script>
<script>
GLSLConformanceTester.runTest();
successfullyParsed = true;
</script>
</body>
</html>

View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL uniform location length tests</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test.js"> </script>
<script src="../../resources/webgl-test-utils.js"> </script>
</head>
<body>
<canvas id="example" width="50" height="50">
There is supposed to be an example drawing here, but it's not important.
</canvas>
<div id="description">Verify limits on the lengths of uniform locations per WebGL spec, "Maximum Uniform and Attribute Location Lengths".</div>
<div id="console"></div>
<script id="goodVertexShader" type="x-shader/x-vertex">
// A vertex shader where the needed uniform location is exactly 256 characters.
struct Nesting2 {
vec4 identifier62CharactersLong_01234567890123456789012345678901234;
};
struct Nesting1 {
Nesting2 identifier64CharactersLong_0123456789012345678901234567890123456;
};
uniform Nesting1 identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789;
void main() {
gl_Position = identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.identifier64CharactersLong_0123456789012345678901234567890123456.identifier62CharactersLong_01234567890123456789012345678901234;
}
</script>
<script id="badVertexShader" type="x-shader/x-vertex">
// A vertex shader where the needed uniform location is 257 characters.
struct Nesting2 {
vec4 identifier63CharactersLong_012345678901234567890123456789012345;
};
struct Nesting1 {
Nesting2 identifier64CharactersLong_0123456789012345678901234567890123456;
};
uniform Nesting1 identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789;
void main() {
Nesting2 temp = identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.identifier64CharactersLong_0123456789012345678901234567890123456;
gl_Position = temp.identifier63CharactersLong_012345678901234567890123456789012345;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script>
if (window.initNonKhronosFramework) {
window.initNonKhronosFramework(false);
}
var wtu = WebGLTestUtils;
var gl = wtu.create3DContext(document.getElementById("example"));
debug("Test uniform location underneath the length limit");
var program = wtu.loadProgramFromScript(gl, "goodVertexShader", "fragmentShader");
shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true');
var uniformLoc = gl.getUniformLocation(program, "identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.identifier64CharactersLong_0123456789012345678901234567890123456.identifier62CharactersLong_01234567890123456789012345678901234");
shouldBeNonNull('uniformLoc');
wtu.glErrorShouldBe(gl, gl.NONE);
debug("Test uniform location over the length limit");
program = wtu.loadProgramFromScript(gl, "badVertexShader", "fragmentShader");
wtu.glErrorShouldBe(gl, gl.NONE);
shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true');
var uniformLoc = gl.getUniformLocation(program, "identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.identifier64CharactersLong_0123456789012345678901234567890123456.identifier63CharactersLong_012345678901234567890123456789012345");
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
shouldBeNull('uniformLoc');
successfullyParsed = true;
</script>
<script src="../../../resources/js-test-post.js"></script>
</body>
</html>

View File

@ -0,0 +1,4 @@
glsl-function-texture2d-bias.html
glsl-function-texture2dlod.html
glsl-function-texture2dproj.html

View File

@ -0,0 +1,104 @@
<!--
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 texture2D GLSL conformance test.</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test.js"> </script>
<script src="../../resources/webgl-test-utils.js"> </script>
</head>
<body>
<canvas id="example" width="256" height="256" style="width: 16px; height: 16px;"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vshader2d" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 texCoord0;
varying vec2 texCoord;
void main() {
gl_Position = vPosition;
texCoord = texCoord0;
}
</script>
<script id="fshader2d" type="x-shader/x-vertex">
precision mediump float;
uniform sampler2D tex;
uniform float bias;
varying vec2 texCoord;
void main() {
gl_FragData[0] = texture2D(tex, texCoord, bias);
}
</script>
<script>
description("tests GLSL texture2D function with bias");
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
shouldBe("canvas.width", "256");
shouldBe("canvas.height", "256");
var gl = wtu.create3DContext(canvas);
var program = wtu.setupProgram(
gl,
[wtu.loadShaderFromScript(gl, 'vshader2d', gl.VERTEX_SHADER),
wtu.loadShaderFromScript(gl, 'fshader2d', gl.FRAGMENT_SHADER)],
['vPosition', 'texCoord0'], [0, 1]);
wtu.setupUnitQuad(gl, 0, 1);
var colors = [
{name: 'red', color:[255, 0, 0, 255]},
{name: 'green', color:[0, 255, 0, 255]},
{name: 'blue', color:[0, 0, 255, 255]},
{name: 'yellow', color:[255, 255, 0, 255]},
{name: 'magenta', color:[255, 0, 255, 255]},
{name: 'cyan', color:[0, 255, 255, 255]},
{name: 'pink', color:[255, 128, 128, 255]},
{name: 'gray', color:[128, 128, 128, 255]},
{name: 'light green', color:[128, 255, 128, 255]},
];
shouldBe("colors.length", "9");
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(
gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
for (var ii = 0; ii < colors.length; ++ii) {
var color = colors[ii];
var size = Math.pow(2, colors.length - ii - 1);
wtu.fillTexture(gl, tex, size, size, color.color, ii);
}
var loc = gl.getUniformLocation(program, "bias");
for (var ii = 0; ii < colors.length; ++ii) {
gl.uniform1f(loc, ii);
var color = colors[ii];
wtu.drawQuad(gl);
wtu.checkCanvas(
gl, color.color,
"256x256 texture drawn to 256x256 dest with bias = " + ii +
" should be " + color.name);
}
glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
successfullyParsed = true;
</script>
<script src="../../../resources/js-test-post.js"></script>
</body>
</html>

View File

@ -0,0 +1,104 @@
<!--
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 texture2D GLSL conformance test.</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test.js"> </script>
<script src="../../resources/webgl-test-utils.js"> </script>
</head>
<body>
<canvas id="example" width="256" height="256" style="width: 16px; height: 16px;"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vshader2d" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 texCoord0;
varying vec4 color;
uniform sampler2D tex;
uniform float lod;
void main() {
gl_Position = vPosition;
color = texture2DLod(tex, texCoord0, lod);
}
</script>
<script id="fshader2d" type="x-shader/x-vertex">
precision mediump float;
varying vec4 color;
void main() {
gl_FragData[0] = color;
}
</script>
<script>
description("tests GLSL texture2DLod function");
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
shouldBe("canvas.width", "256");
shouldBe("canvas.height", "256");
var gl = wtu.create3DContext(canvas);
var program = wtu.setupProgram(
gl,
[wtu.loadShaderFromScript(gl, 'vshader2d', gl.VERTEX_SHADER),
wtu.loadShaderFromScript(gl, 'fshader2d', gl.FRAGMENT_SHADER)],
['vPosition', 'texCoord0'], [0, 1]);
wtu.setupUnitQuad(gl, 0, 1);
var colors = [
{name: 'red', color:[255, 0, 0, 255]},
{name: 'green', color:[0, 255, 0, 255]},
{name: 'blue', color:[0, 0, 255, 255]},
{name: 'yellow', color:[255, 255, 0, 255]},
{name: 'magenta', color:[255, 0, 255, 255]},
{name: 'cyan', color:[0, 255, 255, 255]},
{name: 'pink', color:[255, 128, 128, 255]},
{name: 'gray', color:[128, 128, 128, 255]},
{name: 'light green', color:[128, 255, 128, 255]},
];
shouldBe("colors.length", "9");
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(
gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
for (var ii = 0; ii < colors.length; ++ii) {
var color = colors[ii];
var size = Math.pow(2, colors.length - ii - 1);
wtu.fillTexture(gl, tex, size, size, color.color, ii);
}
var loc = gl.getUniformLocation(program, "lod");
for (var ii = 0; ii < colors.length; ++ii) {
gl.uniform1f(loc, ii);
var color = colors[ii];
wtu.drawQuad(gl);
wtu.checkCanvas(
gl, color.color,
"256x256 texture drawn to 256x256 dest with lod = " + ii +
" should be " + color.name);
}
glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
successfullyParsed = true;
</script>
<script src="../../../resources/js-test-post.js"></script>
</body>
</html>

View File

@ -0,0 +1,120 @@
<!--
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 texture2D GLSL conformance test.</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../resources/js-test-pre.js"></script>
<script src="../../resources/webgl-test.js"> </script>
<script src="../../resources/webgl-test-utils.js"> </script>
</head>
<body>
<canvas id="example" width="32" height="32"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vshader0" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 texCoord0;
varying vec2 texCoord;
void main() {
gl_Position = vPosition;
texCoord = texCoord0;
}
</script>
<script id="fshader0" type="x-shader/x-vertex">
precision mediump float;
uniform sampler2D tex;
uniform float divisor;
varying vec2 texCoord;
void main() {
gl_FragData[0] = texture2DProj(tex, vec3(texCoord, divisor));
}
</script>
<script id="vshader1" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 texCoord0;
varying vec2 texCoord;
void main() {
gl_Position = vPosition;
texCoord = texCoord0;
}
</script>
<script id="fshader1" type="x-shader/x-vertex">
precision mediump float;
uniform sampler2D tex;
uniform float divisor;
varying vec2 texCoord;
void main() {
gl_FragData[0] = texture2DProj(tex, vec4(texCoord, 123.0, divisor));
}
</script>
<script>
description("tests GLSL texture2DProj function with");
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
var gl = wtu.create3DContext(canvas, {antialias: false});
wtu.setupUnitQuad(gl, 0, 1);
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
var c = document.createElement("canvas");
c.width = 16;
c.height = 16;
var ctx = c.getContext("2d");
ctx.fillStyle = "rgb(0,255,0)";
ctx.fillRect(0, 0, 16, 16);
ctx.fillStyle = "rgb(0,0,255)";
ctx.fillRect(0, 0, 8, 8);
ctx.fillRect(8, 8, 8, 8);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, c);
for (var ss = 0; ss < 2; ++ss) {
debug("");
debug(ss ? "testing vec4 version" : "testing vec3 version");
var program = wtu.setupProgram(
gl,
[wtu.loadShaderFromScript(gl, 'vshader' + ss, gl.VERTEX_SHADER),
wtu.loadShaderFromScript(gl, 'fshader' + ss, gl.FRAGMENT_SHADER)],
['vPosition', 'texCoord0'], [0, 1]);
gl.useProgram(program);
var loc = gl.getUniformLocation(program, "divisor");
for (var ii = 0; ii < 3; ++ii) {
var denominator = Math.pow(2, ii);
gl.uniform1f(loc, 1 / denominator);
wtu.drawQuad(gl);
var size = 16 / denominator;
for (var yy = 0; yy < 32; yy += size) {
for (var xx = 0; xx < 32; xx += size) {
var odd = (xx / size + yy / size) % 2;
var color = odd ? [0, 255, 0, 255] : [0, 0, 255, 255];
var msg = "" + xx + ", " + yy + ", " + size + ", " + size + " should be " + (odd ? "green" : "blue");
wtu.checkCanvasRect(gl, xx, yy, size, size, color, msg);
}
}
}
}
glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
successfullyParsed = true;
</script>
<script src="../../../resources/js-test-post.js"></script>
</body>
</html>

View File

@ -45,6 +45,8 @@ found in the LICENSE file.
window.initNonKhronosFramework(false);
}
description("tests gl_FragCoord");
wtu = WebGLTestUtils;
gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);

View File

@ -45,6 +45,8 @@ found in the LICENSE file.
window.initNonKhronosFramework(false);
}
description("tests gl_FrontFacing");
wtu = WebGLTestUtils;
gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);

View File

@ -38,6 +38,7 @@ void main()
}
</script>
<script>
description(document.title);
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
var gl = wtu.create3DContext(canvas);

View File

@ -43,6 +43,7 @@ void main()
}
</script>
<script>
description(document.title);
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
var gl = wtu.create3DContext(canvas);

View File

@ -28,7 +28,7 @@ void main()
<script id="fshader" type="x-shader/x-fragment">
#define NUM_TEXTURES 8 // See spec
precision mediump float;
uniform sampler2D uni[8];
uniform sampler2D uni[NUM_TEXTURES];
void main()
{
vec4 c = vec4(0,0,0,0);
@ -39,6 +39,7 @@ void main()
}
</script>
<script>
description(document.title);
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
var gl = wtu.create3DContext(canvas);
@ -62,7 +63,7 @@ for (var ii = 0; ii < 8; ++ii) {
wtu.drawQuad(gl);
glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
wtu.checkCanvas(gl, [255, 128, 64, 252],
"Should render using all texture units");
"Should render using all texture units", 1);
successfullyParsed = true;

View File

@ -63,6 +63,7 @@ void main()
}
</script>
<script>
description(document.title);
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
var gl = wtu.create3DContext(canvas);

View File

@ -1,91 +1,92 @@
<!--
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 instanceof test.</title>
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../resources/js-test-pre.js"></script>
<script src="../resources/webgl-test.js"> </script>
<script src="../resources/webgl-test-utils.js"> </script>
</head>
<body>
<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">
attribute vec4 vPosition;
varying vec2 texCoord;
void main()
{
gl_Position = vPosition;
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 color;
void main()
{
gl_FragColor = color;
}
</script>
<script>
var wtu = WebGLTestUtils;
debug("Tests that instanceof works on WebGL objects.");
debug("");
var gl = create3DContext(document.getElementById("canvas"));
shouldBeTrue('gl instanceof WebGLRenderingContext');
shouldBeTrue('gl.createBuffer() instanceof WebGLBuffer');
shouldBeTrue('gl.createFramebuffer() instanceof WebGLFramebuffer');
shouldBeTrue('gl.createProgram() instanceof WebGLProgram');
shouldBeTrue('gl.createRenderbuffer() instanceof WebGLRenderbuffer');
shouldBeTrue('gl.createShader(gl.VERTEX_SHADER) instanceof WebGLShader');
shouldBeTrue('gl.createTexture() instanceof WebGLTexture');
var program = wtu.setupProgram(
gl,
[wtu.loadShaderFromScript(gl, 'vshader', gl.VERTEX_SHADER),
wtu.loadShaderFromScript(gl, 'fshader', gl.FRAGMENT_SHADER)],
['vPosition'], [0]);
shouldBeTrue('gl.getUniformLocation(program, "color") instanceof WebGLUniformLocation');
shouldBeTrue('gl.getActiveAttrib(program, 0) instanceof WebGLActiveInfo');
shouldBeTrue('gl.getActiveUniform(program, 0) instanceof WebGLActiveInfo');
debug("");
debug("Tests that those WebGL objects can not be constructed through new operator");
debug("");
function shouldThrowWithNew(objectType, objectName)
{
try {
new objectType;
testFailed('new ' + objectName + ' did not throw');
} catch (e) {
testPassed('new ' + objectName + ' threw an error');
}
}
shouldThrowWithNew(WebGLRenderingContext, 'WebGLRenderingContext');
shouldThrowWithNew(WebGLActiveInfo, 'WebGLActiveInfo');
shouldThrowWithNew(WebGLBuffer, 'WebGLBuffer');
shouldThrowWithNew(WebGLFramebuffer, 'WebGLFramebuffer');
shouldThrowWithNew(WebGLProgram, 'WebGLProgram');
shouldThrowWithNew(WebGLRenderbuffer, 'WebGLRenderbuffer');
shouldThrowWithNew(WebGLShader, 'WebGLShader');
shouldThrowWithNew(WebGLTexture, 'WebGLTexture');
shouldThrowWithNew(WebGLUniformLocation, 'WebGLUniformLocation');
successfullyParsed = true;
</script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
<!--
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 instanceof test.</title>
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../resources/js-test-pre.js"></script>
<script src="../resources/webgl-test.js"> </script>
<script src="../resources/webgl-test-utils.js"> </script>
</head>
<body>
<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">
attribute vec4 vPosition;
varying vec2 texCoord;
void main()
{
gl_Position = vPosition;
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 color;
void main()
{
gl_FragColor = color;
}
</script>
<script>
var wtu = WebGLTestUtils;
description(document.title);
debug("Tests that instanceof works on WebGL objects.");
debug("");
var gl = create3DContext(document.getElementById("canvas"));
shouldBeTrue('gl instanceof WebGLRenderingContext');
shouldBeTrue('gl.createBuffer() instanceof WebGLBuffer');
shouldBeTrue('gl.createFramebuffer() instanceof WebGLFramebuffer');
shouldBeTrue('gl.createProgram() instanceof WebGLProgram');
shouldBeTrue('gl.createRenderbuffer() instanceof WebGLRenderbuffer');
shouldBeTrue('gl.createShader(gl.VERTEX_SHADER) instanceof WebGLShader');
shouldBeTrue('gl.createTexture() instanceof WebGLTexture');
var program = wtu.setupProgram(
gl,
[wtu.loadShaderFromScript(gl, 'vshader', gl.VERTEX_SHADER),
wtu.loadShaderFromScript(gl, 'fshader', gl.FRAGMENT_SHADER)],
['vPosition'], [0]);
shouldBeTrue('gl.getUniformLocation(program, "color") instanceof WebGLUniformLocation');
shouldBeTrue('gl.getActiveAttrib(program, 0) instanceof WebGLActiveInfo');
shouldBeTrue('gl.getActiveUniform(program, 0) instanceof WebGLActiveInfo');
debug("");
debug("Tests that those WebGL objects can not be constructed through new operator");
debug("");
function shouldThrowWithNew(objectType, objectName)
{
try {
new objectType;
testFailed('new ' + objectName + ' did not throw');
} catch (e) {
testPassed('new ' + objectName + ' threw an error');
}
}
shouldThrowWithNew(WebGLRenderingContext, 'WebGLRenderingContext');
shouldThrowWithNew(WebGLActiveInfo, 'WebGLActiveInfo');
shouldThrowWithNew(WebGLBuffer, 'WebGLBuffer');
shouldThrowWithNew(WebGLFramebuffer, 'WebGLFramebuffer');
shouldThrowWithNew(WebGLProgram, 'WebGLProgram');
shouldThrowWithNew(WebGLRenderbuffer, 'WebGLRenderbuffer');
shouldThrowWithNew(WebGLShader, 'WebGLShader');
shouldThrowWithNew(WebGLTexture, 'WebGLTexture');
shouldThrowWithNew(WebGLUniformLocation, 'WebGLUniformLocation');
successfullyParsed = true;
</script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>

View File

@ -10,13 +10,14 @@ found in the LICENSE file.
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../resources/js-test-pre.js"></script>
<script src="../resources/webgl-test.js"></script>
<script src="../resources/webgl-test-utils.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script>
var wtu = WebGLTestUtils;
var gl;
var fbo;
var depthBuffer;
@ -111,6 +112,7 @@ for (width = 0; width <= 2; width += 2)
{
for (height = 0; height <= 2; height += 2)
{
debug("");
debug("Dimensions " + width + " x " + height);
debug("Create renderbuffers");
@ -178,6 +180,209 @@ for (width = 0; width <= 2; width += 2)
}
}
// Determine if we can attach both color and depth or color and depth_stencil
var depthFormat;
var depthAttachment;
function checkValidColorDepthCombination() {
shouldBeNonNull("fbo = gl.createFramebuffer()");
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
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, 16, 16);
shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
return tryDepth(gl.DEPTH_COMPONENT16, gl.DEPTH_ATTACHMENT) || tryDepth(gl.DEPTH_STENCIL, gl.DEPTH_STENCIL_ATTACHMENT);
function tryDepth(try_format, try_attachment) {
if (depthAttachment) {
// If we've tried once unattach the old one.
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, null);
}
depthFormat = try_format;
depthAttachment = try_attachment;
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
glErrorShouldBe(gl, gl.NO_ERROR);
return gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
}
}
if (checkValidColorDepthCombination()) {
testFramebufferIncompleteDimensions();
testFramebufferIncompleteAttachment();
testFramebufferIncompleteMissingAttachment();
testUsingIncompleteFramebuffer();
}
function CheckFramebuffer(expected) {
var actual = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
var msg = "gl.checkFramebufferStatus(gl.FRAMEBUFFER) should be " + wtu.glEnumToString(gl, expected) + " was " + wtu.glEnumToString(gl, actual);
if (expected != gl.FRAMEBUFFER_COMPLETE) {
msg += " or FRAMEBUFFER_UNSUPPORTED";
}
if (actual == expected ||
(expected != gl.FRAMEBUFFER_COMPLETE &&
actual == gl.FRAMBUFFER_UNSUPPORTED)) {
testPassed(msg);
} else {
testFailed(msg);
}
}
function testUsingIncompleteFramebuffer() {
debug("");
debug("Test drawing or reading from an incomplete framebuffer");
var program = wtu.setupTexturedQuad(gl);
var tex = gl.createTexture();
wtu.fillTexture(gl, tex, 1, 1, [0,255,0,255]);
shouldBeNonNull("fbo = gl.createFramebuffer()");
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
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, 16, 16);
shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
glErrorShouldBe(gl, gl.NO_ERROR);
CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
// We pick this combination because it works on desktop OpenGL but should not work on OpenGL ES 2.0
gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 32, 16);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
debug("");
debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
testRenderingAndReading();
shouldBeNonNull("fbo = gl.createFramebuffer()");
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
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");
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_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");
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "clear with incomplete framebuffer");
}
}
function testFramebufferIncompleteAttachment() {
shouldBeNonNull("fbo = gl.createFramebuffer()");
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
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, 16, 16);
CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
debug("");
debug("Wrong storage type for type of attachment be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
debug("");
debug("0 size attachment should be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
glErrorShouldBe(gl, gl.NO_ERROR);
}
function testFramebufferIncompleteMissingAttachment() {
debug("");
debug("No attachments should be INCOMPLETE_FRAMEBUFFER_MISSING_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
shouldBeNonNull("fbo = gl.createFramebuffer()");
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
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, 16, 16);
CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
glErrorShouldBe(gl, gl.NO_ERROR);
}
function testFramebufferIncompleteDimensions() {
debug("");
debug("Attachments of different sizes should be FRAMEBUFFER_INCOMPLETE_DIMENSIONS (OpenGL ES 2.0 4.4.5)");
shouldBeNonNull("fbo = gl.createFramebuffer()");
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
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, 16, 16);
shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
glErrorShouldBe(gl, gl.NO_ERROR);
CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 32, 16);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 32);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
glErrorShouldBe(gl, gl.NO_ERROR);
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
glErrorShouldBe(gl, gl.NO_ERROR);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
return;
}
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
glErrorShouldBe(gl, gl.NO_ERROR);
}
successfullyParsed = true;
</script>

View File

@ -11,6 +11,7 @@ found in the LICENSE file.
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../resources/js-test-pre.js"></script>
<script src="../resources/webgl-test.js"></script>
<script src="../../resources/desktop-gl-constants.js"></script>
</head>
<body>
<div id="description"></div>
@ -97,7 +98,7 @@ if (!gl) {
glErrorShouldBe(gl, gl.NO_ERROR,
"binding a newly created framebuffer should succeed.");
var target = 0x8CA8; // GL_READ_FRAMEBUFFER
var target = desktopGL.READ_FRAMEBUFFER
gl.getFramebufferAttachmentParameter(
target,
gl.COLOR_ATTACHMENT0,
@ -123,7 +124,7 @@ if (!gl) {
glErrorShouldBe(gl, gl.INVALID_ENUM,
"calling framebufferRenderbuffer with target = READ_FRAMEBUFFER should generate INVALID_ENUM.");
var attachment = 0x8CE1; // GL_COLOR_ATTACHMENT1
var attachment = desktopGL.COLOR_ATTACHMENT1
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, fbtex, 0);
glErrorShouldBe(gl, gl.INVALID_ENUM,
"calling framebufferTexImage2D with attachment = COLOR_ATTACHMENT1 should generate INVALID_ENUM.");
@ -133,7 +134,7 @@ if (!gl) {
gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
0x8210); // GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
desktopGL.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
glErrorShouldBe(gl, gl.INVALID_ENUM,
"calling getFramebufferAttachmentParameter with pname = GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING should generate INVALID_ENUM.");

View File

@ -39,6 +39,8 @@ found in the LICENSE file.
window.initNonKhronosFramework(false);
}
description(document.title);
function checkDrawElements(mode, count, type, expect, msg) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(mode, count, type, 0);

View File

@ -72,6 +72,8 @@ There is supposed to be an example drawing here, but it's not important.
window.initNonKhronosFramework(false);
}
description(document.title);
gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
var vertexObject = gl.createBuffer();

View File

@ -591,8 +591,6 @@ var linkProgram = function(gl, program, opt_errorCallback) {
testFailed("Error in program linking:" + error);
gl.deleteProgram(program);
//gl.deleteProgram(fragmentShader);
//gl.deleteProgram(vertexShader);
}
};

View File

@ -46,10 +46,9 @@ function pass(name) {
}
function init() {
debug("There should be 5 red triangles on 5 black squares above");
debug("");
description("This test checks that drawImage and readPixels are not effected by gl.Pixelstorei(gl.PACK_ALIGNMENT) and visa versa");
debug("This test checks that drawImage and readPixels are not effected by gl.Pixelstorei(gl.PACK_ALIGNMENT) and visa versa");
debug("There should be 5 red triangles on 5 black squares above");
debug("");
var canvas3d = document.getElementById("example");

View File

@ -47,7 +47,7 @@ function init()
window.initNonKhronosFramework(false);
}
debug("Tests TexParameter works as expected");
description("Tests TexParameter works as expected");
debug("");
var canvas2d = document.getElementById("canvas2d");

View File

@ -55,10 +55,10 @@ function init()
window.initNonKhronosFramework(false);
}
debug("Tests that binding both TEXTURE_2D and TEXTURE_CUBE_MAP to the same");
debug("active texture unit works as long as they are not used");
debug("simultaneously in the same shader program.");
debug("");
description(
"Tests that binding both TEXTURE_2D and TEXTURE_CUBE_MAP to the same" +
"active texture unit works as long as they are not used" +
"simultaneously in the same shader program.");
var canvas2d = document.getElementById("canvas2d");
var ctx2d = canvas2d.getContext("2d");

View File

@ -38,9 +38,9 @@ function init()
window.initNonKhronosFramework(false);
}
debug("Tests that glActiveTexture and glBindTexture work as expected");
debug("Specifically texture targets are per active texture unit.");
debug("");
description(
"Tests that glActiveTexture and glBindTexture work as expected" +
"Specifically texture targets are per active texture unit.");
var canvas2d = document.getElementById("canvas2d");
var ctx2d = canvas2d.getContext("2d");

View File

@ -25,9 +25,9 @@ function init()
window.initNonKhronosFramework(false);
}
debug("Checks that a texture that is not -texture-complete- does not draw if"+
" filtering needs mips");
debug("");
description(
"Checks that a texture that is not -texture-complete- does not draw if"+
" filtering needs mips");
var canvas2d = document.getElementById("canvas2d");
var ctx2d = canvas2d.getContext("2d");

View File

@ -47,8 +47,7 @@ function init()
window.initNonKhronosFramework(false);
}
debug("Checks mip issues");
debug("");
description("Checks mip issues");
canvas = document.getElementById("example");
shouldBe("canvas.width", "2");

View File

@ -38,6 +38,7 @@ void main()
}
</script>
<script>
description(document.title);
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
var gl = wtu.create3DContext(canvas);

View File

@ -25,9 +25,9 @@ function init()
window.initNonKhronosFramework(false);
}
debug("Tests that only Uniform1i and Uniform1iv can be used to set");
debug("sampler uniforms.");
debug("");
description(
"Tests that only Uniform1i and Uniform1iv can be used to set" +
"sampler uniforms.");
var canvas2d = document.getElementById("canvas2d");

View File

@ -8,3 +8,6 @@ conformance/textures/texture-mips.html
conformance/uniforms/gl-uniform-bool.html
conformance/more/conformance/quickCheckAPI-S_V.html
conformance/more/functions/uniformfArrayLen1.html
conformance/glsl/misc/attrib-location-length-limits.html
conformance/glsl/misc/uniform-location-length-limits.html
conformance/renderbuffers/framebuffer-object-attachment.html

View File

@ -10,3 +10,6 @@ conformance/textures/texture-npot.html
conformance/more/conformance/quickCheckAPI-S_V.html
conformance/more/functions/uniformfBadArgs.html
conformance/more/functions/uniformiBadArgs.html
conformance/glsl/misc/attrib-location-length-limits.html
conformance/glsl/misc/uniform-location-length-limits.html
conformance/renderbuffers/framebuffer-object-attachment.html

View File

@ -7,3 +7,7 @@ conformance/glsl/misc/shader-with-256-character-identifier.frag.html
conformance/glsl/misc/shader-with-long-line.html
conformance/more/conformance/quickCheckAPI-S_V.html
conformance/more/functions/uniformfArrayLen1.html
conformance/glsl/misc/attrib-location-length-limits.html
conformance/glsl/misc/struct-nesting-under-maximum.html
conformance/glsl/misc/uniform-location-length-limits.html
conformance/renderbuffers/framebuffer-object-attachment.html

View File

@ -211,10 +211,14 @@ function start() {
Reporter.prototype.startPage = function(url) {
dump('WebGL mochitest: starting page ' + url + '\n');
if (kIsLinux) {
dump('Calling garbageCollect()\n');
SpecialPowers.DOMWindowUtils.garbageCollect();
}
// Calling garbageCollect before each test page fixes intermittent failures with
// out-of-memory errors, often failing to create a WebGL context.
// The explanation is that the JS engine keeps unreferenced WebGL contexts around
// for too long before GCing (bug 617453), so that during this mochitest dozens of unreferenced
// WebGL contexts can accumulate at a given time.
SpecialPowers.DOMWindowUtils.garbageCollect();
var page = this.pagesByURL[url];
this.currentPage = page;
statusTextNode.textContent = 'Running URL: ' + url;

View File

@ -294,6 +294,12 @@ function start() {
this.contextInfo["ALPHA_BITS"] = ctx.getParameter(ctx.ALPHA_BITS);
this.contextInfo["DEPTH_BITS"] = ctx.getParameter(ctx.DEPTH_BITS);
this.contextInfo["STENCIL_BITS"] = ctx.getParameter(ctx.STENCIL_BITS);
var ext = ctx.getExtension("WEBGL_debug_renderer_info");
if (ext) {
this.contextInfo["UNMASKED_VENDOR"] = ctx.getParameter(ext.UNMASKED_VENDOR_WEBGL);
this.contextInfo["UNMASKED_RENDERER"] = ctx.getParameter(ext.UNMASKED_RENDERER_WEBGL);
}
}
};
@ -364,6 +370,8 @@ function start() {
tx += "WebGL VENDOR: " + this.contextInfo["VENDOR"] + "\n";
tx += "WebGL VERSION: " + this.contextInfo["VERSION"] + "\n";
tx += "WebGL RENDERER: " + this.contextInfo["RENDERER"] + "\n";
tx += "Unmasked VENDOR: " + this.contextInfo["UNMASKED_VENDOR"] + "\n";
tx += "Unmasked RENDERER: " + this.contextInfo["UNMASKED_RENDERER"] + "\n";
tx += "WebGL R/G/B/A/Depth/Stencil bits (default config): " + this.contextInfo["RED_BITS"] + "/" + this.contextInfo["GREEN_BITS"] + "/" + this.contextInfo["BLUE_BITS"] + "/" + this.contextInfo["ALPHA_BITS"] + "/" + this.contextInfo["DEPTH_BITS"] + "/" + this.contextInfo["STENCIL_BITS"] + "\n";
tx += "\n";
tx += "-------------------\n\n";

View File

@ -95,6 +95,21 @@ SVGPathSegListSMILType::IsEqual(const nsSMILValue& aLeft,
*static_cast<const SVGPathDataAndOwner*>(aRight.mU.mPtr);
}
static bool
ArcFlagsDiffer(SVGPathDataAndOwner::const_iterator aPathData1,
SVGPathDataAndOwner::const_iterator aPathData2)
{
NS_ABORT_IF_FALSE
(SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData1[0])),
"ArcFlagsDiffer called with non-arc segment");
NS_ABORT_IF_FALSE
(SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData2[0])),
"ArcFlagsDiffer called with non-arc segment");
return aPathData1[LARGE_ARC_FLAG_IDX] != aPathData2[LARGE_ARC_FLAG_IDX] ||
aPathData1[SWEEP_FLAG_IDX] != aPathData2[SWEEP_FLAG_IDX];
}
enum PathInterpolationResult {
eCannotInterpolate,
eRequiresConversion,
@ -124,6 +139,12 @@ CanInterpolate(const SVGPathDataAndOwner& aStart,
PRUint32 startType = SVGPathSegUtils::DecodeType(*pStart);
PRUint32 endType = SVGPathSegUtils::DecodeType(*pEnd);
if (SVGPathSegUtils::IsArcType(startType) &&
SVGPathSegUtils::IsArcType(endType) &&
ArcFlagsDiffer(pStart, pEnd)) {
return eCannotInterpolate;
}
if (startType != endType) {
if (!SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType)) {
return eCannotInterpolate;
@ -194,22 +215,25 @@ AddWeightedPathSegs(double aCoeff1,
NS_ABORT_IF_FALSE(!aSeg1 || SVGPathSegUtils::DecodeType(*aSeg1) == segType,
"unexpected segment type");
// FIRST: Directly copy the arguments that don't make sense to add.
aResultSeg[0] = aSeg2[0]; // encoded segment type
// FIRST: Add all the arguments.
bool isArcType = SVGPathSegUtils::IsArcType(segType);
if (isArcType) {
// Copy boolean arc flags.
NS_ABORT_IF_FALSE(!aSeg1 || !ArcFlagsDiffer(aSeg1, aSeg2),
"Expecting arc flags to match");
aResultSeg[LARGE_ARC_FLAG_IDX] = aSeg2[LARGE_ARC_FLAG_IDX];
aResultSeg[SWEEP_FLAG_IDX] = aSeg2[SWEEP_FLAG_IDX];
}
// SECOND: Add the arguments that are supposed to be added.
// (The 1's below are to account for segment type)
PRUint32 numArgs = SVGPathSegUtils::ArgCountForType(segType);
for (PRUint32 i = 1; i < 1 + numArgs; ++i) {
aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i];
}
// SECOND: ensure non-zero flags become 1.
if (SVGPathSegUtils::IsArcType(segType)) {
if (aResultSeg[LARGE_ARC_FLAG_IDX] != 0.0f) {
aResultSeg[LARGE_ARC_FLAG_IDX] = 1.0f;
}
if (aResultSeg[SWEEP_FLAG_IDX] != 0.0f) {
aResultSeg[SWEEP_FLAG_IDX] = 1.0f;
// Need to skip arc flags for arc-type segments. (already handled them)
if (!(isArcType && (i == LARGE_ARC_FLAG_IDX || i == SWEEP_FLAG_IDX))) {
aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i];
}
}

View File

@ -138,10 +138,10 @@ var gSuffixes = {
[40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
QQ: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
qq: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
AA: [[10, 20, 30, 1, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
[35, 45, 55, 1, 0, 65, 75], [45, 65, 85, 1, 0, 105, 125]],
aa: [[10, 20, 30, 0, 1, 40, 50], [60, 70, 80, 0, 0, 90, 100],
[35, 45, 55, 0, 1, 65, 75], [45, 65, 85, 0, 1, 105, 125]],
AA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
[35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
[35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
HH: [[10], [20], [15], [25]],
hh: [[10], [20], [15], [25]],
VV: [[10], [20], [15], [25]],
@ -286,6 +286,17 @@ function run()
}
}
// Test that differences in arc flag parameters cause the
// interpolation/addition not to occur.
addTest(1, "M100,100",
"A", [10, 20, 30, 0, 0, 40, 50],
"a", [60, 70, 80, 0, 1, 90, 100],
"a", [60, 70, 80, 0, 1, 90, 100], additive);
addTest(1, "M100,100",
"A", [10, 20, 30, 0, 0, 40, 50],
"a", [60, 70, 80, 1, 0, 90, 100],
"a", [60, 70, 80, 1, 0, 90, 100], additive);
// Test all pairs of segment types that cannot be interpolated between.
for each (let fromType in gTypes) {
let fromArguments = generatePathSegmentArguments(fromType, 0);

View File

@ -9366,25 +9366,27 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
// If we're running pending timeouts because they've been temporarily
// disabled (!aTimeout), set the next interval to be relative to "now",
// and not to when the timeout that was pending should have fired. Also
// check if the next interval timeout is overdue. If so, then restart
// the interval from now.
// and not to when the timeout that was pending should have fired.
TimeStamp firingTime;
if (!aTimeout || timeout->mWhen + nextInterval <= now)
if (!aTimeout)
firingTime = now + nextInterval;
else
firingTime = timeout->mWhen + nextInterval;
TimeDuration delay = firingTime - TimeStamp::Now();
TimeStamp currentNow = TimeStamp::Now();
TimeDuration delay = firingTime - currentNow;
// And make sure delay is nonnegative; that might happen if the timer
// thread is firing our timers somewhat early.
// thread is firing our timers somewhat early or if they're taking a long
// time to run the callback.
if (delay < TimeDuration(0)) {
delay = TimeDuration(0);
}
if (timeout->mTimer) {
timeout->mWhen = firingTime;
timeout->mWhen = currentNow + delay; // firingTime unless delay got
// clamped, in which case it's
// currentNow.
// Reschedule the OS timer. Don't bother returning any error
// codes if this fails since the callers of this method

View File

@ -559,9 +559,7 @@ TransactionPoolEventTarget::Dispatch(nsIRunnable* aRunnable,
NS_ASSERTION(aRunnable, "Null pointer!");
NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "Unsupported!");
TransactionThreadPool* pool = TransactionThreadPool::Get();
NS_ASSERTION(pool, "This should never be null!");
TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
return pool->Dispatch(mTransaction, aRunnable, false, nsnull);
}

View File

@ -206,7 +206,7 @@ CheckPermissionsHelper::Observe(nsISupports* aSubject,
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never be null!");
rv = mgr->WaitForOpenAllowed(mName, mASCIIOrigin, this);
rv = NS_DispatchToCurrentThread(this);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;

View File

@ -64,25 +64,21 @@ public:
CheckPermissionsHelper(OpenDatabaseHelper* aHelper,
nsIDOMWindow* aWindow,
const nsAString& aName,
const nsACString& aASCIIOrigin)
: mHelper(aHelper),
mWindow(aWindow),
mName(aName),
mASCIIOrigin(aASCIIOrigin),
mHasPrompted(false),
mPromptResult(0)
{
NS_ASSERTION(aHelper, "Null pointer!");
NS_ASSERTION(aWindow, "Null pointer!");
NS_ASSERTION(!aName.IsEmpty(), "Empty name!");
NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
}
private:
nsRefPtr<OpenDatabaseHelper> mHelper;
nsCOMPtr<nsIDOMWindow> mWindow;
nsString mName;
nsCString mASCIIOrigin;
bool mHasPrompted;
PRUint32 mPromptResult;

View File

@ -61,7 +61,7 @@ struct DatabaseInfoHash
nsAutoPtr<ObjectStoreInfoHash> objectStoreHash;
};
typedef nsClassHashtable<nsUint32HashKey, DatabaseInfoHash>
typedef nsClassHashtable<nsISupportsHashKey, DatabaseInfoHash>
DatabaseHash;
DatabaseHash* gDatabaseHash = nsnull;
@ -83,8 +83,7 @@ EnumerateObjectStoreNames(const nsAString& aKey,
#ifdef NS_BUILD_REFCNT_LOGGING
DatabaseInfo::DatabaseInfo()
: id(0),
nextObjectStoreId(1),
: nextObjectStoreId(1),
nextIndexId(1),
runningVersionChange(false)
{
@ -135,7 +134,7 @@ IndexUpdateInfo::~IndexUpdateInfo()
// static
bool
DatabaseInfo::Get(PRUint32 aId,
DatabaseInfo::Get(nsIAtom* aId,
DatabaseInfo** aInfo)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@ -187,7 +186,7 @@ DatabaseInfo::Put(DatabaseInfo* aInfo)
// static
void
DatabaseInfo::Remove(PRUint32 aId)
DatabaseInfo::Remove(nsIAtom* aId)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(Get(aId, nsnull), "Don't know anything about this one!");
@ -241,7 +240,7 @@ DatabaseInfo::ContainsStoreName(const nsAString& aName)
// static
bool
ObjectStoreInfo::Get(PRUint32 aDatabaseId,
ObjectStoreInfo::Get(nsIAtom* aDatabaseId,
const nsAString& aName,
ObjectStoreInfo** aInfo)
{
@ -297,7 +296,7 @@ ObjectStoreInfo::Put(ObjectStoreInfo* aInfo)
// static
void
ObjectStoreInfo::Remove(PRUint32 aDatabaseId,
ObjectStoreInfo::Remove(nsIAtom* aDatabaseId,
const nsAString& aName)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");

View File

@ -54,23 +54,23 @@ struct DatabaseInfo
~DatabaseInfo();
#else
DatabaseInfo()
: id(0), nextObjectStoreId(1), nextIndexId(1), runningVersionChange(false)
: nextObjectStoreId(1), nextIndexId(1), runningVersionChange(false)
{ }
#endif
static bool Get(PRUint32 aId,
static bool Get(nsIAtom* aId,
DatabaseInfo** aInfo);
static bool Put(DatabaseInfo* aInfo);
static void Remove(PRUint32 aId);
static void Remove(nsIAtom* aId);
bool GetObjectStoreNames(nsTArray<nsString>& aNames);
bool ContainsStoreName(const nsAString& aName);
nsString name;
PRUint64 version;
PRUint32 id;
nsIAtom* id;
nsString filePath;
PRInt64 nextObjectStoreId;
PRInt64 nextIndexId;
@ -106,20 +106,20 @@ struct ObjectStoreInfo
: id(0), autoIncrement(false), databaseId(0) { }
#endif
static bool Get(PRUint32 aDatabaseId,
static bool Get(nsIAtom* aDatabaseId,
const nsAString& aName,
ObjectStoreInfo** aInfo);
static bool Put(ObjectStoreInfo* aInfo);
static void Remove(PRUint32 aDatabaseId,
static void Remove(nsIAtom* aDatabaseId,
const nsAString& aName);
nsString name;
PRInt64 id;
nsString keyPath;
bool autoIncrement;
PRUint32 databaseId;
nsIAtom* databaseId;
nsTArray<IndexInfo> indexes;
};

View File

@ -141,7 +141,7 @@ NS_STACK_CLASS
class AutoRemoveObjectStore
{
public:
AutoRemoveObjectStore(PRUint32 aId, const nsAString& aName)
AutoRemoveObjectStore(nsIAtom* aId, const nsAString& aName)
: mId(aId), mName(aName)
{ }
@ -158,7 +158,7 @@ public:
}
private:
PRUint32 mId;
nsCOMPtr<nsIAtom> mId;
nsString mName;
};
@ -450,10 +450,6 @@ IDBDatabase::ExitSetVersionTransaction()
NS_ASSERTION(dbInfo->runningVersionChange, "How did that happen?");
dbInfo->runningVersionChange = false;
IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
NS_ASSERTION(manager, "We should always have a manager here");
manager->UnblockSetVersionRunnable(this);
}
void

View File

@ -83,7 +83,7 @@ public:
// nsIDOMEventTarget
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
PRUint32 Id()
nsIAtom* Id()
{
return mDatabaseId;
}
@ -144,7 +144,7 @@ private:
void OnUnlink();
PRUint32 mDatabaseId;
nsCOMPtr<nsIAtom> mDatabaseId;
nsString mName;
nsString mFilePath;
nsCString mASCIIOrigin;

View File

@ -214,7 +214,7 @@ IDBFactory::GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
// static
nsresult
IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
PRUint32 aDatabaseId,
nsIAtom* aDatabaseId,
PRUint64* aVersion,
ObjectStoreInfoArray& aObjectStores)
{
@ -381,11 +381,12 @@ NS_IMETHODIMP
IDBFactory::Open(const nsAString& aName,
PRInt64 aVersion,
JSContext* aCx,
PRUint8 aOptionalArgCount,
nsIIDBOpenDBRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (aVersion < 1) {
if (aVersion < 1 && aOptionalArgCount) {
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
@ -436,13 +437,16 @@ IDBFactory::Open(const nsAString& aName,
nsRefPtr<OpenDatabaseHelper> openHelper =
new OpenDatabaseHelper(request, aName, origin, aVersion);
rv = openHelper->Init();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<CheckPermissionsHelper> permissionHelper =
new CheckPermissionsHelper(openHelper, window, aName, origin);
new CheckPermissionsHelper(openHelper, window, origin);
nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::GetOrCreate();
NS_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = mgr->WaitForOpenAllowed(aName, origin, permissionHelper);
rv = mgr->WaitForOpenAllowed(origin, openHelper->Id(), permissionHelper);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
request.forget(_retval);

View File

@ -49,6 +49,7 @@
#include "nsXULAppAPI.h"
class nsPIDOMWindow;
class nsIAtom;
BEGIN_INDEXEDDB_NAMESPACE
@ -84,7 +85,7 @@ public:
static nsresult
LoadDatabaseInformation(mozIStorageConnection* aConnection,
PRUint32 aDatabaseId,
nsIAtom* aDatabaseId,
PRUint64* aVersion,
ObjectStoreInfoArray& aObjectStores);

View File

@ -364,7 +364,7 @@ NS_STACK_CLASS
class AutoRemoveIndex
{
public:
AutoRemoveIndex(PRUint32 aDatabaseId,
AutoRemoveIndex(nsIAtom* aDatabaseId,
const nsAString& aObjectStoreName,
const nsAString& aIndexName)
: mDatabaseId(aDatabaseId), mObjectStoreName(aObjectStoreName),
@ -392,7 +392,7 @@ public:
}
private:
PRUint32 mDatabaseId;
nsCOMPtr<nsIAtom> mDatabaseId;
nsString mObjectStoreName;
nsString mIndexName;
};

View File

@ -194,7 +194,7 @@ private:
nsString mName;
nsString mKeyPath;
bool mAutoIncrement;
PRUint32 mDatabaseId;
nsCOMPtr<nsIAtom> mDatabaseId;
PRUint32 mStructuredCloneVersion;
nsTArray<nsRefPtr<IDBIndex> > mCreatedIndexes;

View File

@ -143,134 +143,6 @@ EnumerateToTArray(const nsACString& aKey,
return PL_DHASH_NEXT;
}
// Responsible for calling IDBDatabase.setVersion after a pending version change
// transaction has completed.
class DelayedSetVersion : public nsRunnable
{
public:
DelayedSetVersion(IDBDatabase* aDatabase,
IDBOpenDBRequest* aRequest,
PRInt64 aOldVersion,
PRInt64 aNewVersion,
AsyncConnectionHelper* aHelper)
: mDatabase(aDatabase),
mRequest(aRequest),
mOldVersion(aOldVersion),
mNewVersion(aNewVersion),
mHelper(aHelper)
{
NS_ASSERTION(aDatabase, "Null database!");
NS_ASSERTION(aRequest, "Null request!");
NS_ASSERTION(aHelper, "Null helper!");
}
NS_IMETHOD Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never be null!");
nsresult rv = mgr->SetDatabaseVersion(mDatabase, mRequest,
mOldVersion, mNewVersion,
mHelper);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
private:
nsRefPtr<IDBDatabase> mDatabase;
nsRefPtr<IDBOpenDBRequest> mRequest;
PRInt64 mOldVersion;
PRInt64 mNewVersion;
nsRefPtr<AsyncConnectionHelper> mHelper;
};
// Responsible for firing "versionchange" events at all live and non-closed
// databases, and for firing a "blocked" event at the requesting database if any
// databases fail to close.
class VersionChangeEventsRunnable : public nsRunnable
{
public:
VersionChangeEventsRunnable(
IDBDatabase* aRequestingDatabase,
IDBOpenDBRequest* aRequest,
nsTArray<nsRefPtr<IDBDatabase> >& aWaitingDatabases,
PRInt64 aOldVersion,
PRInt64 aNewVersion)
: mRequestingDatabase(aRequestingDatabase),
mRequest(aRequest),
mOldVersion(aOldVersion),
mNewVersion(aNewVersion)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aRequestingDatabase, "Null pointer!");
NS_ASSERTION(aRequest, "Null pointer!");
if (!mWaitingDatabases.SwapElements(aWaitingDatabases)) {
NS_ERROR("This should never fail!");
}
}
NS_IMETHOD Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Fire version change events at all of the databases that are not already
// closed. Also kick bfcached documents out of bfcache.
for (PRUint32 index = 0; index < mWaitingDatabases.Length(); index++) {
nsRefPtr<IDBDatabase>& database = mWaitingDatabases[index];
if (database->IsClosed()) {
continue;
}
// First check if the document the IDBDatabase is part of is bfcached
nsCOMPtr<nsIDocument> ownerDoc = database->GetOwnerDocument();
nsIBFCacheEntry* bfCacheEntry;
if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) {
bfCacheEntry->RemoveFromBFCacheSync();
NS_ASSERTION(database->IsClosed(),
"Kicking doc out of bfcache should have closed database");
continue;
}
// Otherwise fire a versionchange event.
nsRefPtr<nsDOMEvent> event =
IDBVersionChangeEvent::Create(mOldVersion, mNewVersion);
NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
bool dummy;
database->DispatchEvent(event, &dummy);
}
// Now check to see if any didn't close. If there are some running still
// then fire the blocked event.
for (PRUint32 index = 0; index < mWaitingDatabases.Length(); index++) {
if (!mWaitingDatabases[index]->IsClosed()) {
nsRefPtr<nsDOMEvent> event =
IDBVersionChangeEvent::CreateBlocked(mOldVersion, mNewVersion);
NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
bool dummy;
mRequest->DispatchEvent(event, &dummy);
break;
}
}
return NS_OK;
}
private:
nsRefPtr<IDBDatabase> mRequestingDatabase;
nsRefPtr<IDBOpenDBRequest> mRequest;
nsTArray<nsRefPtr<IDBDatabase> > mWaitingDatabases;
PRInt64 mOldVersion;
PRInt64 mNewVersion;
};
} // anonymous namespace
IndexedDatabaseManager::IndexedDatabaseManager()
@ -425,20 +297,6 @@ IndexedDatabaseManager::UnregisterDatabase(IDBDatabase* aDatabase)
NS_ERROR("Didn't know anything about this database!");
}
void
IndexedDatabaseManager::OnOriginClearComplete(OriginClearRunnable* aRunnable)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aRunnable, "Null pointer!");
NS_ASSERTION(!aRunnable->mThread, "Thread should be null!");
NS_ASSERTION(aRunnable->mDelayedRunnables.IsEmpty(),
"Delayed runnables should have been dispatched already!");
if (!mOriginClearRunnables.RemoveElement(aRunnable)) {
NS_ERROR("Don't know anything about this runnable!");
}
}
void
IndexedDatabaseManager::OnUsageCheckComplete(AsyncUsageRunnable* aRunnable)
{
@ -452,64 +310,136 @@ IndexedDatabaseManager::OnUsageCheckComplete(AsyncUsageRunnable* aRunnable)
}
}
void
IndexedDatabaseManager::OnSetVersionRunnableComplete(
SetVersionRunnable* aRunnable)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aRunnable, "Null pointer!");
NS_ASSERTION(aRunnable->mDelayedRunnables.IsEmpty(),
"Delayed runnables should have been dispatched already!");
// Remove this runnable from the list. This will allow other databases to
// begin to request version changes.
if (!mSetVersionRunnables.RemoveElement(aRunnable)) {
NS_ERROR("Don't know anything about this runnable!");
}
}
nsresult
IndexedDatabaseManager::WaitForOpenAllowed(const nsAString& aName,
const nsACString& aOrigin,
IndexedDatabaseManager::WaitForOpenAllowed(const nsACString& aOrigin,
nsIAtom* aId,
nsIRunnable* aRunnable)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aName.IsEmpty(), "Empty name!");
NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
NS_ASSERTION(aRunnable, "Null pointer!");
// See if we're currently clearing database files for this origin. If so then
// queue the runnable for later dispatch after we're done clearing.
PRUint32 count = mOriginClearRunnables.Length();
nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOrigin, aId));
// See if this runnable needs to wait.
bool delayed = false;
for (PRUint32 index = mSynchronizedOps.Length(); index > 0; index--) {
nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
if (op->MustWaitFor(*existingOp)) {
existingOp->DelayRunnable(aRunnable);
delayed = true;
break;
}
}
// Otherwise, dispatch it immediately.
if (!delayed) {
nsresult rv = NS_DispatchToCurrentThread(aRunnable);
NS_ENSURE_SUCCESS(rv, rv);
}
// Adding this to the synchronized ops list will block any additional
// ops from proceeding until this one is done.
mSynchronizedOps.AppendElement(op.forget());
return NS_OK;
}
void
IndexedDatabaseManager::AllowNextSynchronizedOp(const nsACString& aOrigin,
nsIAtom* aId)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
PRUint32 count = mSynchronizedOps.Length();
for (PRUint32 index = 0; index < count; index++) {
nsRefPtr<OriginClearRunnable>& data = mOriginClearRunnables[index];
if (data->mOrigin == aOrigin) {
nsCOMPtr<nsIRunnable>* newPtr =
data->mDelayedRunnables.AppendElement(aRunnable);
NS_ENSURE_TRUE(newPtr, NS_ERROR_OUT_OF_MEMORY);
nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
if (op->mOrigin.Equals(aOrigin)) {
if (op->mId == aId) {
NS_ASSERTION(op->mDatabases.IsEmpty(), "How did this happen?");
return NS_OK;
op->DispatchDelayedRunnables();
mSynchronizedOps.RemoveElementAt(index);
return;
}
// If one or the other is for an origin clear, we should have matched
// solely on origin.
NS_ASSERTION(op->mId && aId, "Why didn't we match earlier?");
}
}
// Check to see if we're currently doing a SetVersion transaction for this
// database. If so then we delay this runnable for later.
for (PRUint32 index = 0; index < mSetVersionRunnables.Length(); index++) {
nsRefPtr<SetVersionRunnable>& runnable = mSetVersionRunnables[index];
if (runnable->mRequestingDatabase->Name() == aName &&
runnable->mRequestingDatabase->Origin() == aOrigin) {
nsCOMPtr<nsIRunnable>* newPtr =
runnable->mDelayedRunnables.AppendElement(aRunnable);
NS_ENSURE_TRUE(newPtr, NS_ERROR_OUT_OF_MEMORY);
NS_NOTREACHED("Why didn't we find a SynchronizedOp?");
}
return NS_OK;
nsresult
IndexedDatabaseManager::AcquireExclusiveAccess(const nsACString& aOrigin,
IDBDatabase* aDatabase,
AsyncConnectionHelper* aHelper,
WaitingOnDatabasesCallback aCallback,
void* aClosure)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aHelper, "Why are you talking to me?");
// Find the right SynchronizedOp.
SynchronizedOp* op = nsnull;
PRUint32 count = mSynchronizedOps.Length();
for (PRUint32 index = 0; index < count; index++) {
SynchronizedOp* currentop = mSynchronizedOps[index].get();
if (currentop->mOrigin.Equals(aOrigin)) {
if (!currentop->mId ||
(aDatabase && currentop->mId == aDatabase->Id())) {
// We've found the right one.
NS_ASSERTION(!currentop->mHelper,
"SynchronizedOp already has a helper?!?");
op = currentop;
break;
}
}
}
// We aren't currently clearing databases for this origin and we're not
// running a SetVersion transaction for this database so dispatch the runnable
// immediately.
return NS_DispatchToCurrentThread(aRunnable);
NS_ASSERTION(op, "We didn't find a SynchronizedOp?");
nsTArray<IDBDatabase*>* array;
mLiveDatabases.Get(aOrigin, &array);
// We need to wait for the databases to go away.
// Hold on to all database objects that represent the same database file
// (except the one that is requesting this version change).
nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
if (array) {
PRUint32 count = array->Length();
for (PRUint32 index = 0; index < count; index++) {
IDBDatabase*& database = array->ElementAt(index);
if (!database->IsClosed() &&
(!aDatabase ||
(aDatabase &&
database != aDatabase &&
database->Id() == aDatabase->Id()))) {
liveDatabases.AppendElement(database);
}
}
}
if (liveDatabases.IsEmpty()) {
IndexedDatabaseManager::DispatchHelper(aHelper);
return NS_OK;
}
NS_ASSERTION(op->mDatabases.IsEmpty(), "How do we already have databases here?");
op->mDatabases.AppendElements(liveDatabases);
op->mHelper = aHelper;
// Give our callback the databases so it can decide what to do with them.
aCallback(liveDatabases, aClosure);
NS_ASSERTION(liveDatabases.IsEmpty(),
"Should have done something with the array!");
return NS_OK;
}
// static
@ -519,107 +449,6 @@ IndexedDatabaseManager::IsShuttingDown()
return !!gShutdown;
}
nsresult
IndexedDatabaseManager::SetDatabaseVersion(IDBDatabase* aDatabase,
IDBOpenDBRequest* aRequest,
PRInt64 aOldVersion,
PRInt64 aNewVersion,
AsyncConnectionHelper* aHelper)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDatabase, "Null pointer!");
NS_ASSERTION(aHelper, "Null pointer!");
nsresult rv;
// See if another database has already asked to change the version.
for (PRUint32 index = 0; index < mSetVersionRunnables.Length(); index++) {
nsRefPtr<SetVersionRunnable>& runnable = mSetVersionRunnables[index];
if (runnable->mRequestingDatabase->Id() == aDatabase->Id()) {
if (runnable->mRequestingDatabase == aDatabase) {
// Same database, just queue this call to run after the current
// SetVersion transaction completes.
nsRefPtr<DelayedSetVersion> delayed =
new DelayedSetVersion(aDatabase, aRequest, aOldVersion, aNewVersion,
aHelper);
if (!runnable->mDelayedRunnables.AppendElement(delayed)) {
NS_WARNING("Out of memory!");
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
// Different database, we can't let this one succeed.
aHelper->SetError(NS_ERROR_DOM_INDEXEDDB_DEADLOCK_ERR);
rv = NS_DispatchToCurrentThread(aHelper);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
}
// Grab all live databases for the same origin.
nsTArray<IDBDatabase*>* array;
if (!mLiveDatabases.Get(aDatabase->Origin(), &array)) {
NS_ERROR("Must have some alive if we've got a live argument!");
}
// Hold on to all database objects that represent the same database file
// (except the one that is requesting this version change).
nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
for (PRUint32 index = 0; index < array->Length(); index++) {
IDBDatabase*& database = array->ElementAt(index);
if (database != aDatabase &&
database->Id() == aDatabase->Id() &&
!database->IsClosed() &&
!liveDatabases.AppendElement(database)) {
NS_WARNING("Out of memory?");
return NS_ERROR_OUT_OF_MEMORY;
}
}
// Adding an element to this array here will keep other databases from
// requesting a version change.
nsRefPtr<SetVersionRunnable> runnable =
new SetVersionRunnable(aDatabase, liveDatabases);
if (!mSetVersionRunnables.AppendElement(runnable)) {
NS_WARNING("Out of memory!");
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ASSERTION(liveDatabases.IsEmpty(), "Should have swapped!");
// When all databases are closed we want to dispatch the SetVersion
// transaction to the transaction pool.
runnable->mHelper = aHelper;
if (runnable->mDatabases.IsEmpty()) {
// There are no other databases that need to be closed. Go ahead and run
// the transaction now.
RunSetVersionTransaction(aDatabase);
}
else {
// Otherwise we need to wait for all the other databases to complete.
// Schedule a version change events runnable .
nsTArray<nsRefPtr<IDBDatabase> > waitingDatabases;
if (!waitingDatabases.AppendElements(runnable->mDatabases)) {
NS_WARNING("Out of memory!");
return NS_ERROR_OUT_OF_MEMORY;
}
nsRefPtr<VersionChangeEventsRunnable> eventsRunnable =
new VersionChangeEventsRunnable(aDatabase, aRequest, waitingDatabases,
aOldVersion, aNewVersion);
rv = NS_DispatchToCurrentThread(eventsRunnable);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
void
IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
{
@ -676,71 +505,49 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase)
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDatabase, "Null pointer!");
// Check through the list of SetVersionRunnables we have amassed to see if
// this database is part of a SetVersion callback.
for (PRUint32 index = 0; index < mSetVersionRunnables.Length(); index++) {
nsRefPtr<SetVersionRunnable>& runnable = mSetVersionRunnables[index];
// Check through the list of SynchronizedOps to see if any are waiting for
// this database to close before proceeding.
PRUint32 count = mSynchronizedOps.Length();
for (PRUint32 index = 0; index < count; index++) {
nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
if (runnable->mRequestingDatabase->Id() == aDatabase->Id()) {
// This is the SetVersionRunnable for the given database file. Remove the
// database from the list of databases that need to be closed. Since we
// use this hook for SetVersion requests that don't actually need to wait
// for other databases the mDatabases array may be empty.
if (!runnable->mDatabases.IsEmpty() &&
!runnable->mDatabases.RemoveElement(aDatabase)) {
NS_ERROR("Didn't have this database in our list!");
}
if (op->mOrigin == aDatabase->Origin() &&
(op->mId == aDatabase->Id() || !op->mId)) {
// This database is in the scope of this SynchronizedOp. Remove it
// from the list if necessary.
if (op->mDatabases.RemoveElement(aDatabase)) {
// Now set up the helper if there are no more live databases.
NS_ASSERTION(op->mHelper, "How did we get rid of the helper before "
"removing the last database?");
if (op->mDatabases.IsEmpty()) {
// At this point, all databases are closed, so no new transactions
// can be started. There may, however, still be outstanding
// transactions that have not completed. We need to wait for those
// before we dispatch the helper.
// Now run the helper if there are no more live databases.
if (runnable->mHelper && runnable->mDatabases.IsEmpty()) {
// At this point, all databases are closed, so no new transactions can
// be started. There may, however, still be outstanding transactions
// that have not completed. We need to wait for those before we
// dispatch the helper.
TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
if (!pool) {
NS_ERROR("IndexedDB is totally broken.");
return;
}
TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
nsRefPtr<WaitForTransactionsToFinishRunnable> waitRunnable =
new WaitForTransactionsToFinishRunnable(op);
nsRefPtr<WaitForTransactionsToFinishRunnable> waitRunnable =
new WaitForTransactionsToFinishRunnable(runnable);
nsAutoTArray<nsRefPtr<IDBDatabase>, 1> array;
array.AppendElement(aDatabase);
// All other databases should be closed, so we only need to wait on this
// one.
nsAutoTArray<nsRefPtr<IDBDatabase>, 1> array;
if (!array.AppendElement(aDatabase)) {
NS_ERROR("This should never fail!");
}
// Use the WaitForTransactionsToFinishRunnable as the callback.
if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) {
NS_WARNING("Failed to wait for transaction to complete!");
// Use the WaitForTransactionsToFinishRunnable as the callback.
if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) {
NS_WARNING("Failed to wait for transaction to complete!");
}
}
break;
}
break;
}
}
}
void
IndexedDatabaseManager::UnblockSetVersionRunnable(IDBDatabase* aDatabase)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDatabase, "Null pointer!");
// Check through the list of SetVersionRunnables to find the one we're seeking.
for (PRUint32 index = 0; index < mSetVersionRunnables.Length(); index++) {
nsRefPtr<SetVersionRunnable>& runnable = mSetVersionRunnables[index];
if (runnable->mRequestingDatabase->Id() == aDatabase->Id()) {
NS_ASSERTION(!runnable->mHelper,
"Why are we unblocking a runnable if the helper didn't run?");
NS_DispatchToCurrentThread(runnable);
return;
}
}
NS_NOTREACHED("How did we get here!");
}
// static
bool
IndexedDatabaseManager::SetCurrentDatabase(IDBDatabase* aDatabase)
@ -855,6 +662,45 @@ IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
return rv;
}
// static
nsresult
IndexedDatabaseManager::DispatchHelper(AsyncConnectionHelper* aHelper)
{
nsresult rv = NS_OK;
// If the helper has a transaction, dispatch it to the transaction
// threadpool.
if (aHelper->HasTransaction()) {
rv = aHelper->DispatchToTransactionPool();
}
else {
// Otherwise, dispatch it to the IO thread.
IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
NS_ASSERTION(manager, "We should definitely have a manager here");
rv = aHelper->Dispatch(manager->IOThread());
}
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
bool
IndexedDatabaseManager::IsClearOriginPending(const nsACString& origin)
{
// Iterate through our SynchronizedOps to see if we have an entry that matches
// this origin and has no id.
PRUint32 count = mSynchronizedOps.Length();
for (PRUint32 index = 0; index < count; index++) {
nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
if (op->mOrigin.Equals(origin) && !op->mId) {
return true;
}
}
return false;
}
NS_IMPL_ISUPPORTS2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
nsIObserver)
@ -890,12 +736,10 @@ IndexedDatabaseManager::GetUsageForURI(
// See if we're currently clearing the databases for this origin. If so then
// we pretend that we've already deleted everything.
for (PRUint32 index = 0; index < mOriginClearRunnables.Length(); index++) {
if (mOriginClearRunnables[index]->mOrigin == origin) {
rv = NS_DispatchToCurrentThread(runnable);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
if (IsClearOriginPending(origin)) {
rv = NS_DispatchToCurrentThread(runnable);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// Otherwise dispatch to the IO thread to actually compute the usage.
@ -949,15 +793,20 @@ IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI)
return NS_OK;
}
// If we're already clearing out files for this origin then return
// If there is a pending or running clear operation for this origin, return
// immediately.
PRUint32 clearDataCount = mOriginClearRunnables.Length();
for (PRUint32 index = 0; index < clearDataCount; index++) {
if (mOriginClearRunnables[index]->mOrigin == origin) {
return NS_OK;
}
if (IsClearOriginPending(origin)) {
return NS_OK;
}
// Queue up the origin clear runnable.
nsRefPtr<OriginClearRunnable> runnable =
new OriginClearRunnable(origin, mIOThread);
rv = WaitForOpenAllowed(origin, nsnull, runnable);
NS_ENSURE_SUCCESS(rv, rv);
// Give the runnable some help by invalidating any databases in the way.
// We need to grab references to any live databases here to prevent them from
// dying while we invalidate them.
nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
@ -965,25 +814,7 @@ IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI)
// Grab all live databases for this origin.
nsTArray<IDBDatabase*>* array;
if (mLiveDatabases.Get(origin, &array)) {
if (!liveDatabases.AppendElements(*array)) {
NS_WARNING("Out of memory?");
return NS_ERROR_OUT_OF_MEMORY;
}
}
nsRefPtr<OriginClearRunnable> runnable =
new OriginClearRunnable(origin, mIOThread);
// Make a new entry for this origin in mOriginClearRunnables.
nsRefPtr<OriginClearRunnable>* newRunnable =
mOriginClearRunnables.AppendElement(runnable);
NS_ENSURE_TRUE(newRunnable, NS_ERROR_OUT_OF_MEMORY);
if (liveDatabases.IsEmpty()) {
rv = runnable->Run();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
liveDatabases.AppendElements(*array);
}
// Invalidate all the live databases first.
@ -991,16 +822,8 @@ IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI)
liveDatabases[index]->Invalidate();
}
// Now set up our callback so that we know when they have finished.
TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
NS_ENSURE_TRUE(pool, NS_ERROR_FAILURE);
if (!pool->WaitForAllDatabasesToComplete(liveDatabases, runnable)) {
NS_WARNING("Can't wait on databases!");
return NS_ERROR_FAILURE;
}
NS_ASSERTION(liveDatabases.IsEmpty(), "Should have swapped!");
// After everything has been invalidated the helper should be dispatched to
// the end of the event queue.
return NS_OK;
}
@ -1077,7 +900,7 @@ NS_IMETHODIMP
IndexedDatabaseManager::OriginClearRunnable::Run()
{
if (NS_IsMainThread()) {
// On the first time on the main thread we simply dispatch to the IO thread.
// On the first time on the main thread we dispatch to the IO thread.
if (mFirstCallback) {
NS_ASSERTION(mThread, "Should have a thread here!");
@ -1097,19 +920,11 @@ IndexedDatabaseManager::OriginClearRunnable::Run()
NS_ASSERTION(!mThread, "Should have been cleared already!");
// Dispatch any queued runnables that we collected while we were waiting.
for (PRUint32 index = 0; index < mDelayedRunnables.Length(); index++) {
if (NS_FAILED(NS_DispatchToCurrentThread(mDelayedRunnables[index]))) {
NS_WARNING("Failed to dispatch delayed runnable!");
}
}
mDelayedRunnables.Clear();
// Tell the IndexedDatabaseManager that we're done.
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
if (mgr) {
mgr->OnOriginClearComplete(this);
}
NS_ASSERTION(mgr, "This should never fail!");
mgr->AllowNextSynchronizedOp(mOrigin, nsnull);
return NS_OK;
}
@ -1257,50 +1072,6 @@ IndexedDatabaseManager::AsyncUsageRunnable::Run()
return NS_OK;
}
IndexedDatabaseManager::SetVersionRunnable::SetVersionRunnable(
IDBDatabase* aDatabase,
nsTArray<nsRefPtr<IDBDatabase> >& aDatabases)
: mRequestingDatabase(aDatabase)
{
NS_ASSERTION(aDatabase, "Null database!");
if (!mDatabases.SwapElements(aDatabases)) {
NS_ERROR("This should never fail!");
}
}
IndexedDatabaseManager::SetVersionRunnable::~SetVersionRunnable()
{
}
NS_IMPL_ISUPPORTS1(IndexedDatabaseManager::SetVersionRunnable, nsIRunnable)
NS_IMETHODIMP
IndexedDatabaseManager::SetVersionRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mHelper, "Should have been cleared already!");
// Dispatch any queued runnables that we picked up while waiting for the
// SetVersion transaction to complete.
for (PRUint32 index = 0; index < mDelayedRunnables.Length(); index++) {
if (NS_FAILED(NS_DispatchToCurrentThread(mDelayedRunnables[index]))) {
NS_WARNING("Failed to dispatch delayed runnable!");
}
}
// No need to hold these alive any longer.
mDelayedRunnables.Clear();
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never be null!");
// Let the IndexedDatabaseManager know that the SetVersion transaction has
// completed.
mgr->OnSetVersionRunnableComplete(this);
return NS_OK;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::WaitForTransactionsToFinishRunnable,
nsIRunnable)
@ -1308,31 +1079,84 @@ NS_IMETHODIMP
IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mOp && mOp->mHelper, "What?");
// Don't hold the callback alive longer than necessary.
nsRefPtr<AsyncConnectionHelper> helper;
helper.swap(mRunnable->mHelper);
helper.swap(mOp->mHelper);
nsRefPtr<SetVersionRunnable> runnable;
runnable.swap(mRunnable);
mOp = nsnull;
// If the helper has a transaction, dispatch it to the transaction
// threadpool.
if (helper->HasTransaction()) {
if (NS_FAILED(helper->DispatchToTransactionPool())) {
NS_WARNING("Failed to dispatch to thread pool!");
}
}
// Otherwise, dispatch it to the IO thread.
else {
IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
NS_ASSERTION(manager, "We should definitely have a manager here");
helper->Dispatch(manager->IOThread());
}
IndexedDatabaseManager::DispatchHelper(helper);
// The helper is responsible for calling
// IndexedDatabaseManager::UnblockSetVersionRunnable.
// IndexedDatabaseManager::AllowNextSynchronizedOp.
return NS_OK;
}
IndexedDatabaseManager::SynchronizedOp::SynchronizedOp(const nsACString& aOrigin,
nsIAtom* aId)
: mOrigin(aOrigin), mId(aId)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_COUNT_CTOR(IndexedDatabaseManager::SynchronizedOp);
}
IndexedDatabaseManager::SynchronizedOp::~SynchronizedOp()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_COUNT_DTOR(IndexedDatabaseManager::SynchronizedOp);
}
bool
IndexedDatabaseManager::SynchronizedOp::MustWaitFor(const SynchronizedOp& aRhs)
const
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// If the origins don't match, the second can proceed.
if (!aRhs.mOrigin.Equals(mOrigin)) {
return false;
}
// If the origins and the ids match, the second must wait.
if (aRhs.mId == mId) {
return true;
}
// Waiting is required if either one corresponds to an origin clearing
// (a null Id).
if (!aRhs.mId || !mId) {
return true;
}
// Otherwise, things for the same origin but different databases can proceed
// independently.
return false;
}
void
IndexedDatabaseManager::SynchronizedOp::DelayRunnable(nsIRunnable* aRunnable)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mDelayedRunnables.IsEmpty() || !mId,
"Only ClearOrigin operations can delay multiple runnables!");
mDelayedRunnables.AppendElement(aRunnable);
}
void
IndexedDatabaseManager::SynchronizedOp::DispatchDelayedRunnables()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mHelper, "Any helper should be gone by now!");
PRUint32 count = mDelayedRunnables.Length();
for (PRUint32 index = 0; index < count; index++) {
NS_DispatchToCurrentThread(mDelayedRunnables[index]);
}
mDelayedRunnables.Clear();
}

View File

@ -81,11 +81,14 @@ public:
NS_DECL_NSIOBSERVER
// Waits for databases to be cleared and for version change transactions to
// complete before dispatching the give runnable.
nsresult WaitForOpenAllowed(const nsAString& aName,
const nsACString& aOrigin,
// complete before dispatching the given runnable.
nsresult WaitForOpenAllowed(const nsACString& aOrigin,
nsIAtom* aId,
nsIRunnable* aRunnable);
void AllowNextSynchronizedOp(const nsACString& aOrigin,
nsIAtom* aId);
nsIThread* IOThread()
{
NS_ASSERTION(mIOThread, "This should never be null!");
@ -95,12 +98,28 @@ public:
// Returns true if we've begun the shutdown process.
static bool IsShuttingDown();
// Begins the process of setting a database version.
nsresult SetDatabaseVersion(IDBDatabase* aDatabase,
IDBOpenDBRequest* aRequest,
PRInt64 aOldVersion,
PRInt64 aNewVersion,
AsyncConnectionHelper* aHelper);
typedef void (*WaitingOnDatabasesCallback)(nsTArray<nsRefPtr<IDBDatabase> >&, void*);
// Acquire exclusive access to the database given (waits for all others to
// close). If databases need to close first, the callback will be invoked
// with an array of said databases.
nsresult AcquireExclusiveAccess(IDBDatabase* aDatabase,
AsyncConnectionHelper* aHelper,
WaitingOnDatabasesCallback aCallback,
void* aClosure)
{
NS_ASSERTION(aDatabase, "Need a DB here!");
return AcquireExclusiveAccess(aDatabase->Origin(), aDatabase, aHelper,
aCallback, aClosure);
}
nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
AsyncConnectionHelper* aHelper,
WaitingOnDatabasesCallback aCallback,
void* aClosure)
{
return AcquireExclusiveAccess(aOrigin, nsnull, aHelper, aCallback,
aClosure);
}
// Called when a window is being purged from the bfcache or the user leaves
// a page which isn't going into the bfcache. Forces any live database
@ -122,6 +141,12 @@ private:
IndexedDatabaseManager();
~IndexedDatabaseManager();
nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
IDBDatabase* aDatabase,
AsyncConnectionHelper* aHelper,
WaitingOnDatabasesCallback aCallback,
void* aClosure);
// Called when a database is created.
bool RegisterDatabase(IDBDatabase* aDatabase);
@ -131,21 +156,14 @@ private:
// Called when a database has been closed.
void OnDatabaseClosed(IDBDatabase* aDatabase);
// Called when a version change transaction can run immediately.
void RunSetVersionTransaction(IDBDatabase* aDatabase)
{
OnDatabaseClosed(aDatabase);
}
// Responsible for clearing the database files for a particular origin on the
// IO thread. Created when nsIIDBIndexedDatabaseManager::ClearDatabasesForURI
// is called. Runs three times, first on the main thread, next on the IO
// thread, and then finally again on the main thread. While on the IO thread
// the runnable will actually remove the origin's database files and the
// directory that contains them before dispatching itself back to the main
// thread. When on the main thread the runnable will dispatch any queued
// runnables and then notify the IndexedDatabaseManager that the job has been
// completed.
// thread. When back on the main thread the runnable will notify the
// IndexedDatabaseManager that the job has been completed.
class OriginClearRunnable : public nsIRunnable
{
public:
@ -161,12 +179,10 @@ private:
nsCString mOrigin;
nsCOMPtr<nsIThread> mThread;
nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
bool mFirstCallback;
};
// Called when OriginClearRunnable has finished its Run() method.
inline void OnOriginClearComplete(OriginClearRunnable* aRunnable);
bool IsClearOriginPending(const nsACString& origin);
// Responsible for calculating the amount of space taken up by databases of a
// certain origin. Created when nsIIDBIndexedDatabaseManager::GetUsageForURI
@ -204,63 +220,59 @@ private:
// Called when AsyncUsageRunnable has finished its Run() method.
inline void OnUsageCheckComplete(AsyncUsageRunnable* aRunnable);
void UnblockSetVersionRunnable(IDBDatabase* aDatabase);
// Responsible for waiting until all databases have been closed before running
// the version change transaction. Created when
// IndexedDatabaseManager::SetDatabaseVersion is called. Runs only once on the
// main thread when the version change transaction has completed.
class SetVersionRunnable : public nsIRunnable
// A struct that contains the information corresponding to a pending or
// running operation that requires synchronization (e.g. opening a db,
// clearing dbs for an origin, etc).
struct SynchronizedOp
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
SynchronizedOp(const nsACString& aOrigin, nsIAtom* aId);
~SynchronizedOp();
SetVersionRunnable(IDBDatabase* aDatabase,
nsTArray<nsRefPtr<IDBDatabase> >& aDatabases);
~SetVersionRunnable();
// Test whether the second SynchronizedOp needs to get behind this one.
bool MustWaitFor(const SynchronizedOp& aRhs) const;
nsRefPtr<IDBDatabase> mRequestingDatabase;
nsTArray<nsRefPtr<IDBDatabase> > mDatabases;
void DelayRunnable(nsIRunnable* aRunnable);
void DispatchDelayedRunnables();
const nsCString mOrigin;
nsCOMPtr<nsIAtom> mId;
nsRefPtr<AsyncConnectionHelper> mHelper;
nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
nsTArray<nsRefPtr<IDBDatabase> > mDatabases;
};
// Called when SetVersionRunnable has finished its Run() method.
inline void OnSetVersionRunnableComplete(SetVersionRunnable* aRunnable);
// A callback runnable used by the TransactionPool when it's safe to proceed
// with a SetVersion/DeleteDatabase/etc.
class WaitForTransactionsToFinishRunnable : public nsIRunnable
{
public:
WaitForTransactionsToFinishRunnable(SetVersionRunnable* aRunnable)
: mRunnable(aRunnable)
WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp)
: mOp(aOp)
{
NS_ASSERTION(mRunnable, "Why don't we have a runnable?");
NS_ASSERTION(mRunnable->mDatabases.IsEmpty(), "We're here too early!");
NS_ASSERTION(mOp, "Why don't we have a runnable?");
NS_ASSERTION(mOp->mDatabases.IsEmpty(), "We're here too early!");
NS_ASSERTION(mOp->mHelper, "What are we supposed to do when we're done?");
}
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
private:
nsRefPtr<SetVersionRunnable> mRunnable;
// The IndexedDatabaseManager holds this alive.
SynchronizedOp* mOp;
};
static nsresult DispatchHelper(AsyncConnectionHelper* aHelper);
// Maintains a list of live databases per origin.
nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
// Maintains a list of origins that are currently being cleared.
nsAutoTArray<nsRefPtr<OriginClearRunnable>, 1> mOriginClearRunnables;
// Maintains a list of origins that we're currently enumerating to gather
// usage statistics.
nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
// Maintains a list of SetVersion calls that are in progress.
nsAutoTArray<nsRefPtr<SetVersionRunnable>, 1> mSetVersionRunnables;
// Maintains a list of synchronized operatons that are in progress or queued.
nsAutoTArray<nsAutoPtr<SynchronizedOp>, 5> mSynchronizedOps;
// Thread on which IO is performed.
nsCOMPtr<nsIThread> mIOThread;

View File

@ -498,6 +498,9 @@ public:
nsresult GetSuccessResult(JSContext* aCx,
jsval* aVal);
static
void QueueVersionChange(nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
void* aClosure);
protected:
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
nsresult Init();
@ -519,10 +522,109 @@ private:
PRUint64 mCurrentVersion;
};
// Responsible for firing "versionchange" events at all live and non-closed
// databases, and for firing a "blocked" event at the requesting database if any
// databases fail to close.
class VersionChangeEventsRunnable : public nsRunnable
{
public:
VersionChangeEventsRunnable(
IDBDatabase* aRequestingDatabase,
IDBOpenDBRequest* aRequest,
nsTArray<nsRefPtr<IDBDatabase> >& aWaitingDatabases,
PRInt64 aOldVersion,
PRInt64 aNewVersion)
: mRequestingDatabase(aRequestingDatabase),
mRequest(aRequest),
mOldVersion(aOldVersion),
mNewVersion(aNewVersion)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aRequestingDatabase, "Null pointer!");
NS_ASSERTION(aRequest, "Null pointer!");
if (!mWaitingDatabases.SwapElements(aWaitingDatabases)) {
NS_ERROR("This should never fail!");
}
}
NS_IMETHOD Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Fire version change events at all of the databases that are not already
// closed. Also kick bfcached documents out of bfcache.
PRUint32 count = mWaitingDatabases.Length();
for (PRUint32 index = 0; index < count; index++) {
nsRefPtr<IDBDatabase>& database = mWaitingDatabases[index];
if (database->IsClosed()) {
continue;
}
// First check if the document the IDBDatabase is part of is bfcached.
nsCOMPtr<nsIDocument> ownerDoc = database->GetOwnerDocument();
nsIBFCacheEntry* bfCacheEntry;
if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) {
bfCacheEntry->RemoveFromBFCacheSync();
NS_ASSERTION(database->IsClosed(),
"Kicking doc out of bfcache should have closed database");
continue;
}
// Otherwise fire a versionchange event.
nsRefPtr<nsDOMEvent> event =
IDBVersionChangeEvent::Create(mOldVersion, mNewVersion);
NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
bool dummy;
database->DispatchEvent(event, &dummy);
}
// Now check to see if any didn't close. If there are some running still
// then fire the blocked event.
for (PRUint32 index = 0; index < count; index++) {
if (!mWaitingDatabases[index]->IsClosed()) {
nsRefPtr<nsDOMEvent> event =
IDBVersionChangeEvent::CreateBlocked(mOldVersion, mNewVersion);
NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
bool dummy;
mRequest->DispatchEvent(event, &dummy);
break;
}
}
return NS_OK;
}
private:
nsRefPtr<IDBDatabase> mRequestingDatabase;
nsRefPtr<IDBOpenDBRequest> mRequest;
nsTArray<nsRefPtr<IDBDatabase> > mWaitingDatabases;
PRInt64 mOldVersion;
PRInt64 mNewVersion;
};
} // anonymous namespace
NS_IMPL_THREADSAFE_ISUPPORTS1(OpenDatabaseHelper, nsIRunnable);
nsresult
OpenDatabaseHelper::Init()
{
nsCString str(mASCIIOrigin);
str.Append("*");
str.Append(NS_ConvertUTF16toUTF8(mName));
nsCOMPtr<nsIAtom> atom = do_GetAtom(str);
NS_ENSURE_TRUE(atom, NS_ERROR_FAILURE);
atom.swap(mDatabaseId);
return NS_OK;
}
nsresult
OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget)
{
@ -630,9 +732,6 @@ OpenDatabaseHelper::DoDatabaseWork()
NS_NOTYETIMPLEMENTED("Implement me!");
}
mDatabaseId = HashString(mDatabaseFilePath);
NS_ASSERTION(mDatabaseId, "HashString gave us 0?!");
rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId, &mCurrentVersion,
mObjectStores);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -647,6 +746,20 @@ OpenDatabaseHelper::DoDatabaseWork()
}
// See if we need to do a VERSION_CHANGE transaction
// Optional version semantics.
if (!mRequestedVersion) {
// If the requested version was not specified and the database was created,
// treat it as if version 1 were requested.
if (mCurrentVersion == 0) {
mRequestedVersion = 1;
}
else {
// Otherwise, treat it as if the current version were requested.
mRequestedVersion = mCurrentVersion;
}
}
if (mCurrentVersion > mRequestedVersion) {
return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR;
}
@ -683,8 +796,9 @@ OpenDatabaseHelper::StartSetVersion()
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never be null!");
rv = mgr->SetDatabaseVersion(mDatabase, mOpenDBRequest, mCurrentVersion,
mRequestedVersion, helper);
rv = mgr->AcquireExclusiveAccess(mDatabase, helper,
&SetVersionHelper::QueueVersionChange,
helper);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
// The SetVersionHelper is responsible for dispatching us back to the
@ -745,6 +859,11 @@ OpenDatabaseHelper::Run()
DispatchSuccessEvent();
}
IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
NS_ASSERTION(manager, "This should never be null!");
manager->AllowNextSynchronizedOp(mASCIIOrigin, mDatabaseId);
ReleaseMainThreadObjects();
return NS_OK;
@ -989,6 +1108,27 @@ SetVersionHelper::GetSuccessResult(JSContext* aCx,
aVal);
}
// static
void
SetVersionHelper::QueueVersionChange(nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
void* aClosure)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aDatabases.IsEmpty(), "Why are we here?");
SetVersionHelper* helper = static_cast<SetVersionHelper*>(aClosure);
NS_ASSERTION(helper, "Why don't we have a helper?");
nsRefPtr<VersionChangeEventsRunnable> eventsRunnable =
new VersionChangeEventsRunnable(helper->mOpenHelper->Database(),
helper->mOpenRequest,
aDatabases,
helper->mCurrentVersion,
helper->mRequestedVersion);
NS_DispatchToCurrentThread(eventsRunnable);
}
already_AddRefed<nsDOMEvent>
SetVersionHelper::CreateSuccessEvent()
{

View File

@ -67,6 +67,8 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
nsresult Init();
nsresult Dispatch(nsIEventTarget* aDatabaseThread);
nsresult RunImmediately();
@ -84,6 +86,17 @@ public:
nsresult NotifySetVersionFinished();
void BlockDatabase();
nsIAtom* Id() const
{
return mDatabaseId.get();
}
IDBDatabase* Database() const
{
NS_ASSERTION(mDatabase, "Calling at the wrong time!");
return mDatabase;
}
protected:
// Methods only called on the main thread
nsresult EnsureSuccessResult();
@ -103,13 +116,13 @@ private:
nsString mName;
nsCString mASCIIOrigin;
PRUint64 mRequestedVersion;
nsCOMPtr<nsIAtom> mDatabaseId;
// Out-params.
nsTArray<nsAutoPtr<ObjectStoreInfo> > mObjectStores;
PRUint64 mCurrentVersion;
PRUint32 mDataVersion;
nsString mDatabaseFilePath;
PRUint32 mDatabaseId;
PRInt64 mLastObjectStoreId;
PRInt64 mLastIndexId;
nsRefPtr<IDBDatabase> mDatabase;

View File

@ -223,7 +223,7 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction)
// AddRef here because removing from the hash will call Release.
nsRefPtr<IDBTransaction> transaction(aTransaction);
const PRUint32 databaseId = aTransaction->mDatabase->Id();
nsIAtom* databaseId = aTransaction->mDatabase->Id();
DatabaseTransactionInfo* dbTransactionInfo;
if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
@ -324,7 +324,7 @@ TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction,
NS_ASSERTION(aCanRun, "Null pointer!");
NS_ASSERTION(aExistingQueue, "Null pointer!");
const PRUint32 databaseId = aTransaction->mDatabase->Id();
nsIAtom* databaseId = aTransaction->mDatabase->Id();
const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames;
const PRUint16 mode = aTransaction->mMode;
@ -421,7 +421,7 @@ TransactionThreadPool::Dispatch(IDBTransaction* aTransaction,
return NS_OK;
}
const PRUint32 databaseId = aTransaction->mDatabase->Id();
nsIAtom* databaseId = aTransaction->mDatabase->Id();
#ifdef DEBUG
if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) {

View File

@ -168,7 +168,7 @@ protected:
nsCOMPtr<nsIThreadPool> mThreadPool;
nsClassHashtable<nsUint32HashKey, DatabaseTransactionInfo>
nsClassHashtable<nsISupportsHashKey, DatabaseTransactionInfo>
mTransactionsInProgress;
nsTArray<QueuedDispatchInfo> mDelayedDispatchQueue;

View File

@ -39,7 +39,7 @@
#include "nsISupports.idl"
[scriptable, uuid(2f182bf1-1542-47fe-b2f7-4b1741b5283c)]
[scriptable, uuid(7aad2542-a5cb-4a57-b20c-c7d16b8582ab)]
interface nsIIDBDatabaseException : nsISupports
{
// const unsigned short NO_ERR = 0;
@ -52,10 +52,9 @@ interface nsIIDBDatabaseException : nsISupports
const unsigned short TRANSACTION_INACTIVE_ERR = 7;
const unsigned short ABORT_ERR = 8;
const unsigned short READ_ONLY_ERR = 9;
const unsigned short RECOVERABLE_ERR = 10;
const unsigned short TRANSIENT_ERR = 11;
const unsigned short TIMEOUT_ERR = 12;
const unsigned short DEADLOCK_ERR = 13;
const unsigned short TIMEOUT_ERR = 10;
const unsigned short QUOTA_ERR = 11;
const unsigned short VERSION_ERR = 12;
readonly attribute unsigned short code;
};

View File

@ -49,10 +49,11 @@ interface nsIVariant;
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBFactory
* for more information.
*/
[scriptable, uuid(4b23254a-ce6d-4442-8c90-9d8744d3c633)]
[scriptable, uuid(d2889b8f-662a-42d3-8a8f-ac5179b9d5b0)]
interface nsIIDBFactory : nsISupports
{
[implicit_jscontext]
[implicit_jscontext, optional_argc]
nsIIDBOpenDBRequest
open(in AString name, in long long version);
open(in AString name,
[optional] in long long version);
};

Some files were not shown because too many files have changed in this diff Show More