nsresult
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
                         bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
                         bool* aIsReady, nsXBLBinding** aResult,
                         nsTArray<nsCOMPtr<nsIURI>>& aDontExtendURIs)
{
  NS_ASSERTION(aPeekOnly || aResult,
               "Must have non-null out param if not just peeking to see "
               "whether the binding is ready");

  if (aResult)
    *aResult = nullptr;

  if (!aURI)
    return NS_ERROR_FAILURE;

  nsAutoCString ref;
  aURI->GetRef(ref);

  nsCOMPtr<nsIDocument> boundDocument = aBoundElement->OwnerDoc();

  RefPtr<nsXBLDocumentInfo> docInfo;
  nsresult rv = LoadBindingDocumentInfo(aBoundElement, boundDocument, aURI,
                                        aOriginPrincipal,
                                        false, getter_AddRefs(docInfo));
  NS_ENSURE_SUCCESS(rv, rv);

  if (!docInfo)
    return NS_ERROR_FAILURE;

  WeakPtr<nsXBLPrototypeBinding> protoBinding =
    docInfo->GetPrototypeBinding(ref);

  if (!protoBinding) {
#ifdef DEBUG
    nsAutoCString uriSpec;
    aURI->GetSpec(uriSpec);
    nsAutoCString doc;
    boundDocument->GetDocumentURI()->GetSpec(doc);
    nsAutoCString message("Unable to locate an XBL binding for URI ");
    message += uriSpec;
    message += " in document ";
    message += doc;
    NS_WARNING(message.get());
#endif
    return NS_ERROR_FAILURE;
  }

  // If the binding isn't whitelisted, refuse to apply it to content that
  // doesn't subsume it (modulo a few exceptions).
  if (!MayBindToContent(protoBinding, aBoundElement, aURI)) {
#ifdef DEBUG
    nsAutoCString uriSpec;
    aURI->GetSpec(uriSpec);
    nsAutoCString message("Permission denied to apply binding ");
    message += uriSpec;
    message += " to unprivileged content. Set bindToUntrustedContent=true on "
               "the binding to override this restriction.";
    NS_WARNING(message.get());
#endif
   return NS_ERROR_FAILURE;
  }

  aDontExtendURIs.AppendElement(protoBinding->BindingURI());
  nsCOMPtr<nsIURI> altBindingURI = protoBinding->AlternateBindingURI();
  if (altBindingURI) {
    aDontExtendURIs.AppendElement(altBindingURI);
  }

  // Our prototype binding must have all its resources loaded.
  bool ready = protoBinding->LoadResources();
  if (!ready) {
    // Add our bound element to the protos list of elts that should
    // be notified when the stylesheets and scripts finish loading.
    protoBinding->AddResourceListener(aBoundElement);
    return NS_ERROR_FAILURE; // The binding isn't ready yet.
  }

  rv = protoBinding->ResolveBaseBinding();
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIURI> baseBindingURI;
  WeakPtr<nsXBLPrototypeBinding> baseProto = protoBinding->GetBasePrototype();
  if (baseProto) {
    baseBindingURI = baseProto->BindingURI();
  }
  else {
    baseBindingURI = protoBinding->GetBaseBindingURI();
    if (baseBindingURI) {
      uint32_t count = aDontExtendURIs.Length();
      for (uint32_t index = 0; index < count; ++index) {
        bool equal;
        rv = aDontExtendURIs[index]->Equals(baseBindingURI, &equal);
        NS_ENSURE_SUCCESS(rv, rv);
        if (equal) {
          nsAutoCString spec, basespec;
          protoBinding->BindingURI()->GetSpec(spec);
          NS_ConvertUTF8toUTF16 protoSpec(spec);
          baseBindingURI->GetSpec(basespec);
          NS_ConvertUTF8toUTF16 baseSpecUTF16(basespec);
          const char16_t* params[] = { protoSpec.get(), baseSpecUTF16.get() };
          nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                          NS_LITERAL_CSTRING("XBL"), nullptr,
                                          nsContentUtils::eXBL_PROPERTIES,
                                          "CircularExtendsBinding",
                                          params, ArrayLength(params),
                                          boundDocument->GetDocumentURI());
          return NS_ERROR_ILLEGAL_VALUE;
        }
      }
    }
  }

  RefPtr<nsXBLBinding> baseBinding;
  if (baseBindingURI) {
    nsCOMPtr<nsIContent> child = protoBinding->GetBindingElement();
    rv = GetBinding(aBoundElement, baseBindingURI, aPeekOnly,
                    child->NodePrincipal(), aIsReady,
                    getter_AddRefs(baseBinding), aDontExtendURIs);
    if (NS_FAILED(rv))
      return rv; // We aren't ready yet.
  }

  *aIsReady = true;

  if (!aPeekOnly) {
    // Make a new binding
    NS_ENSURE_STATE(protoBinding);
    nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);

    if (baseBinding) {
      if (!baseProto) {
        protoBinding->SetBasePrototype(baseBinding->PrototypeBinding());
      }
       newBinding->SetBaseBinding(baseBinding);
    }

    NS_ADDREF(*aResult = newBinding);
  }

  return NS_OK;
}