Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/webserial/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 10 kB image not shown  

Quelle  SerialPermissionRequest.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "SerialPermissionRequest.h"

#include "SerialLogging.h"
#include "js/PropertyAndElement.h"
#include "js/Wrapper.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/JSONStringWriteFuncs.h"
#include "mozilla/JSONWriter.h"
#include "mozilla/RandomNum.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "nsContentPermissionHelper.h"
#include "nsContentUtils.h"
#include "nsIArray.h"
#include "nsThreadUtils.h"
#include "xpcpublic.h"

namespace mozilla::dom {

constexpr uint32_t kPermissionDeniedBaseDelayMs = 3000;
constexpr uint32_t kPermissionDeniedMaxRandomDelayMs = 10000;

NS_IMPL_ISUPPORTS(SerialPermissionRequest, nsIContentPermissionRequest)

SerialPermissionRequest::SerialPermissionRequest(
    WindowGlobalParent* aWindowGlobalParent, bool aAutoselect,
    nsTArray<IPCSerialPortInfo>&& aPorts)
    : mWindowGlobalParent(aWindowGlobalParent),
      mAutoselect(aAutoselect),
      mPorts(std::move(aPorts)) {
  MOZ_ASSERT(mWindowGlobalParent);
  MOZ_ASSERT(mWindowGlobalParent->DocumentPrincipal());
}

nsIPrincipal* SerialPermissionRequest::Principal() const {
  return mWindowGlobalParent->DocumentPrincipal();
}

SerialPermissionRequest::~SerialPermissionRequest() {
  // Defensive: if no path settled the promise, fall back to InternalError so
  // the consumer always sees a decision.
  mPromiseHolder.RejectIfExists(RequestPortReason::InternalError, __func__);
}

bool SerialPermissionRequest::IsSitePermAllow() const {
  return nsContentUtils::IsSitePermAllow(Principal(), "serial"_ns);
}

bool SerialPermissionRequest::IsSitePermDeny() const {
  return nsContentUtils::IsSitePermDeny(Principal(), "serial"_ns);
}

bool SerialPermissionRequest::ShouldShowAddonGate() const {
  nsIPrincipal* principal = Principal();
  return StaticPrefs::dom_webserial_gated() &&
         StaticPrefs::dom_sitepermsaddon_provider_enabled() &&
         !IsSitePermAllow() && !principal->GetIsLoopbackHost() &&
         !principal->SchemeIs("file");
}

RefPtr<SerialChooserPromise> SerialPermissionRequest::Run() {
  AssertIsOnMainThread();

  RefPtr<SerialChooserPromise> promise = mPromiseHolder.Ensure(__func__);

  if (!IsSitePermAllow()) {
    if (IsSitePermDeny()) {
      CancelWithRandomizedDelay(RequestPortReason::AddonDenied);
      return promise;
    }
    if (nsContentUtils::IsSitePermDeny(Principal(), "install"_ns) &&
        StaticPrefs::dom_sitepermsaddon_provider_enabled() &&
        !Principal()->GetIsLoopbackHost()) {
      CancelWithRandomizedDelay(RequestPortReason::AddonDenied);
      return promise;
    }
  }

  // Testing-only autoselect bypass: when the testing pref is on and gated
  // mode is off, we skip the prompt entirely and pick the first port. The
  // bypass is only honored when testing is enabled, so a malicious content
  // process cannot use the autoselect flag to skip the chooser in release.
  if (mAutoselect && StaticPrefs::dom_webserial_testing_enabled() &&
      !StaticPrefs::dom_webserial_gated()) {
    if (mPorts.IsEmpty()) {
      ResolveCancelled(RequestPortReason::UserCancelled);
      return promise;
    }
    ResolveWithPort(mPorts[0]);
    return promise;
  }

  if (NS_FAILED(DoPrompt())) {
    ResolveCancelled(RequestPortReason::InternalError);
  }
  return promise;
}

void SerialPermissionRequest::CancelWithRandomizedDelay(
    RequestPortReason aReason) {
  AssertIsOnMainThread();

  uint32_t randomDelayMS =
      xpc::IsInAutomation()
          ? 0
          : RandomUint64OrDie() % kPermissionDeniedMaxRandomDelayMs;
  auto delay = TimeDuration::FromMilliseconds(kPermissionDeniedBaseDelayMs +
                                              randomDelayMS);
  NS_NewTimerWithCallback(
      getter_AddRefs(mCancelTimer),
      [self = RefPtr{this}, aReason](auto) { self->ResolveCancelled(aReason); },
      delay, nsITimer::TYPE_ONE_SHOT,
      "SerialPermissionRequest::CancelWithRandomizedDelay"_ns);
}

nsresult SerialPermissionRequest::DoPrompt() {
  return nsContentPermissionUtils::AskPermission(this, /* aWindow */ nullptr);
}

void SerialPermissionRequest::ResolveWithPort(const IPCSerialPortInfo& aPort) {
  mCancelTimer = nullptr;
  MOZ_LOG(gWebSerialLog, LogLevel::Info,
          ("    friendlyName: %s",
           NS_ConvertUTF16toUTF8(aPort.friendlyName()).get()));
  if (aPort.usbVendorId().isSome()) {
    MOZ_LOG(gWebSerialLog, LogLevel::Info,
            ("    usbVendorId: 0x%04x", aPort.usbVendorId().value()));
  }
  if (aPort.usbProductId().isSome()) {
    MOZ_LOG(gWebSerialLog, LogLevel::Info,
            ("    usbProductId: 0x%04x", aPort.usbProductId().value()));
  }

  mPromiseHolder.ResolveIfExists(aPort, __func__);
}

void SerialPermissionRequest::ResolveCancelled(RequestPortReason aReason) {
  mCancelTimer = nullptr;
  mPromiseHolder.RejectIfExists(aReason, __func__);
}

NS_IMETHODIMP
SerialPermissionRequest::GetTypes(nsIArray** aTypes) {
  NS_ENSURE_ARG_POINTER(aTypes);

  MOZ_LOG(gWebSerialLog, LogLevel::Info,
          ("SerialPermissionRequest::GetTypes called with %zu ports",
           mPorts.Length()));

  nsTArray<nsString> options;

  // Pass the autoselect flag to the JS UI so that it can auto-confirm the
  // chooser when testing+gated is in use.
  if (mAutoselect && StaticPrefs::dom_webserial_testing_enabled()) {
    options.AppendElement(u"{\"__autoselect__\":true}"_ns);
  }

  size_t index = 0;
  for (const auto& port : mPorts) {
    nsCString utf8Json;
    JSONStringRefWriteFunc writeFunc(utf8Json);
    JSONWriter writer(writeFunc, JSONWriter::SingleLineStyle);

    NS_ConvertUTF16toUTF8 utf8Path(port.path());
    NS_ConvertUTF16toUTF8 utf8FriendlyName(port.friendlyName());

    MOZ_LOG(gWebSerialLog, LogLevel::Debug,
            ("  Port[%zu]: path=%s, friendlyName=%s, vid=0x%04x, pid=0x%04x",
             index, utf8Path.get(), utf8FriendlyName.get(),
             port.usbVendorId().valueOr(0), port.usbProductId().valueOr(0)));

    writer.StartObjectElement();
    writer.StringProperty("path", MakeStringSpan(utf8Path.get()));
    writer.StringProperty("friendlyName",
                          MakeStringSpan(utf8FriendlyName.get()));
    writer.IntProperty("usbVendorId", port.usbVendorId().valueOr(0));
    writer.IntProperty("usbProductId", port.usbProductId().valueOr(0));
    writer.EndObject();

    nsString utf16Json;
    CopyUTF8toUTF16(utf8Json, utf16Json);
    options.AppendElement(std::move(utf16Json));
    ++index;
  }

  return nsContentPermissionUtils::CreatePermissionArray("serial"_ns, options,
                                                         aTypes);
}

NS_IMETHODIMP
SerialPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal) {
  NS_IF_ADDREF(*aPrincipal = Principal());
  return NS_OK;
}

