Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  DocumentL10n.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "DocumentL10n.h"
#include "nsIContentSink.h"
#include "nsContentUtils.h"
#include "mozilla/dom/AutoEntryScript.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentL10nBinding.h"

using namespace mozilla;
using namespace mozilla::intl;
using namespace mozilla::dom;

NS_IMPL_CYCLE_COLLECTION_CLASS(DocumentL10n)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocumentL10n, DOMLocalization)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContentSink)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocumentL10n, DOMLocalization)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentSink)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_ADDREF_INHERITED(DocumentL10n, DOMLocalization)
NS_IMPL_RELEASE_INHERITED(DocumentL10n, DOMLocalization)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentL10n)
NS_INTERFACE_MAP_END_INHERITING(DOMLocalization)

/* static */
RefPtr<DocumentL10n> DocumentL10n::Create(Document* aDocument, bool aSync) {
  RefPtr<DocumentL10n> l10n = new DocumentL10n(aDocument, aSync);

  IgnoredErrorResult rv;
  l10n->mReady = Promise::Create(l10n->mGlobal, rv);
  if (NS_WARN_IF(rv.Failed())) {
    return nullptr;
  }

  return l10n.forget();
}

DocumentL10n::DocumentL10n(Document* aDocument, bool aSync)
    : DOMLocalization(aDocument->GetScopeObject(), aSync),
      mDocument(aDocument),
      mState(DocumentL10nState::Constructed) {
  mContentSink = do_QueryInterface(aDocument->GetCurrentContentSink());
}

JSObject* DocumentL10n::WrapObject(JSContext* aCx,
                                   JS::Handle<JSObject*> aGivenProto) {
  return DocumentL10n_Binding::Wrap(aCx, this, aGivenProto);
}

class L10nReadyHandler final : public PromiseNativeHandler {
 public:
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  NS_DECL_CYCLE_COLLECTION_CLASS(L10nReadyHandler)

  explicit L10nReadyHandler(Promise* aPromise, DocumentL10n* aDocumentL10n)
      : mPromise(aPromise), mDocumentL10n(aDocumentL10n) {}

  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                        ErrorResult& aRv) override {
    mDocumentL10n->InitialTranslationCompleted(true);
    mPromise->MaybeResolveWithUndefined();
  }

  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                        ErrorResult& aRv) override {
    mDocumentL10n->InitialTranslationCompleted(false);

    nsTArray<nsCString> errors{
        "[dom/l10n] Could not complete initial document translation."_ns,
    };
    IgnoredErrorResult rv;
    MaybeReportErrorsToGecko(errors, rv, mDocumentL10n->GetParentObject());

    /**
     * We resolve the mReady here even if we encountered failures, because
     * there is nothing actionable for the user pending on `mReady` to do here
     * and we don't want to incentivized consumers of this API to plan the
     * same pending operation for resolve and reject scenario.
     *
     * Additionally, without it, the stderr received "uncaught promise
     * rejection" warning, which is noisy and not-actionable.
     *
     * So instead, we just resolve and report errors.
     *
     * However, in automated tests we do reject so that errors with missing
     * messages and resources can be caught.
     */

    if (xpc::IsInAutomation()) {
      mPromise->MaybeRejectWithClone(aCx, aValue);
    } else {
      mPromise->MaybeResolveWithUndefined();
    }
  }

 private:
  ~L10nReadyHandler() = default;

  RefPtr<Promise> mPromise;
  RefPtr<DocumentL10n> mDocumentL10n;
};

NS_IMPL_CYCLE_COLLECTION(L10nReadyHandler, mPromise, mDocumentL10n)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(L10nReadyHandler)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(L10nReadyHandler)
NS_IMPL_CYCLE_COLLECTING_RELEASE(L10nReadyHandler)

void DocumentL10n::TriggerInitialTranslation() {
  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
  if (mState >= DocumentL10nState::InitialTranslationTriggered) {
    return;
  }
  if (!mReady) {
    // If we don't have `mReady` it means that we are in shutdown mode.
    // See bug 1687118 for details.
    InitialTranslationCompleted(false);
    return;
  }

  AutoAllowLegacyScriptExecution exemption;

  nsTArray<RefPtr<Promise>> promises;

  ErrorResult rv;
  promises.AppendElement(TranslateDocument(rv));
  if (NS_WARN_IF(rv.Failed())) {
    rv.SuppressException();
    InitialTranslationCompleted(false);
    mReady->MaybeRejectWithUndefined();
    return;
  }
  promises.AppendElement(TranslateRoots(rv));
  Element* documentElement = mDocument->GetDocumentElement();
  if (!documentElement) {
    InitialTranslationCompleted(false);
    mReady->MaybeRejectWithUndefined();
    return;
  }

  DOMLocalization::ConnectRoot(*documentElement);

  AutoEntryScript aes(mGlobal, "DocumentL10n InitialTranslation");
  RefPtr<Promise> promise = Promise::All(aes.cx(), promises, rv);

  if (promise->State() == Promise::PromiseState::Resolved) {
    // If the promise is already resolved, we can fast-track
    // to initial translation completed.
    InitialTranslationCompleted(true);
    mReady->MaybeResolveWithUndefined();
  } else {
    RefPtr<PromiseNativeHandler> l10nReadyHandler =
        new L10nReadyHandler(mReady, this);
    promise->AppendNativeHandler(l10nReadyHandler);

    mState = DocumentL10nState::InitialTranslationTriggered;
  }
}

