layout/style/ErrorReporter.cpp
author Xianzhu Wang <wangxianzhu@chromium.org>
Tue, 05 Mar 2019 12:18:01 +0000
changeset 464575 4325ba7b38576495a78289c2a8f4dff391922e66
parent 461895 e0fb4657355d4d8052bff3f1e8baac265b5efef7
child 466334 de3403c1d70449a352f0774fe68d47f83a9113b0
permissions -rw-r--r--
Bug 1529059 [wpt PR 15420] - [BlinkGenPropertyTrees] Initiailize double_sided of synthetic effect, a=testonly Automatic update from web-platform-tests [BlinkGenPropertyTrees] Initiailize double_sided of synthetic effect Previously synthetic effects always had double_sided==false, causing the layer disappear when the backface was facing forward. Bug: 928190 Change-Id: I35534b40346d5c5918bc99c00a4ca6b4e3b68796 Reviewed-on: https://chromium-review.googlesource.com/c/1475815 Reviewed-by: Philip Rogers <pdr@chromium.org> Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org> Cr-Commit-Position: refs/heads/master@{#632764} -- wpt-commits: a89467050deaf1dcbd9140a2f0670b1b85e518ee wpt-pr: 15420

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */

/* diagnostic reporting for CSS style sheet parser */

#include "mozilla/css/ErrorReporter.h"

#include "mozilla/StaticPrefs.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/css/Loader.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/SystemGroup.h"
#include "nsIConsoleService.h"
#include "mozilla/dom/Document.h"
#include "nsIDocShell.h"
#include "nsIFactory.h"
#include "nsINode.h"
#include "nsIScriptError.h"
#include "nsISensitiveInfoHiddenURI.h"
#include "nsIStringBundle.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleUtil.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"

using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::dom;

namespace {
class ShortTermURISpecCache : public Runnable {
 public:
  ShortTermURISpecCache()
      : Runnable("ShortTermURISpecCache"), mPending(false) {}

  nsString const& GetSpec(nsIURI* aURI) {
    if (mURI != aURI) {
      mURI = aURI;

      if (NS_FAILED(NS_GetSanitizedURIStringFromURI(mURI, mSpec))) {
        mSpec.AssignLiteral("[nsIURI::GetSpec failed]");
      }
    }
    return mSpec;
  }

  bool IsInUse() const { return mURI != nullptr; }
  bool IsPending() const { return mPending; }
  void SetPending() { mPending = true; }

  // When invoked as a runnable, zap the cache.
  NS_IMETHOD Run() override {
    mURI = nullptr;
    mSpec.Truncate();
    mPending = false;
    return NS_OK;
  }

 private:
  nsCOMPtr<nsIURI> mURI;
  nsString mSpec;
  bool mPending;
};

}  // namespace

bool ErrorReporter::sInitialized = false;

static nsIConsoleService* sConsoleService;
static nsIFactory* sScriptErrorFactory;
static nsIStringBundle* sStringBundle;
static ShortTermURISpecCache* sSpecCache;

void ErrorReporter::InitGlobals() {
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!sInitialized, "should not have been called");

  sInitialized = true;

  nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
  if (!cs) {
    return;
  }

  nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
  if (!sf) {
    return;
  }

  nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
  if (!sbs) {
    return;
  }

  nsCOMPtr<nsIStringBundle> sb;
  nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties",
                                  getter_AddRefs(sb));
  if (NS_FAILED(rv) || !sb) {
    return;
  }

  cs.forget(&sConsoleService);
  sf.forget(&sScriptErrorFactory);
  sb.forget(&sStringBundle);
}

