Index: mojo/public/js/new_bindings/validator.js |
diff --git a/mojo/public/js/new_bindings/validator.js b/mojo/public/js/new_bindings/validator.js |
index 610112b58eb498c076a139708f1e4411c06c05a3..b43a65b4abfb3514893156aa6b4fe295da9d3b9c 100644 |
--- a/mojo/public/js/new_bindings/validator.js |
+++ b/mojo/public/js/new_bindings/validator.js |
@@ -15,6 +15,9 @@ |
UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE', |
ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER', |
UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER', |
+ ILLEGAL_INTERFACE_ID: 'VALIDATION_ERROR_ILLEGAL_INTERFACE_ID', |
+ UNEXPECTED_INVALID_INTERFACE_ID: |
+ 'VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID', |
MESSAGE_HEADER_INVALID_FLAGS: |
'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS', |
MESSAGE_HEADER_MISSING_REQUEST_ID: |
@@ -27,6 +30,49 @@ |
}; |
var NULL_MOJO_POINTER = "NULL_MOJO_POINTER"; |
+ var gValidationErrorObserver = null; |
+ |
+ function reportValidationError(error) { |
+ if (gValidationErrorObserver) { |
+ gValidationErrorObserver.setLastError(error); |
+ } |
+ } |
+ |
+ var ValidationErrorObserverForTesting = (function() { |
+ function Observer() { |
+ this.lastError = validationError.NONE; |
+ this.callback = null; |
+ } |
+ |
+ Observer.prototype.setLastError = function(error) { |
+ this.lastError = error; |
+ if (this.callback) { |
+ this.callback(error); |
+ } |
+ }; |
+ |
+ Observer.prototype.reset = function(error) { |
+ this.lastError = validationError.NONE; |
+ this.callback = null; |
+ }; |
+ |
+ return { |
+ getInstance: function() { |
+ if (!gValidationErrorObserver) { |
+ gValidationErrorObserver = new Observer(); |
+ } |
+ return gValidationErrorObserver; |
+ } |
+ }; |
+ })(); |
+ |
+ function isTestingMode() { |
+ return Boolean(gValidationErrorObserver); |
+ } |
+ |
+ function clearTestingMode() { |
+ gValidationErrorObserver = null; |
+ } |
function isEnumClass(cls) { |
return cls instanceof internal.Enum; |
@@ -49,9 +95,21 @@ |
cls === internal.NullableInterfaceRequest; |
} |
+ function isAssociatedInterfaceClass(cls) { |
+ return cls === internal.AssociatedInterfacePtrInfo || |
+ cls === internal.NullableAssociatedInterfacePtrInfo; |
+ } |
+ |
+ function isAssociatedInterfaceRequestClass(cls) { |
+ return cls === internal.AssociatedInterfaceRequest || |
+ cls === internal.NullableAssociatedInterfaceRequest; |
+ } |
+ |
function isNullable(type) { |
return type === internal.NullableString || |
type === internal.NullableHandle || |
+ type === internal.NullableAssociatedInterfacePtrInfo || |
+ type === internal.NullableAssociatedInterfaceRequest || |
type === internal.NullableInterface || |
type === internal.NullableInterfaceRequest || |
type instanceof internal.NullableArrayOf || |
@@ -62,16 +120,21 @@ |
this.message = message; |
this.offset = 0; |
this.handleIndex = 0; |
+ this.associatedEndpointHandleIndex = 0; |
+ this.payloadInterfaceIds = null; |
+ this.offsetLimit = this.message.buffer.byteLength; |
} |
- Object.defineProperty(Validator.prototype, "offsetLimit", { |
- get: function() { return this.message.buffer.byteLength; } |
- }); |
- |
Object.defineProperty(Validator.prototype, "handleIndexLimit", { |
get: function() { return this.message.handles.length; } |
}); |
+ Object.defineProperty(Validator.prototype, "associatedHandleIndexLimit", { |
+ get: function() { |
+ return this.payloadInterfaceIds ? this.payloadInterfaceIds.length : 0; |
+ } |
+ }); |
+ |
// True if we can safely allocate a block of bytes from start to |
// to start + numBytes. |
Validator.prototype.isValidRange = function(start, numBytes) { |
@@ -109,6 +172,21 @@ |
return true; |
}; |
+ Validator.prototype.claimAssociatedEndpointHandle = function(index) { |
+ if (index === internal.kEncodedInvalidHandleValue) { |
+ return true; |
+ } |
+ |
+ if (index < this.associatedEndpointHandleIndex || |
+ index >= this.associatedHandleIndexLimit) { |
+ return false; |
+ } |
+ |
+ // This is safe because handle indices are uint32. |
+ this.associatedEndpointHandleIndex = index + 1; |
+ return true; |
+ }; |
+ |
Validator.prototype.validateEnum = function(offset, enumClass) { |
// Note: Assumes that enums are always 32 bits! But this matches |
// mojom::generate::pack::PackedField::GetSizeForKind, so it should be okay. |
@@ -129,6 +207,22 @@ |
return validationError.NONE; |
}; |
+ Validator.prototype.validateAssociatedEndpointHandle = function(offset, |
+ nullable) { |
+ var index = this.message.buffer.getUint32(offset); |
+ |
+ if (index === internal.kEncodedInvalidHandleValue) { |
+ return nullable ? validationError.NONE : |
+ validationError.UNEXPECTED_INVALID_INTERFACE_ID; |
+ } |
+ |
+ if (!this.claimAssociatedEndpointHandle(index)) { |
+ return validationError.ILLEGAL_INTERFACE_ID; |
+ } |
+ |
+ return validationError.NONE; |
+ }; |
+ |
Validator.prototype.validateInterface = function(offset, nullable) { |
return this.validateHandle(offset, nullable); |
}; |
@@ -137,6 +231,16 @@ |
return this.validateHandle(offset, nullable); |
}; |
+ Validator.prototype.validateAssociatedInterface = function(offset, |
+ nullable) { |
+ return this.validateAssociatedEndpointHandle(offset, nullable); |
+ }; |
+ |
+ Validator.prototype.validateAssociatedInterfaceRequest = function( |
+ offset, nullable) { |
+ return this.validateAssociatedEndpointHandle(offset, nullable); |
+ }; |
+ |
Validator.prototype.validateStructHeader = function(offset, minNumBytes) { |
if (!internal.isAligned(offset)) |
return validationError.MISALIGNED_OBJECT; |
@@ -181,31 +285,63 @@ |
}; |
Validator.prototype.validateMessageHeader = function() { |
- |
- var err = this.validateStructHeader(0, internal.kMessageHeaderSize); |
- if (err != validationError.NONE) |
+ var err = this.validateStructHeader(0, internal.kMessageV0HeaderSize); |
+ if (err != validationError.NONE) { |
return err; |
+ } |
var numBytes = this.message.getHeaderNumBytes(); |
var version = this.message.getHeaderVersion(); |
var validVersionAndNumBytes = |
- (version == 0 && numBytes == internal.kMessageHeaderSize) || |
- (version == 1 && |
- numBytes == internal.kMessageWithRequestIDHeaderSize) || |
- (version > 1 && |
- numBytes >= internal.kMessageWithRequestIDHeaderSize); |
- if (!validVersionAndNumBytes) |
+ (version == 0 && numBytes == internal.kMessageV0HeaderSize) || |
+ (version == 1 && numBytes == internal.kMessageV1HeaderSize) || |
+ (version == 2 && numBytes == internal.kMessageV2HeaderSize) || |
+ (version > 2 && numBytes >= internal.kMessageV2HeaderSize); |
+ |
+ if (!validVersionAndNumBytes) { |
return validationError.UNEXPECTED_STRUCT_HEADER; |
+ } |
var expectsResponse = this.message.expectsResponse(); |
var isResponse = this.message.isResponse(); |
- if (version == 0 && (expectsResponse || isResponse)) |
+ if (version == 0 && (expectsResponse || isResponse)) { |
return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID; |
+ } |
- if (isResponse && expectsResponse) |
+ if (isResponse && expectsResponse) { |
return validationError.MESSAGE_HEADER_INVALID_FLAGS; |
+ } |
+ |
+ if (version < 2) { |
+ return validationError.NONE; |
+ } |
+ |
+ var err = this.validateArrayPointer( |
+ internal.kMessagePayloadInterfaceIdsPointerOffset, |
+ internal.Uint32.encodedSize, internal.Uint32, true, [0], 0); |
+ |
+ if (err != validationError.NONE) { |
+ return err; |
+ } |
+ |
+ this.payloadInterfaceIds = this.message.getPayloadInterfaceIds(); |
+ if (this.payloadInterfaceIds) { |
+ for (var interfaceId of this.payloadInterfaceIds) { |
+ if (!internal.isValidInterfaceId(interfaceId) || |
+ internal.isMasterInterfaceId(interfaceId)) { |
+ return validationError.ILLEGAL_INTERFACE_ID; |
+ } |
+ } |
+ } |
+ |
+ // Set offset to the start of the payload and offsetLimit to the start of |
+ // the payload interface Ids so that payload can be validated using the |
+ // same messageValidator. |
+ this.offset = this.message.getHeaderNumBytes(); |
+ this.offsetLimit = this.decodePointer( |
+ internal.kMessagePayloadInterfaceIdsPointerOffset); |
return validationError.NONE; |
}; |
@@ -323,7 +459,7 @@ |
validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; |
var mapEncodedSize = internal.kStructHeaderSize + |
- internal.kMapStructPayloadSize; |
+ internal.kMapStructPayloadSize; |
var err = this.validateStructHeader(structOffset, mapEncodedSize); |
if (err !== validationError.NONE) |
return err; |
@@ -408,6 +544,12 @@ |
if (isInterfaceRequestClass(elementType)) |
return this.validateInterfaceRequestElements( |
elementsOffset, numElements, nullable); |
+ if (isAssociatedInterfaceClass(elementType)) |
+ return this.validateAssociatedInterfaceElements( |
+ elementsOffset, numElements, nullable); |
+ if (isAssociatedInterfaceRequestClass(elementType)) |
+ return this.validateAssociatedInterfaceRequestElements( |
+ elementsOffset, numElements, nullable); |
if (isStringClass(elementType)) |
return this.validateArrayElements( |
elementsOffset, numElements, internal.Uint8, nullable, [0], 0); |
@@ -465,6 +607,33 @@ |
return validationError.NONE; |
}; |
+ Validator.prototype.validateAssociatedInterfaceElements = |
+ function(offset, numElements, nullable) { |
+ var elementSize = internal.AssociatedInterfacePtrInfo.prototype.encodedSize; |
+ for (var i = 0; i < numElements; i++) { |
+ var elementOffset = offset + i * elementSize; |
+ var err = this.validateAssociatedInterface(elementOffset, nullable); |
+ if (err != validationError.NONE) { |
+ return err; |
+ } |
+ } |
+ return validationError.NONE; |
+ }; |
+ |
+ Validator.prototype.validateAssociatedInterfaceRequestElements = |
+ function(offset, numElements, nullable) { |
+ var elementSize = internal.AssociatedInterfaceRequest.encodedSize; |
+ for (var i = 0; i < numElements; i++) { |
+ var elementOffset = offset + i * elementSize; |
+ var err = this.validateAssociatedInterfaceRequest(elementOffset, |
+ nullable); |
+ if (err != validationError.NONE) { |
+ return err; |
+ } |
+ } |
+ return validationError.NONE; |
+ }; |
+ |
// The elementClass parameter is the element type of the element arrays. |
Validator.prototype.validateArrayElements = |
function(offset, numElements, elementClass, nullable, |
@@ -508,4 +677,9 @@ |
internal.validationError = validationError; |
internal.Validator = Validator; |
+ internal.ValidationErrorObserverForTesting = |
+ ValidationErrorObserverForTesting; |
+ internal.reportValidationError = reportValidationError; |
+ internal.isTestingMode = isTestingMode; |
+ internal.clearTestingMode = clearTestingMode; |
})(); |