NS_IMETHODIMP
SerialPermissionRequest::GetTopLevelPrincipal(nsIPrincipal** aPrincipal) {
  NS_IF_ADDREF(*aPrincipal = mWindowGlobalParent->TopWindowContext()
                                 ->DocumentPrincipal());
  return NS_OK;
}

NS_IMETHODIMP
SerialPermissionRequest::GetDelegatePrincipal(const nsACString& aType,
                                              nsIPrincipal** aPrincipal) {
  return PermissionDelegateHandler::GetDelegatePrincipal(aType, this,
                                                         aPrincipal);
}

NS_IMETHODIMP
SerialPermissionRequest::GetWindow(mozIDOMWindow** aWindow) {
  *aWindow = nullptr;
  return NS_OK;
}

NS_IMETHODIMP
SerialPermissionRequest::GetElement(Element** aElement) {
  NS_ENSURE_ARG_POINTER(aElement);
  RefPtr<Element> element = mWindowGlobalParent->GetRootOwnerElement();
  element.forget(aElement);
  return NS_OK;
}

NS_IMETHODIMP
SerialPermissionRequest::GetHasValidTransientUserGestureActivation(bool* aOut) {
  // The content process already rejects requestPort() without transient
  // user activation (Serial::RequestPort step 3), so the parent-side request
  // can report true without re-querying WindowContext (whose transient-
  // activation state is only tracked in-process and would assert here).
  *aOut = true;
  return NS_OK;
}

