static combineAttributes(
      fixedAttributes, defaultAttributes, attributes = undefined) {
    const combinedAttributes = {};

    for (const name in fixedAttributes) {
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty#Using_hasOwnProperty_as_a_property_name
      if (Object.prototype.hasOwnProperty.call(fixedAttributes, name)) {
        asserts.assert(name.toLowerCase() == name, 'Must be lower case');
        combinedAttributes[name] = fixedAttributes[name];
      }
    }
    for (const name in defaultAttributes) {
      if (Object.prototype.hasOwnProperty.call(defaultAttributes, name)) {
        asserts.assert(name.toLowerCase() == name, 'Must be lower case');
        combinedAttributes[name] = defaultAttributes[name];
      }
    }

    if (attributes) {
      for (const name in attributes) {
        if (Object.prototype.hasOwnProperty.call(attributes, name)) {
          const nameLower = name.toLowerCase();
          if (nameLower in fixedAttributes) {
            throw new Error(
                SafeHtml.ENABLE_ERROR_MESSAGES ?
                    `Cannot override "${nameLower}" attribute, got "` + name +
                        '" with value "' + attributes[name] + '"' :
                    '');
          }
          if (nameLower in defaultAttributes) {
            delete combinedAttributes[nameLower];
          }
          combinedAttributes[name] = attributes[name];
        }
      }
    }

    return combinedAttributes;
  }
@param {!Object<string, ?SafeHtml.AttributeValue>} fixedAttributes
@param {!Object<string, string>} defaultAttributes
@param {?Object<string, ?SafeHtml.AttributeValue>=} attributes  Optional
    attributes passed to create*().
@return {!Object<string, ?SafeHtml.AttributeValue>}
@throws {!Error} If attributes contains an attribute with the same name as
    an attribute in fixedAttributes.
@package