namespace mozilla {
namespace css {

/* static */
void ErrorReporter::ReleaseGlobals() {
  NS_IF_RELEASE(sConsoleService);
  NS_IF_RELEASE(sScriptErrorFactory);
  NS_IF_RELEASE(sStringBundle);
  NS_IF_RELEASE(sSpecCache);
}

static uint64_t FindInnerWindowID(const StyleSheet* aSheet,
                                  const Loader* aLoader) {
  uint64_t innerWindowID = 0;
  if (aSheet) {
    innerWindowID = aSheet->FindOwningWindowInnerID();
  }
  if (innerWindowID == 0 && aLoader) {
    if (Document* doc = aLoader->GetDocument()) {
      innerWindowID = doc->InnerWindowID();
    }
  }
  return innerWindowID;
}

ErrorReporter::ErrorReporter(const StyleSheet* aSheet, const Loader* aLoader,
                             nsIURI* aURI)
    : mSheet(aSheet),
      mLoader(aLoader),
      mURI(aURI),
      mErrorLineNumber(0),
      mPrevErrorLineNumber(0),
      mErrorColNumber(0) {
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
  EnsureGlobalsInitialized();
}

ErrorReporter::~ErrorReporter() {
  MOZ_ASSERT(NS_IsMainThread());
  // Schedule deferred cleanup for cached data. We want to strike a
  // balance between performance and memory usage, so we only allow
  // short-term caching.
  if (sSpecCache && sSpecCache->IsInUse() && !sSpecCache->IsPending()) {
    nsCOMPtr<nsIRunnable> runnable(sSpecCache);
    nsresult rv = SystemGroup::Dispatch(TaskCategory::Other, runnable.forget());
    if (NS_FAILED(rv)) {
      // Peform the "deferred" cleanup immediately if the dispatch fails.
      sSpecCache->Run();
    } else {
      sSpecCache->SetPending();
    }
  }
}

bool ErrorReporter::ShouldReportErrors(const Document& aDoc) {
  MOZ_ASSERT(NS_IsMainThread());
  nsIDocShell* shell = aDoc.GetDocShell();
  if (!shell) {
    return false;
  }

  bool report = false;
  shell->GetCssErrorReportingEnabled(&report);
  return report;
}

static nsINode* SheetOwner(const StyleSheet& aSheet) {
  if (nsINode* owner = aSheet.GetOwnerNode()) {
    return owner;
  }

  auto* associated = aSheet.GetAssociatedDocumentOrShadowRoot();
  return associated ? &associated->AsNode() : nullptr;
}

bool ErrorReporter::ShouldReportErrors(const StyleSheet* aSheet,
                                       const Loader* aLoader) {
  MOZ_ASSERT(NS_IsMainThread());

  if (!StaticPrefs::layout_css_report_errors()) {
    return false;
  }

  if (aSheet) {
    nsINode* owner = SheetOwner(*aSheet);
    if (owner && ShouldReportErrors(*owner->OwnerDoc())) {
      return true;
    }
  }

  if (aLoader && aLoader->GetDocument() &&
      ShouldReportErrors(*aLoader->GetDocument())) {
    return true;
  }

  return false;
}

void ErrorReporter::OutputError() {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));

  if (mError.IsEmpty()) {
    return;
  }

  if (mFileName.IsEmpty()) {
    if (mURI) {
      if (!sSpecCache) {
        sSpecCache = new ShortTermURISpecCache;
        NS_ADDREF(sSpecCache);
      }
      mFileName = sSpecCache->GetSpec(mURI);
      mURI = nullptr;
    } else {
      mFileName.AssignLiteral("from DOM");
    }
  }

  nsresult rv;
  nsCOMPtr<nsIScriptError> errorObject =
      do_CreateInstance(sScriptErrorFactory, &rv);

  if (NS_SUCCEEDED(rv)) {
    // It is safe to used InitWithSanitizedSource because mFileName is
    // an already anonymized uri spec.
    rv = errorObject->InitWithSanitizedSource(
        mError, mFileName, mErrorLine, mErrorLineNumber, mErrorColNumber,
        nsIScriptError::warningFlag, "CSS Parser",
        FindInnerWindowID(mSheet, mLoader));
    if (NS_SUCCEEDED(rv)) {
      sConsoleService->LogMessage(errorObject);
    }
  }

  ClearError();
}

// When Stylo's CSS parser is in use, this reporter does not have access to the
// CSS parser's state. The users of ErrorReporter need to provide:
// - the line number of the error
// - the column number of the error
// - the complete source line containing the invalid CSS

void ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aColNumber,
                                const nsACString& aSourceLine) {
  mErrorLineNumber = aLineNumber;
  mErrorColNumber = aColNumber;

  // Retrieve the error line once per line, and reuse the same nsString
  // for all errors on that line.  That causes the text of the line to
  // be shared among all the nsIScriptError objects.
  if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) {
    mErrorLine.Truncate();
    // This could be a really long string for minified CSS; just leave it empty
    // if we OOM.
    if (!AppendUTF8toUTF16(aSourceLine, mErrorLine, fallible)) {
      mErrorLine.Truncate();
    }

    mPrevErrorLineNumber = aLineNumber;
  }

  OutputError();
}

void ErrorReporter::ClearError() { mError.Truncate(); }

void ErrorReporter::AddToError(const nsString& aErrorText) {
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));

  if (mError.IsEmpty()) {
    mError = aErrorText;
  } else {
    mError.AppendLiteral("  ");
    mError.Append(aErrorText);
  }
}

void ErrorReporter::ReportUnexpected(const char* aMessage) {
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));

  nsAutoString str;
  sStringBundle->GetStringFromName(aMessage, str);
  AddToError(str);
}

void ErrorReporter::ReportUnexpectedUnescaped(const char* aMessage,
                                              const nsAutoString& aParam) {
  MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));

  const char16_t* params[1] = {aParam.get()};

  nsAutoString str;
  sStringBundle->FormatStringFromName(aMessage, params, ArrayLength(params),
                                      str);
  AddToError(str);
}

}  // namespace css
}  // namespace mozilla