Repository: mozilla/pdf.js
File: src/core/xref.js (JavaScript)

Code before:
      ) {
        return dict;
      }
      // The current dictionary is a candidate, but continue searching.
      trailerDict = dict;
    }
    // No trailer with 'ID', taking last one (if exists).
    if (trailerDict) {
      return trailerDict;
    }
    // No trailer dictionary found, taking the "top"-dictionary (if exists).
    if (this.topDict) {
      return this.topDict;
    }

    // When no trailer dictionary candidate exists, try picking the first
    // dictionary that contains a /Root entry (fixes issue18986.pdf).
    if (!trailerDicts.length) {
      // In case, this.entries is a sparse array we don't want to
      // iterate over empty entries so we use the `in` operator instead of
      // using for..of on entries() or a for with the array length.
      // The only drawback of for..in is that the keys are strings, so we need
      // to convert them to numbers when using them as array indices.
      for (const num in this.entries) {
        const entry = this.entries[num];
        if (!entry) {
          continue;
        }
        const ref = Ref.get(parseInt(num, 10), entry.gen);
        let obj;

        try {
          obj = this.fetch(ref);
        } catch {
          continue;
        }

        if (obj instanceof BaseStream) {
          obj = obj.dict;
        }
        if (obj instanceof Dict && obj.has("Root")) {
          return obj;
        }
      }
    }

    // nothing helps
    throw new InvalidPDFException("Invalid PDF structure.");
  }

  readXRef(recoveryMode = false) {

Review comment (suggestion):
If someone uses a non-exact polyfill for array methods that does, for example:
```js
Array.prototype.toSorted = function (compareFn) {
  return this.slice().sort(compareFn);
};
```

Then this `for-in` will also get `toSorted` as an array key. It's arguably a problem with the polyfill, but it's not uncommon to have them install methods like this.

It'd be better to either:
- do an `Object.hasOwn(this.entries, num)` check instead of the `if (!entry)` check, so that we `continue;` when we find an inherited property, or
- iterate over `Object.keys(this.entries)` instead of `for-in`, so that we do not even see inherited properties.

Code after:
      ) {
        return dict;
      }
      // The current dictionary is a candidate, but continue searching.
      trailerDict = dict;
    }
    // No trailer with 'ID', taking last one (if exists).
    if (trailerDict) {
      return trailerDict;
    }
    // No trailer dictionary found, taking the "top"-dictionary (if exists).
    if (this.topDict) {
      return this.topDict;
    }

    // When no trailer dictionary candidate exists, try picking the first
    // dictionary that contains a /Root entry (fixes issue18986.pdf).
    if (!trailerDicts.length) {
      // In case, this.entries is a sparse array we don't want to
      // iterate over empty entries so we use the `in` operator instead of
      // using for..of on entries() or a for with the array length.
      for (const num in this.entries) {
        if (!Object.hasOwn(this.entries, num)) {
          continue;
        }
        const entry = this.entries[num];
        const ref = Ref.get(parseInt(num), entry.gen);
        let obj;

        try {
          obj = this.fetch(ref);
        } catch {
          continue;
        }

        if (obj instanceof BaseStream) {
          obj = obj.dict;
        }
        if (obj instanceof Dict && obj.has("Root")) {
          return obj;
        }
      }
    }

    // nothing helps
    throw new InvalidPDFException("Invalid PDF structure.");
  }

  readXRef(recoveryMode = false) {
    const stream = this.stream;
    // Keep track of already parsed XRef tables, to prevent an infinite loop