1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5(function() { 6 var internal = mojo.internal; 7 8 var validationError = { 9 NONE: 'VALIDATION_ERROR_NONE', 10 MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT', 11 ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE', 12 UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER', 13 UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER', 14 ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE', 15 UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE', 16 ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER', 17 UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER', 18 ILLEGAL_INTERFACE_ID: 'VALIDATION_ERROR_ILLEGAL_INTERFACE_ID', 19 UNEXPECTED_INVALID_INTERFACE_ID: 20 'VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID', 21 MESSAGE_HEADER_INVALID_FLAGS: 22 'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS', 23 MESSAGE_HEADER_MISSING_REQUEST_ID: 24 'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID', 25 DIFFERENT_SIZED_ARRAYS_IN_MAP: 26 'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP', 27 INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE', 28 UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION', 29 UNKNOWN_ENUM_VALUE: 'VALIDATION_ERROR_UNKNOWN_ENUM_VALUE', 30 }; 31 32 var NULL_MOJO_POINTER = "NULL_MOJO_POINTER"; 33 var gValidationErrorObserver = null; 34 35 function reportValidationError(error) { 36 if (gValidationErrorObserver) { 37 gValidationErrorObserver.lastError = error; 38 } else { 39 console.warn('Invalid message: ' + error); 40 } 41 } 42 43 var ValidationErrorObserverForTesting = (function() { 44 function Observer() { 45 this.lastError = validationError.NONE; 46 } 47 48 Observer.prototype.reset = function() { 49 this.lastError = validationError.NONE; 50 }; 51 52 return { 53 getInstance: function() { 54 if (!gValidationErrorObserver) { 55 gValidationErrorObserver = new Observer(); 56 } 57 return gValidationErrorObserver; 58 } 59 }; 60 })(); 61 62 function isTestingMode() { 63 return Boolean(gValidationErrorObserver); 64 } 65 66 function clearTestingMode() { 67 gValidationErrorObserver = null; 68 } 69 70 function isEnumClass(cls) { 71 return cls instanceof internal.Enum; 72 } 73 74 function isStringClass(cls) { 75 return cls === internal.String || cls === internal.NullableString; 76 } 77 78 function isHandleClass(cls) { 79 return cls === internal.Handle || cls === internal.NullableHandle; 80 } 81 82 function isInterfaceClass(cls) { 83 return cls instanceof internal.Interface; 84 } 85 86 function isInterfaceRequestClass(cls) { 87 return cls === internal.InterfaceRequest || 88 cls === internal.NullableInterfaceRequest; 89 } 90 91 function isAssociatedInterfaceClass(cls) { 92 return cls === internal.AssociatedInterfacePtrInfo || 93 cls === internal.NullableAssociatedInterfacePtrInfo; 94 } 95 96 function isAssociatedInterfaceRequestClass(cls) { 97 return cls === internal.AssociatedInterfaceRequest || 98 cls === internal.NullableAssociatedInterfaceRequest; 99 } 100 101 function isNullable(type) { 102 return type === internal.NullableString || 103 type === internal.NullableHandle || 104 type === internal.NullableAssociatedInterfacePtrInfo || 105 type === internal.NullableAssociatedInterfaceRequest || 106 type === internal.NullableInterface || 107 type === internal.NullableInterfaceRequest || 108 type instanceof internal.NullableArrayOf || 109 type instanceof internal.NullablePointerTo; 110 } 111 112 function Validator(message) { 113 this.message = message; 114 this.offset = 0; 115 this.handleIndex = 0; 116 this.associatedEndpointHandleIndex = 0; 117 this.payloadInterfaceIds = null; 118 this.offsetLimit = this.message.buffer.byteLength; 119 } 120 121 Object.defineProperty(Validator.prototype, "handleIndexLimit", { 122 get: function() { return this.message.handles.length; } 123 }); 124 125 Object.defineProperty(Validator.prototype, "associatedHandleIndexLimit", { 126 get: function() { 127 return this.payloadInterfaceIds ? this.payloadInterfaceIds.length : 0; 128 } 129 }); 130 131 // True if we can safely allocate a block of bytes from start to 132 // to start + numBytes. 133 Validator.prototype.isValidRange = function(start, numBytes) { 134 // Only positive JavaScript integers that are less than 2^53 135 // (Number.MAX_SAFE_INTEGER) can be represented exactly. 136 if (start < this.offset || numBytes <= 0 || 137 !Number.isSafeInteger(start) || 138 !Number.isSafeInteger(numBytes)) 139 return false; 140 141 var newOffset = start + numBytes; 142 if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit) 143 return false; 144 145 return true; 146 }; 147 148 Validator.prototype.claimRange = function(start, numBytes) { 149 if (this.isValidRange(start, numBytes)) { 150 this.offset = start + numBytes; 151 return true; 152 } 153 return false; 154 }; 155 156 Validator.prototype.claimHandle = function(index) { 157 if (index === internal.kEncodedInvalidHandleValue) 158 return true; 159 160 if (index < this.handleIndex || index >= this.handleIndexLimit) 161 return false; 162 163 // This is safe because handle indices are uint32. 164 this.handleIndex = index + 1; 165 return true; 166 }; 167 168 Validator.prototype.claimAssociatedEndpointHandle = function(index) { 169 if (index === internal.kEncodedInvalidHandleValue) { 170 return true; 171 } 172 173 if (index < this.associatedEndpointHandleIndex || 174 index >= this.associatedHandleIndexLimit) { 175 return false; 176 } 177 178 // This is safe because handle indices are uint32. 179 this.associatedEndpointHandleIndex = index + 1; 180 return true; 181 }; 182 183 Validator.prototype.validateEnum = function(offset, enumClass) { 184 // Note: Assumes that enums are always 32 bits! But this matches 185 // mojom::generate::pack::PackedField::GetSizeForKind, so it should be okay. 186 var value = this.message.buffer.getInt32(offset); 187 return enumClass.validate(value); 188 } 189 190 Validator.prototype.validateHandle = function(offset, nullable) { 191 var index = this.message.buffer.getUint32(offset); 192 193 if (index === internal.kEncodedInvalidHandleValue) 194 return nullable ? 195 validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE; 196 197 if (!this.claimHandle(index)) 198 return validationError.ILLEGAL_HANDLE; 199 200 return validationError.NONE; 201 }; 202 203 Validator.prototype.validateAssociatedEndpointHandle = function(offset, 204 nullable) { 205 var index = this.message.buffer.getUint32(offset); 206 207 if (index === internal.kEncodedInvalidHandleValue) { 208 return nullable ? validationError.NONE : 209 validationError.UNEXPECTED_INVALID_INTERFACE_ID; 210 } 211 212 if (!this.claimAssociatedEndpointHandle(index)) { 213 return validationError.ILLEGAL_INTERFACE_ID; 214 } 215 216 return validationError.NONE; 217 }; 218 219 Validator.prototype.validateInterface = function(offset, nullable) { 220 return this.validateHandle(offset, nullable); 221 }; 222 223 Validator.prototype.validateInterfaceRequest = function(offset, nullable) { 224 return this.validateHandle(offset, nullable); 225 }; 226 227 Validator.prototype.validateAssociatedInterface = function(offset, 228 nullable) { 229 return this.validateAssociatedEndpointHandle(offset, nullable); 230 }; 231 232 Validator.prototype.validateAssociatedInterfaceRequest = function( 233 offset, nullable) { 234 return this.validateAssociatedEndpointHandle(offset, nullable); 235 }; 236 237 Validator.prototype.validateStructHeader = function(offset, minNumBytes) { 238 if (!internal.isAligned(offset)) 239 return validationError.MISALIGNED_OBJECT; 240 241 if (!this.isValidRange(offset, internal.kStructHeaderSize)) 242 return validationError.ILLEGAL_MEMORY_RANGE; 243 244 var numBytes = this.message.buffer.getUint32(offset); 245 246 if (numBytes < minNumBytes) 247 return validationError.UNEXPECTED_STRUCT_HEADER; 248 249 if (!this.claimRange(offset, numBytes)) 250 return validationError.ILLEGAL_MEMORY_RANGE; 251 252 return validationError.NONE; 253 }; 254 255 Validator.prototype.validateStructVersion = function(offset, versionSizes) { 256 var numBytes = this.message.buffer.getUint32(offset); 257 var version = this.message.buffer.getUint32(offset + 4); 258 259 if (version <= versionSizes[versionSizes.length - 1].version) { 260 // Scan in reverse order to optimize for more recent versionSizes. 261 for (var i = versionSizes.length - 1; i >= 0; --i) { 262 if (version >= versionSizes[i].version) { 263 if (numBytes == versionSizes[i].numBytes) 264 break; 265 return validationError.UNEXPECTED_STRUCT_HEADER; 266 } 267 } 268 } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) { 269 return validationError.UNEXPECTED_STRUCT_HEADER; 270 } 271 272 return validationError.NONE; 273 }; 274 275 Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) { 276 var structVersion = this.message.buffer.getUint32(offset + 4); 277 return fieldVersion <= structVersion; 278 }; 279 280 Validator.prototype.validateMessageHeader = function() { 281 var err = this.validateStructHeader(0, internal.kMessageV0HeaderSize); 282 if (err != validationError.NONE) { 283 return err; 284 } 285 286 var numBytes = this.message.getHeaderNumBytes(); 287 var version = this.message.getHeaderVersion(); 288 289 var validVersionAndNumBytes = 290 (version == 0 && numBytes == internal.kMessageV0HeaderSize) || 291 (version == 1 && numBytes == internal.kMessageV1HeaderSize) || 292 (version == 2 && numBytes == internal.kMessageV2HeaderSize) || 293 (version > 2 && numBytes >= internal.kMessageV2HeaderSize); 294 295 if (!validVersionAndNumBytes) { 296 return validationError.UNEXPECTED_STRUCT_HEADER; 297 } 298 299 var expectsResponse = this.message.expectsResponse(); 300 var isResponse = this.message.isResponse(); 301 302 if (version == 0 && (expectsResponse || isResponse)) { 303 return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID; 304 } 305 306 if (isResponse && expectsResponse) { 307 return validationError.MESSAGE_HEADER_INVALID_FLAGS; 308 } 309 310 if (version < 2) { 311 return validationError.NONE; 312 } 313 314 var err = this.validateArrayPointer( 315 internal.kMessagePayloadInterfaceIdsPointerOffset, 316 internal.Uint32.encodedSize, internal.Uint32, true, [0], 0); 317 318 if (err != validationError.NONE) { 319 return err; 320 } 321 322 this.payloadInterfaceIds = this.message.getPayloadInterfaceIds(); 323 if (this.payloadInterfaceIds) { 324 for (var interfaceId of this.payloadInterfaceIds) { 325 if (!internal.isValidInterfaceId(interfaceId) || 326 internal.isMasterInterfaceId(interfaceId)) { 327 return validationError.ILLEGAL_INTERFACE_ID; 328 } 329 } 330 } 331 332 // Set offset to the start of the payload and offsetLimit to the start of 333 // the payload interface Ids so that payload can be validated using the 334 // same messageValidator. 335 this.offset = this.message.getHeaderNumBytes(); 336 this.offsetLimit = this.decodePointer( 337 internal.kMessagePayloadInterfaceIdsPointerOffset); 338 339 return validationError.NONE; 340 }; 341 342 Validator.prototype.validateMessageIsRequestWithoutResponse = function() { 343 if (this.message.isResponse() || this.message.expectsResponse()) { 344 return validationError.MESSAGE_HEADER_INVALID_FLAGS; 345 } 346 return validationError.NONE; 347 }; 348 349 Validator.prototype.validateMessageIsRequestExpectingResponse = function() { 350 if (this.message.isResponse() || !this.message.expectsResponse()) { 351 return validationError.MESSAGE_HEADER_INVALID_FLAGS; 352 } 353 return validationError.NONE; 354 }; 355 356 Validator.prototype.validateMessageIsResponse = function() { 357 if (this.message.expectsResponse() || !this.message.isResponse()) { 358 return validationError.MESSAGE_HEADER_INVALID_FLAGS; 359 } 360 return validationError.NONE; 361 }; 362 363 // Returns the message.buffer relative offset this pointer "points to", 364 // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the 365 // pointer's value is not valid. 366 Validator.prototype.decodePointer = function(offset) { 367 var pointerValue = this.message.buffer.getUint64(offset); 368 if (pointerValue === 0) 369 return NULL_MOJO_POINTER; 370 var bufferOffset = offset + pointerValue; 371 return Number.isSafeInteger(bufferOffset) ? bufferOffset : null; 372 }; 373 374 Validator.prototype.decodeUnionSize = function(offset) { 375 return this.message.buffer.getUint32(offset); 376 }; 377 378 Validator.prototype.decodeUnionTag = function(offset) { 379 return this.message.buffer.getUint32(offset + 4); 380 }; 381 382 Validator.prototype.validateArrayPointer = function( 383 offset, elementSize, elementType, nullable, expectedDimensionSizes, 384 currentDimension) { 385 var arrayOffset = this.decodePointer(offset); 386 if (arrayOffset === null) 387 return validationError.ILLEGAL_POINTER; 388 389 if (arrayOffset === NULL_MOJO_POINTER) 390 return nullable ? 391 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; 392 393 return this.validateArray(arrayOffset, elementSize, elementType, 394 expectedDimensionSizes, currentDimension); 395 }; 396 397 Validator.prototype.validateStructPointer = function( 398 offset, structClass, nullable) { 399 var structOffset = this.decodePointer(offset); 400 if (structOffset === null) 401 return validationError.ILLEGAL_POINTER; 402 403 if (structOffset === NULL_MOJO_POINTER) 404 return nullable ? 405 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; 406 407 return structClass.validate(this, structOffset); 408 }; 409 410 Validator.prototype.validateUnion = function( 411 offset, unionClass, nullable) { 412 var size = this.message.buffer.getUint32(offset); 413 if (size == 0) { 414 return nullable ? 415 validationError.NONE : validationError.UNEXPECTED_NULL_UNION; 416 } 417 418 return unionClass.validate(this, offset); 419 }; 420 421 Validator.prototype.validateNestedUnion = function( 422 offset, unionClass, nullable) { 423 var unionOffset = this.decodePointer(offset); 424 if (unionOffset === null) 425 return validationError.ILLEGAL_POINTER; 426 427 if (unionOffset === NULL_MOJO_POINTER) 428 return nullable ? 429 validationError.NONE : validationError.UNEXPECTED_NULL_UNION; 430 431 return this.validateUnion(unionOffset, unionClass, nullable); 432 }; 433 434 // This method assumes that the array at arrayPointerOffset has 435 // been validated. 436 437 Validator.prototype.arrayLength = function(arrayPointerOffset) { 438 var arrayOffset = this.decodePointer(arrayPointerOffset); 439 return this.message.buffer.getUint32(arrayOffset + 4); 440 }; 441 442 Validator.prototype.validateMapPointer = function( 443 offset, mapIsNullable, keyClass, valueClass, valueIsNullable) { 444 // Validate the implicit map struct: 445 // struct {array<keyClass> keys; array<valueClass> values}; 446 var structOffset = this.decodePointer(offset); 447 if (structOffset === null) 448 return validationError.ILLEGAL_POINTER; 449 450 if (structOffset === NULL_MOJO_POINTER) 451 return mapIsNullable ? 452 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; 453 454 var mapEncodedSize = internal.kStructHeaderSize + 455 internal.kMapStructPayloadSize; 456 var err = this.validateStructHeader(structOffset, mapEncodedSize); 457 if (err !== validationError.NONE) 458 return err; 459 460 // Validate the keys array. 461 var keysArrayPointerOffset = structOffset + internal.kStructHeaderSize; 462 err = this.validateArrayPointer( 463 keysArrayPointerOffset, keyClass.encodedSize, keyClass, false, [0], 0); 464 if (err !== validationError.NONE) 465 return err; 466 467 // Validate the values array. 468 var valuesArrayPointerOffset = keysArrayPointerOffset + 8; 469 var valuesArrayDimensions = [0]; // Validate the actual length below. 470 if (valueClass instanceof internal.ArrayOf) 471 valuesArrayDimensions = 472 valuesArrayDimensions.concat(valueClass.dimensions()); 473 var err = this.validateArrayPointer(valuesArrayPointerOffset, 474 valueClass.encodedSize, 475 valueClass, 476 valueIsNullable, 477 valuesArrayDimensions, 478 0); 479 if (err !== validationError.NONE) 480 return err; 481 482 // Validate the lengths of the keys and values arrays. 483 var keysArrayLength = this.arrayLength(keysArrayPointerOffset); 484 var valuesArrayLength = this.arrayLength(valuesArrayPointerOffset); 485 if (keysArrayLength != valuesArrayLength) 486 return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP; 487 488 return validationError.NONE; 489 }; 490 491 Validator.prototype.validateStringPointer = function(offset, nullable) { 492 return this.validateArrayPointer( 493 offset, internal.Uint8.encodedSize, internal.Uint8, nullable, [0], 0); 494 }; 495 496 // Similar to Array_Data<T>::Validate() 497 // mojo/public/cpp/bindings/lib/array_internal.h 498 499 Validator.prototype.validateArray = 500 function (offset, elementSize, elementType, expectedDimensionSizes, 501 currentDimension) { 502 if (!internal.isAligned(offset)) 503 return validationError.MISALIGNED_OBJECT; 504 505 if (!this.isValidRange(offset, internal.kArrayHeaderSize)) 506 return validationError.ILLEGAL_MEMORY_RANGE; 507 508 var numBytes = this.message.buffer.getUint32(offset); 509 var numElements = this.message.buffer.getUint32(offset + 4); 510 511 // Note: this computation is "safe" because elementSize <= 8 and 512 // numElements is a uint32. 513 var elementsTotalSize = (elementType === internal.PackedBool) ? 514 Math.ceil(numElements / 8) : (elementSize * numElements); 515 516 if (numBytes < internal.kArrayHeaderSize + elementsTotalSize) 517 return validationError.UNEXPECTED_ARRAY_HEADER; 518 519 if (expectedDimensionSizes[currentDimension] != 0 && 520 numElements != expectedDimensionSizes[currentDimension]) { 521 return validationError.UNEXPECTED_ARRAY_HEADER; 522 } 523 524 if (!this.claimRange(offset, numBytes)) 525 return validationError.ILLEGAL_MEMORY_RANGE; 526 527 // Validate the array's elements if they are pointers or handles. 528 529 var elementsOffset = offset + internal.kArrayHeaderSize; 530 var nullable = isNullable(elementType); 531 532 if (isHandleClass(elementType)) 533 return this.validateHandleElements(elementsOffset, numElements, nullable); 534 if (isInterfaceClass(elementType)) 535 return this.validateInterfaceElements( 536 elementsOffset, numElements, nullable); 537 if (isInterfaceRequestClass(elementType)) 538 return this.validateInterfaceRequestElements( 539 elementsOffset, numElements, nullable); 540 if (isAssociatedInterfaceClass(elementType)) 541 return this.validateAssociatedInterfaceElements( 542 elementsOffset, numElements, nullable); 543 if (isAssociatedInterfaceRequestClass(elementType)) 544 return this.validateAssociatedInterfaceRequestElements( 545 elementsOffset, numElements, nullable); 546 if (isStringClass(elementType)) 547 return this.validateArrayElements( 548 elementsOffset, numElements, internal.Uint8, nullable, [0], 0); 549 if (elementType instanceof internal.PointerTo) 550 return this.validateStructElements( 551 elementsOffset, numElements, elementType.cls, nullable); 552 if (elementType instanceof internal.ArrayOf) 553 return this.validateArrayElements( 554 elementsOffset, numElements, elementType.cls, nullable, 555 expectedDimensionSizes, currentDimension + 1); 556 if (isEnumClass(elementType)) 557 return this.validateEnumElements(elementsOffset, numElements, 558 elementType.cls); 559 560 return validationError.NONE; 561 }; 562 563 // Note: the |offset + i * elementSize| computation in the validateFooElements 564 // methods below is "safe" because elementSize <= 8, offset and 565 // numElements are uint32, and 0 <= i < numElements. 566 567 Validator.prototype.validateHandleElements = 568 function(offset, numElements, nullable) { 569 var elementSize = internal.Handle.encodedSize; 570 for (var i = 0; i < numElements; i++) { 571 var elementOffset = offset + i * elementSize; 572 var err = this.validateHandle(elementOffset, nullable); 573 if (err != validationError.NONE) 574 return err; 575 } 576 return validationError.NONE; 577 }; 578 579 Validator.prototype.validateInterfaceElements = 580 function(offset, numElements, nullable) { 581 var elementSize = internal.Interface.prototype.encodedSize; 582 for (var i = 0; i < numElements; i++) { 583 var elementOffset = offset + i * elementSize; 584 var err = this.validateInterface(elementOffset, nullable); 585 if (err != validationError.NONE) 586 return err; 587 } 588 return validationError.NONE; 589 }; 590 591 Validator.prototype.validateInterfaceRequestElements = 592 function(offset, numElements, nullable) { 593 var elementSize = internal.InterfaceRequest.encodedSize; 594 for (var i = 0; i < numElements; i++) { 595 var elementOffset = offset + i * elementSize; 596 var err = this.validateInterfaceRequest(elementOffset, nullable); 597 if (err != validationError.NONE) 598 return err; 599 } 600 return validationError.NONE; 601 }; 602 603 Validator.prototype.validateAssociatedInterfaceElements = 604 function(offset, numElements, nullable) { 605 var elementSize = internal.AssociatedInterfacePtrInfo.prototype.encodedSize; 606 for (var i = 0; i < numElements; i++) { 607 var elementOffset = offset + i * elementSize; 608 var err = this.validateAssociatedInterface(elementOffset, nullable); 609 if (err != validationError.NONE) { 610 return err; 611 } 612 } 613 return validationError.NONE; 614 }; 615 616 Validator.prototype.validateAssociatedInterfaceRequestElements = 617 function(offset, numElements, nullable) { 618 var elementSize = internal.AssociatedInterfaceRequest.encodedSize; 619 for (var i = 0; i < numElements; i++) { 620 var elementOffset = offset + i * elementSize; 621 var err = this.validateAssociatedInterfaceRequest(elementOffset, 622 nullable); 623 if (err != validationError.NONE) { 624 return err; 625 } 626 } 627 return validationError.NONE; 628 }; 629 630 // The elementClass parameter is the element type of the element arrays. 631 Validator.prototype.validateArrayElements = 632 function(offset, numElements, elementClass, nullable, 633 expectedDimensionSizes, currentDimension) { 634 var elementSize = internal.PointerTo.prototype.encodedSize; 635 for (var i = 0; i < numElements; i++) { 636 var elementOffset = offset + i * elementSize; 637 var err = this.validateArrayPointer( 638 elementOffset, elementClass.encodedSize, elementClass, nullable, 639 expectedDimensionSizes, currentDimension); 640 if (err != validationError.NONE) 641 return err; 642 } 643 return validationError.NONE; 644 }; 645 646 Validator.prototype.validateStructElements = 647 function(offset, numElements, structClass, nullable) { 648 var elementSize = internal.PointerTo.prototype.encodedSize; 649 for (var i = 0; i < numElements; i++) { 650 var elementOffset = offset + i * elementSize; 651 var err = 652 this.validateStructPointer(elementOffset, structClass, nullable); 653 if (err != validationError.NONE) 654 return err; 655 } 656 return validationError.NONE; 657 }; 658 659 Validator.prototype.validateEnumElements = 660 function(offset, numElements, enumClass) { 661 var elementSize = internal.Enum.prototype.encodedSize; 662 for (var i = 0; i < numElements; i++) { 663 var elementOffset = offset + i * elementSize; 664 var err = this.validateEnum(elementOffset, enumClass); 665 if (err != validationError.NONE) 666 return err; 667 } 668 return validationError.NONE; 669 }; 670 671 internal.validationError = validationError; 672 internal.Validator = Validator; 673 internal.ValidationErrorObserverForTesting = 674 ValidationErrorObserverForTesting; 675 internal.reportValidationError = reportValidationError; 676 internal.isTestingMode = isTestingMode; 677 internal.clearTestingMode = clearTestingMode; 678})(); 679