already_AddRefed<Promise> DocumentL10n::TranslateDocument(ErrorResult& aRv) {
  MOZ_ASSERT(mState == DocumentL10nState::Constructed,
             "This method should be called only from Constructed state.");
  RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return nullptr;
  }

  Element* elem = mDocument->GetDocumentElement();
  if (!elem) {
    promise->MaybeRejectWithUndefined();
    return promise.forget();
  }

  // 1. Collect all localizable elements.
  Sequence<OwningNonNull<Element>> elements;
  GetTranslatables(*elem, elements, aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    promise->MaybeRejectWithUndefined();
    return promise.forget();
  }

  RefPtr<nsXULPrototypeDocument> proto = mDocument->GetPrototype();

  // 2. Check if the document has a prototype that may cache
  //    translated elements.
  if (proto) {
    // 2.1. Handle the case when we have proto.

    // 2.1.1. Move elements that are not in the proto to a separate
    //        array.
    Sequence<OwningNonNull<Element>> nonProtoElements;

    uint32_t i = elements.Length();
    while (i > 0) {
      Element* elem = elements.ElementAt(i - 1);
      MOZ_RELEASE_ASSERT(elem->HasAttr(nsGkAtoms::datal10nid));
      if (!elem->HasElementCreatedFromPrototypeAndHasUnmodifiedL10n()) {
        if (NS_WARN_IF(!nonProtoElements.AppendElement(*elem, fallible))) {
          promise->MaybeRejectWithUndefined();
          return promise.forget();
        }
        elements.RemoveElement(elem);
      }
      i--;
    }

    // We populate the sequence in reverse order. Let's bring it
    // back to top->bottom one.
    nonProtoElements.Reverse();

    AutoAllowLegacyScriptExecution exemption;

    nsTArray<RefPtr<Promise>> promises;

    // 2.1.2. If we're not loading from cache, push the elements that
    //        are in the prototype to be translated and cached.
    if (!proto->WasL10nCached() && !elements.IsEmpty()) {
      RefPtr<Promise> translatePromise =
          TranslateElements(elements, proto, aRv);
      if (NS_WARN_IF(!translatePromise || aRv.Failed())) {
        promise->MaybeRejectWithUndefined();
        return promise.forget();
      }
      promises.AppendElement(translatePromise);
    }

    // 2.1.3. If there are elements that are not in the prototype,
    //        localize them without attempting to cache and
    //        independently of if we're loading from cache.
    if (!nonProtoElements.IsEmpty()) {
      RefPtr<Promise> nonProtoTranslatePromise =
          TranslateElements(nonProtoElements, nullptr, aRv);
      if (NS_WARN_IF(!nonProtoTranslatePromise || aRv.Failed())) {
        promise->MaybeRejectWithUndefined();
        return promise.forget();
      }
      promises.AppendElement(nonProtoTranslatePromise);
    }

    // 2.1.4. Collect promises with Promise::All (maybe empty).
    AutoEntryScript aes(mGlobal, "DocumentL10n InitialTranslationCompleted");
    promise = Promise::All(aes.cx(), promises, aRv);
    if (NS_WARN_IF(aRv.Failed())) {
      return nullptr;
    }
  } else {
    // 2.2. Handle the case when we don't have proto.

    // 2.2.1. Otherwise, translate all available elements,
    //        without attempting to cache them.
    promise = TranslateElements(elements, nullptr, aRv);
    if (NS_WARN_IF(aRv.Failed())) {
      return nullptr;
    }
  }

  return promise.forget();
}

void DocumentL10n::InitialTranslationCompleted(bool aL10nCached) {
  if (mState >= DocumentL10nState::Ready) {
    return;
  }

  Element* documentElement = mDocument->GetDocumentElement();
  if (documentElement) {
    SetRootInfo(documentElement);
  }

  mState = DocumentL10nState::Ready;

  RefPtr<Document> doc = mDocument;
  doc->InitialTranslationCompleted(aL10nCached);

  // In XUL scenario contentSink is nullptr.
  if (mContentSink) {
    nsCOMPtr<nsIContentSink> sink = mContentSink.forget();
    sink->InitialTranslationCompleted();
  }

  // From now on, the state of Localization is unconditionally
  // async.
  SetAsync();
}

void DocumentL10n::ConnectRoot(nsINode& aNode, bool aTranslate,
                               ErrorResult& aRv) {
  if (aTranslate) {
    if (mState >= DocumentL10nState::InitialTranslationTriggered) {
      RefPtr<Promise> promise = TranslateFragment(aNode, aRv);
    }
  }
  DOMLocalization::ConnectRoot(aNode);
}

Promise* DocumentL10n::Ready() { return mReady; }

void DocumentL10n::OnCreatePresShell() { mMutations->OnCreatePresShell(); }

Messung V0.5
C=93 H=94 G=93

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge