tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

JSWindowActorProtocol.cpp (12730B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 
      3 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "mozilla/dom/JSWindowActorProtocol.h"
      9 
     10 #include "JSActorProtocolUtils.h"
     11 #include "mozilla/dom/ContentParent.h"
     12 #include "mozilla/dom/Event.h"
     13 #include "mozilla/dom/JSActorBinding.h"
     14 #include "mozilla/dom/JSActorService.h"
     15 #include "mozilla/dom/JSWindowActorBinding.h"
     16 #include "mozilla/dom/JSWindowActorChild.h"
     17 #include "mozilla/dom/PContent.h"
     18 #include "mozilla/dom/WindowGlobalChild.h"
     19 #include "mozilla/extensions/MatchPattern.h"
     20 #include "nsContentUtils.h"
     21 
     22 namespace mozilla::dom {
     23 
     24 NS_IMPL_CYCLE_COLLECTING_ADDREF(JSWindowActorProtocol)
     25 NS_IMPL_CYCLE_COLLECTING_RELEASE(JSWindowActorProtocol)
     26 
     27 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorProtocol)
     28  NS_INTERFACE_MAP_ENTRY(nsIObserver)
     29  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
     30 NS_INTERFACE_MAP_END
     31 
     32 NS_IMPL_CYCLE_COLLECTION(JSWindowActorProtocol)
     33 
     34 /* static */ already_AddRefed<JSWindowActorProtocol>
     35 JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
     36  MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
     37 
     38  RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aInfo.name());
     39  JSActorProtocolUtils::FromIPCShared(proto, aInfo);
     40 
     41  // Content processes cannot load chrome browsing contexts, so this flag is
     42  // irrelevant and not propagated.
     43  proto->mIncludeChrome = false;
     44  proto->mAllFrames = aInfo.allFrames();
     45  proto->mMatches = aInfo.matches().Clone();
     46  proto->mMessageManagerGroups = aInfo.messageManagerGroups().Clone();
     47 
     48  proto->mChild.mEvents.SetCapacity(aInfo.events().Length());
     49  for (auto& ipc : aInfo.events()) {
     50    auto event = proto->mChild.mEvents.AppendElement();
     51    event->mName.Assign(ipc.name());
     52    event->mFlags.mCapture = ipc.capture();
     53    event->mFlags.mInSystemGroup = ipc.systemGroup();
     54    event->mFlags.mAllowUntrustedEvents = ipc.allowUntrusted();
     55    if (ipc.passive()) {
     56      event->mPassive.Construct(ipc.passive().value());
     57    }
     58    event->mCreateActor = ipc.createActor();
     59  }
     60 
     61  return proto.forget();
     62 }
     63 
     64 JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
     65  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
     66 
     67  JSWindowActorInfo info;
     68  JSActorProtocolUtils::ToIPCShared(info, this);
     69 
     70  info.allFrames() = mAllFrames;
     71  info.matches() = mMatches.Clone();
     72  info.messageManagerGroups() = mMessageManagerGroups.Clone();
     73 
     74  info.events().SetCapacity(mChild.mEvents.Length());
     75  for (auto& event : mChild.mEvents) {
     76    auto ipc = info.events().AppendElement();
     77    ipc->name().Assign(event.mName);
     78    ipc->capture() = event.mFlags.mCapture;
     79    ipc->systemGroup() = event.mFlags.mInSystemGroup;
     80    ipc->allowUntrusted() = event.mFlags.mAllowUntrustedEvents;
     81    if (event.mPassive.WasPassed()) {
     82      ipc->passive() = Some(event.mPassive.Value());
     83    }
     84    ipc->createActor() = event.mCreateActor;
     85  }
     86 
     87  return info;
     88 }
     89 
     90 already_AddRefed<JSWindowActorProtocol>
     91 JSWindowActorProtocol::FromWebIDLOptions(const nsACString& aName,
     92                                         const WindowActorOptions& aOptions,
     93                                         ErrorResult& aRv) {
     94  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
     95 
     96  RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aName);
     97  if (!JSActorProtocolUtils::FromWebIDLOptionsShared(proto, aOptions, aRv)) {
     98    return nullptr;
     99  }
    100 
    101  proto->mAllFrames = aOptions.mAllFrames;
    102  proto->mIncludeChrome = aOptions.mIncludeChrome;
    103 
    104  if (aOptions.mMatches.WasPassed()) {
    105    MOZ_ASSERT(aOptions.mMatches.Value().Length());
    106    proto->mMatches = aOptions.mMatches.Value();
    107  }
    108 
    109  if (aOptions.mMessageManagerGroups.WasPassed()) {
    110    proto->mMessageManagerGroups = aOptions.mMessageManagerGroups.Value();
    111  }
    112 
    113  // For each event declared in the source dictionary, initialize the
    114  // corresponding event declaration entry in the JSWindowActorProtocol.
    115  if (aOptions.mChild.WasPassed() &&
    116      aOptions.mChild.Value().mEvents.WasPassed()) {
    117    auto& entries = aOptions.mChild.Value().mEvents.Value().Entries();
    118    proto->mChild.mEvents.SetCapacity(entries.Length());
    119 
    120    for (auto& entry : entries) {
    121      // We don't support the mOnce field, as it doesn't work well in this
    122      // environment. For now, throw an error in that case.
    123      if (entry.mValue.mOnce) {
    124        aRv.ThrowNotSupportedError("mOnce is not supported");
    125        return nullptr;
    126      }
    127 
    128      // Add the EventDecl to our list of events.
    129      EventDecl* evt = proto->mChild.mEvents.AppendElement();
    130      evt->mName = entry.mKey;
    131      evt->mFlags.mCapture = entry.mValue.mCapture;
    132      evt->mFlags.mInSystemGroup = entry.mValue.mMozSystemGroup;
    133      evt->mFlags.mAllowUntrustedEvents =
    134          entry.mValue.mWantUntrusted.WasPassed()
    135              ? entry.mValue.mWantUntrusted.Value()
    136              : false;
    137      if (entry.mValue.mPassive.WasPassed()) {
    138        evt->mPassive.Construct(entry.mValue.mPassive.Value());
    139      }
    140      evt->mCreateActor = entry.mValue.mCreateActor;
    141    }
    142  }
    143 
    144  return proto.forget();
    145 }
    146 
    147 /**
    148 * This listener only listens for events for the child side of the protocol.
    149 * This will work in both content and parent processes.
    150 */
    151 NS_IMETHODIMP JSWindowActorProtocol::HandleEvent(Event* aEvent) {
    152  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
    153 
    154  // Determine which inner window we're associated with, and get its
    155  // WindowGlobalChild actor.
    156  EventTarget* target = aEvent->GetOriginalTarget();
    157  if (NS_WARN_IF(!target)) {
    158    return NS_ERROR_FAILURE;
    159  }
    160 
    161  nsCOMPtr<nsPIDOMWindowInner> inner =
    162      do_QueryInterface(target->GetOwnerGlobal());
    163  if (!inner) {
    164    return NS_ERROR_FAILURE;
    165  }
    166 
    167  RefPtr<WindowGlobalChild> wgc = inner->GetWindowGlobalChild();
    168  if (NS_WARN_IF(!wgc)) {
    169    return NS_ERROR_FAILURE;
    170  }
    171 
    172  if (aEvent->ShouldIgnoreChromeEventTargetListener()) {
    173    return NS_OK;
    174  }
    175 
    176  // Ensure our actor is present.
    177  RefPtr<JSActor> actor = wgc->GetExistingActor(mName);
    178  if (!actor) {
    179    // Check if we're supposed to create the actor when this event is fired.
    180    bool createActor = true;
    181    nsAutoString typeStr;
    182    aEvent->GetType(typeStr);
    183    for (auto& event : mChild.mEvents) {
    184      if (event.mName == typeStr) {
    185        createActor = event.mCreateActor;
    186        break;
    187      }
    188    }
    189 
    190    // If we're supposed to create the actor, call GetActor to cause it to be
    191    // created.
    192    if (createActor) {
    193      AutoJSAPI jsapi;
    194      jsapi.Init();
    195      actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors());
    196    }
    197  }
    198  if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) {
    199    return NS_OK;
    200  }
    201 
    202  // Build our event listener & call it.
    203  JS::Rooted<JSObject*> global(RootingCx(),
    204                               JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
    205  RefPtr<EventListener> eventListener =
    206      new EventListener(actor->GetWrapper(), global, nullptr, nullptr);
    207  eventListener->HandleEvent(*aEvent, "JSWindowActorProtocol::HandleEvent");
    208  return NS_OK;
    209 }
    210 
    211 NS_IMETHODIMP JSWindowActorProtocol::Observe(nsISupports* aSubject,
    212                                             const char* aTopic,
    213                                             const char16_t* aData) {
    214  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
    215 
    216  nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aSubject);
    217  RefPtr<WindowGlobalChild> wgc;
    218 
    219  if (!inner) {
    220    nsCOMPtr<nsPIDOMWindowOuter> outer = do_QueryInterface(aSubject);
    221    if (NS_WARN_IF(!outer)) {
    222      nsContentUtils::LogSimpleConsoleError(
    223          NS_ConvertUTF8toUTF16(nsPrintfCString(
    224              "JSWindowActor %s: expected window subject for topic '%s'.",
    225              mName.get(), aTopic)),
    226          "JSActor"_ns,
    227          /* aFromPrivateWindow */ false,
    228          /* aFromChromeContext */ true);
    229      return NS_ERROR_FAILURE;
    230    }
    231    if (NS_WARN_IF(!outer->GetCurrentInnerWindow())) {
    232      return NS_ERROR_FAILURE;
    233    }
    234    wgc = outer->GetCurrentInnerWindow()->GetWindowGlobalChild();
    235  } else {
    236    wgc = inner->GetWindowGlobalChild();
    237  }
    238 
    239  if (NS_WARN_IF(!wgc)) {
    240    return NS_ERROR_FAILURE;
    241  }
    242 
    243  // Ensure our actor is present.
    244  AutoJSAPI jsapi;
    245  jsapi.Init();
    246  RefPtr<JSActor> actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors());
    247  if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) {
    248    return NS_OK;
    249  }
    250 
    251  // Build a observer callback.
    252  JS::Rooted<JSObject*> global(jsapi.cx(),
    253                               JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
    254  RefPtr<MozObserverCallback> observerCallback =
    255      new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr);
    256  observerCallback->Observe(aSubject, nsDependentCString(aTopic),
    257                            aData ? nsDependentString(aData) : VoidString());
    258  return NS_OK;
    259 }
    260 
    261 void JSWindowActorProtocol::RegisterListenersFor(EventTarget* aTarget) {
    262  EventListenerManager* elm = aTarget->GetOrCreateListenerManager();
    263 
    264  for (auto& event : mChild.mEvents) {
    265    elm->AddEventListenerByType(EventListenerHolder(this), event.mName,
    266                                event.mFlags, event.mPassive);
    267  }
    268 }
    269 
    270 void JSWindowActorProtocol::UnregisterListenersFor(EventTarget* aTarget) {
    271  EventListenerManager* elm = aTarget->GetOrCreateListenerManager();
    272 
    273  for (auto& event : mChild.mEvents) {
    274    elm->RemoveEventListenerByType(EventListenerHolder(this), event.mName,
    275                                   event.mFlags);
    276  }
    277 }
    278 
    279 void JSWindowActorProtocol::AddObservers() {
    280  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    281  for (auto& topic : mChild.mObservers) {
    282    // This makes the observer service hold an owning reference to the
    283    // JSWindowActorProtocol. The JSWindowActorProtocol objects will be living
    284    // for the full lifetime of the content process, thus the extra strong
    285    // referencec doesn't have a negative impact.
    286    os->AddObserver(this, topic.get(), false);
    287  }
    288 }
    289 
    290 void JSWindowActorProtocol::RemoveObservers() {
    291  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    292  for (auto& topic : mChild.mObservers) {
    293    os->RemoveObserver(this, topic.get());
    294  }
    295 }
    296 
    297 extensions::MatchPatternSetCore* JSWindowActorProtocol::GetURIMatcher() {
    298  // If we've already created the pattern set, return it.
    299  if (mURIMatcher || mMatches.IsEmpty()) {
    300    return mURIMatcher;
    301  }
    302 
    303  nsTArray<RefPtr<extensions::MatchPatternCore>> patterns(mMatches.Length());
    304  for (const nsString& pattern : mMatches) {
    305    patterns.AppendElement(new extensions::MatchPatternCore(
    306        pattern, false, false, IgnoreErrors()));
    307  }
    308  mURIMatcher = new extensions::MatchPatternSetCore(std::move(patterns));
    309  return mURIMatcher;
    310 }
    311 
    312 bool JSWindowActorProtocol::MessageManagerGroupMatches(
    313    BrowsingContext* aBrowsingContext) {
    314  BrowsingContext* top = aBrowsingContext->Top();
    315  for (auto& group : mMessageManagerGroups) {
    316    if (group == top->GetMessageManagerGroup()) {
    317      return true;
    318    }
    319  }
    320  return false;
    321 }
    322 
    323 bool JSWindowActorProtocol::Matches(BrowsingContext* aBrowsingContext,
    324                                    nsIURI* aURI, const nsACString& aRemoteType,
    325                                    ErrorResult& aRv) {
    326  MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
    327  MOZ_ASSERT(aURI, "Must have URI!");
    328 
    329  if (!mAllFrames && aBrowsingContext->GetParent()) {
    330    aRv.ThrowNotSupportedError(nsPrintfCString(
    331        "Window protocol '%s' doesn't match subframes", mName.get()));
    332    return false;
    333  }
    334 
    335  if (!mIncludeChrome && !aBrowsingContext->IsContent()) {
    336    aRv.ThrowNotSupportedError(nsPrintfCString(
    337        "Window protocol '%s' doesn't match chrome browsing contexts",
    338        mName.get()));
    339    return false;
    340  }
    341 
    342  if (!RemoteTypePrefixMatches(aRemoteType)) {
    343    aRv.ThrowNotSupportedError(
    344        nsPrintfCString("Window protocol '%s' doesn't match remote type '%s'",
    345                        mName.get(), PromiseFlatCString(aRemoteType).get()));
    346    return false;
    347  }
    348 
    349  if (!mMessageManagerGroups.IsEmpty() &&
    350      !MessageManagerGroupMatches(aBrowsingContext)) {
    351    aRv.ThrowNotSupportedError(nsPrintfCString(
    352        "Window protocol '%s' doesn't match message manager group",
    353        mName.get()));
    354    return false;
    355  }
    356 
    357  if (extensions::MatchPatternSetCore* uriMatcher = GetURIMatcher()) {
    358    if (!uriMatcher->Matches(aURI)) {
    359      aRv.ThrowNotSupportedError(nsPrintfCString(
    360          "Window protocol '%s' doesn't match uri %s", mName.get(),
    361          nsContentUtils::TruncatedURLForDisplay(aURI).get()));
    362      return false;
    363    }
    364  }
    365 
    366  return true;
    367 }
    368 
    369 }  // namespace mozilla::dom