mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			201 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=2 sw=2 et tw=78: */
 | 
						|
/* 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 "ImportScanner.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
static inline bool IsWhitespace(char16_t aChar) {
 | 
						|
  return nsContentUtils::IsHTMLWhitespace(aChar);
 | 
						|
}
 | 
						|
 | 
						|
void ImportScanner::Start() {
 | 
						|
  Stop();
 | 
						|
  mState = State::Idle;
 | 
						|
}
 | 
						|
 | 
						|
void ImportScanner::EmitUrl() {
 | 
						|
  MOZ_ASSERT(mState == State::AfterRuleValue);
 | 
						|
  if (mInImportRule) {
 | 
						|
    // Trim trailing whitespace from an unquoted URL.
 | 
						|
    if (mUrlValueDelimiterClosingChar == ')') {
 | 
						|
      // FIXME: Add a convenience function in nsContentUtils or something?
 | 
						|
      mRuleValue.Trim(" \t\n\r\f", false);
 | 
						|
    }
 | 
						|
    mUrlsFound.AppendElement(std::move(mRuleValue));
 | 
						|
  }
 | 
						|
  mInImportRule = false;
 | 
						|
  // We try to avoid freeing the buffers here.
 | 
						|
  mRuleName.Truncate(0);
 | 
						|
  mRuleValue.Truncate(0);
 | 
						|
  MOZ_ASSERT(mRuleValue.IsEmpty());
 | 
						|
}
 | 
						|
 | 
						|
nsTArray<nsString> ImportScanner::Stop() {
 | 
						|
  if (mState == State::AfterRuleValue) {
 | 
						|
    EmitUrl();
 | 
						|
  }
 | 
						|
  mState = State::OutsideOfStyleElement;
 | 
						|
  mInImportRule = false;
 | 
						|
  mRuleName.Truncate(0);
 | 
						|
  mRuleValue.Truncate(0);
 | 
						|
  return std::move(mUrlsFound);
 | 
						|
}
 | 
						|
 | 
						|
nsTArray<nsString> ImportScanner::Scan(Span<const char16_t> aFragment) {
 | 
						|
  MOZ_ASSERT(ShouldScan());
 | 
						|
 | 
						|
  for (char16_t c : aFragment) {
 | 
						|
    mState = Scan(c);
 | 
						|
    if (mState == State::Done) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return std::move(mUrlsFound);
 | 
						|
}
 | 
						|
 | 
						|
auto ImportScanner::Scan(char16_t aChar) -> State {
 | 
						|
  switch (mState) {
 | 
						|
    case State::OutsideOfStyleElement:
 | 
						|
    case State::Done:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("How?");
 | 
						|
      return mState;
 | 
						|
    case State::Idle: {
 | 
						|
      // TODO(emilio): Maybe worth caring about html-style comments like:
 | 
						|
      // <style>
 | 
						|
      // <!--
 | 
						|
      //   @import url(stuff);
 | 
						|
      // -->
 | 
						|
      // </style>
 | 
						|
      if (IsWhitespace(aChar)) {
 | 
						|
        return mState;
 | 
						|
      }
 | 
						|
      if (aChar == '/') {
 | 
						|
        return State::MaybeAtCommentStart;
 | 
						|
      }
 | 
						|
      if (aChar == '@') {
 | 
						|
        MOZ_ASSERT(mRuleName.IsEmpty());
 | 
						|
        return State::AtRuleName;
 | 
						|
      }
 | 
						|
      return State::Done;
 | 
						|
    }
 | 
						|
    case State::MaybeAtCommentStart: {
 | 
						|
      return aChar == '*' ? State::AtComment : State::Done;
 | 
						|
    }
 | 
						|
    case State::AtComment: {
 | 
						|
      return aChar == '*' ? State::MaybeAtCommentEnd : mState;
 | 
						|
    }
 | 
						|
    case State::MaybeAtCommentEnd: {
 | 
						|
      return aChar == '/' ? State::Idle : State::AtComment;
 | 
						|
    }
 | 
						|
    case State::AtRuleName: {
 | 
						|
      if (IsAsciiAlpha(aChar)) {
 | 
						|
        if (mRuleName.Length() > kMaxRuleNameLength - 1) {
 | 
						|
          return State::Done;
 | 
						|
        }
 | 
						|
        mRuleName.Append(aChar);
 | 
						|
        return mState;
 | 
						|
      }
 | 
						|
      if (IsWhitespace(aChar)) {
 | 
						|
        mInImportRule = mRuleName.LowerCaseEqualsLiteral("import");
 | 
						|
        if (mInImportRule) {
 | 
						|
          return State::AtRuleValue;
 | 
						|
        }
 | 
						|
        // Ignorable rules, we skip until the next semi-colon for these.
 | 
						|
        if (mRuleName.LowerCaseEqualsLiteral("charset") ||
 | 
						|
            mRuleName.LowerCaseEqualsLiteral("layer")) {
 | 
						|
          MOZ_ASSERT(mRuleValue.IsEmpty());
 | 
						|
          return State::AfterRuleValue;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return State::Done;
 | 
						|
    }
 | 
						|
    case State::AtRuleValue: {
 | 
						|
      MOZ_ASSERT(mInImportRule, "Should only get to this state for @import");
 | 
						|
      if (mRuleValue.IsEmpty()) {
 | 
						|
        if (IsWhitespace(aChar)) {
 | 
						|
          return mState;
 | 
						|
        }
 | 
						|
        if (aChar == '"' || aChar == '\'') {
 | 
						|
          mUrlValueDelimiterClosingChar = aChar;
 | 
						|
          return State::AtRuleValueDelimited;
 | 
						|
        }
 | 
						|
        if (aChar == 'u' || aChar == 'U') {
 | 
						|
          mRuleValue.Append('u');
 | 
						|
          return mState;
 | 
						|
        }
 | 
						|
        return State::Done;
 | 
						|
      }
 | 
						|
      if (mRuleValue.Length() == 1) {
 | 
						|
        MOZ_ASSERT(mRuleValue.EqualsLiteral("u"));
 | 
						|
        if (aChar == 'r' || aChar == 'R') {
 | 
						|
          mRuleValue.Append('r');
 | 
						|
          return mState;
 | 
						|
        }
 | 
						|
        return State::Done;
 | 
						|
      }
 | 
						|
      if (mRuleValue.Length() == 2) {
 | 
						|
        MOZ_ASSERT(mRuleValue.EqualsLiteral("ur"));
 | 
						|
        if (aChar == 'l' || aChar == 'L') {
 | 
						|
          mRuleValue.Append('l');
 | 
						|
        }
 | 
						|
        return mState;
 | 
						|
      }
 | 
						|
      if (mRuleValue.Length() == 3) {
 | 
						|
        MOZ_ASSERT(mRuleValue.EqualsLiteral("url"));
 | 
						|
        if (aChar == '(') {
 | 
						|
          mUrlValueDelimiterClosingChar = ')';
 | 
						|
          mRuleValue.Truncate(0);
 | 
						|
          return State::AtRuleValueDelimited;
 | 
						|
        }
 | 
						|
        return State::Done;
 | 
						|
      }
 | 
						|
      MOZ_ASSERT_UNREACHABLE(
 | 
						|
          "How? We should find a paren or a string delimiter");
 | 
						|
      return State::Done;
 | 
						|
    }
 | 
						|
    case State::AtRuleValueDelimited: {
 | 
						|
      MOZ_ASSERT(mInImportRule, "Should only get to this state for @import");
 | 
						|
      if (aChar == mUrlValueDelimiterClosingChar) {
 | 
						|
        return State::AfterRuleValue;
 | 
						|
      }
 | 
						|
      if (mUrlValueDelimiterClosingChar == ')' && mRuleValue.IsEmpty()) {
 | 
						|
        if (IsWhitespace(aChar)) {
 | 
						|
          return mState;
 | 
						|
        }
 | 
						|
        if (aChar == '"' || aChar == '\'') {
 | 
						|
          // Handle url("") and url('').
 | 
						|
          mUrlValueDelimiterClosingChar = aChar;
 | 
						|
          return mState;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (!mRuleValue.Append(aChar, mozilla::fallible)) {
 | 
						|
        mRuleValue.Truncate(0);
 | 
						|
        return State::Done;
 | 
						|
      }
 | 
						|
      return mState;
 | 
						|
    }
 | 
						|
    case State::AfterRuleValue: {
 | 
						|
      if (aChar == ';') {
 | 
						|
        EmitUrl();
 | 
						|
        return State::Idle;
 | 
						|
      }
 | 
						|
      // If there's a selector here and the import was unterminated, just give
 | 
						|
      // up.
 | 
						|
      if (aChar == '{') {
 | 
						|
        return State::Done;
 | 
						|
      }
 | 
						|
      return mState;  // There can be all sorts of stuff here like media
 | 
						|
                      // queries or what not.
 | 
						|
    }
 | 
						|
  }
 | 
						|
  MOZ_ASSERT_UNREACHABLE("Forgot to handle a state?");
 | 
						|
  return State::Done;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace mozilla
 |