NS_IMETHODIMP
SerialPermissionRequest::GetIsRequestDelegatedToUnsafeThirdParty(bool* aOut) {
  // The parent process does not have a permission delegate handler available
  // (that lives on the document) so we conservatively report false.
  *aOut = false;
  return NS_OK;
}

NS_IMETHODIMP
SerialPermissionRequest::GetIgnoreAllowSitePermission(bool* aOut) {
  *aOut = false;
  return NS_OK;
}

NS_IMETHODIMP
SerialPermissionRequest::NotifyShown() { return NS_OK; }

NS_IMETHODIMP
SerialPermissionRequest::Cancel() {
  AssertIsOnMainThread();
  mCancelTimer = nullptr;

  RequestPortReason reason = RequestPortReason::UserCancelled;
  if (ShouldShowAddonGate()) {
    reason = RequestPortReason::AddonDenied;
  }

  ResolveCancelled(reason);
  return NS_OK;
}

NS_IMETHODIMP
SerialPermissionRequest::Allow(JS::Handle<JS::Value> aChoices) {
  AssertIsOnMainThread();
  mCancelTimer = nullptr;

  if (!aChoices.isObject()) {
    ResolveCancelled(RequestPortReason::InternalError);
    return NS_OK;
  }

  // The choices object can come in any realm — chrome calls Allow() from JS.
  // Unwrap and enter its realm before reading properties.
  JS::Rooted<JSObject*> obj(RootingCx(), &aChoices.toObject());
  obj = js::CheckedUnwrapStatic(obj);
  if (!obj) {
    ResolveCancelled(RequestPortReason::InternalError);
    return NS_OK;
  }

  nsIGlobalObject* global = xpc::NativeGlobal(obj);
  if (!global) {
    ResolveCancelled(RequestPortReason::InternalError);
    return NS_OK;
  }

  AutoJSAPI jsapi;
  if (!jsapi.Init(global)) {
    ResolveCancelled(RequestPortReason::InternalError);
    return NS_OK;
  }
  JSContext* cx = jsapi.cx();

  JS::Rooted<JS::Value> serialVal(cx);
  if (!JS_GetProperty(cx, obj, "serial", &serialVal) || !serialVal.isString()) {
    ResolveCancelled(RequestPortReason::InternalError);
    return NS_OK;
  }

  nsAutoJSString choice;
  if (!choice.init(cx, serialVal)) {
    ResolveCancelled(RequestPortReason::InternalError);
    return NS_OK;
  }

  nsresult rv;
  int32_t selectedIndex = choice.ToInteger(&rv);
  if (NS_FAILED(rv) || selectedIndex < 0 ||
      static_cast<size_t>(selectedIndex) >= mPorts.Length()) {
    ResolveCancelled(RequestPortReason::InternalError);
    return NS_OK;
  }

  MOZ_LOG(gWebSerialLog, LogLevel::Info,
          ("  Selected port at index %d:", selectedIndex));
  ResolveWithPort(mPorts[selectedIndex]);
  return NS_OK;
}

}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=91 H=95 G=92

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.