Ejemplo n.º 1
0
nsresult
XULContentSinkImpl::SetElementScriptType(nsXULPrototypeElement* element,
                                         const PRUnichar** aAttributes, 
                                         const PRUint32 aAttrLen)
{
    // First check if the attributes specify an explicit script type.
    nsresult rv = NS_OK;
    PRUint32 i;
    PRBool found = PR_FALSE;
    for (i=0;i<aAttrLen;i++) {
        const nsDependentString key(aAttributes[i*2]);
        if (key.EqualsLiteral("script-type")) {
            const nsDependentString value(aAttributes[i*2+1]);
            if (!value.IsEmpty()) {
                nsCOMPtr<nsIScriptRuntime> runtime;
                rv = NS_GetScriptRuntime(value, getter_AddRefs(runtime));
                if (NS_SUCCEEDED(rv))
                    element->mScriptTypeID = runtime->GetScriptTypeID();
                else {
                    // probably just a bad language name (typo, etc)
                    NS_WARNING("Failed to load the node's script language!");
                    // Leave the default language as unknown - we don't want js
                    // trying to execute this stuff.
                    NS_ASSERTION(element->mScriptTypeID == nsIProgrammingLanguage::UNKNOWN,
                                 "Default script type should be unknown");
                }
                found = PR_TRUE;
                break;
            }
        }
    }
    // If not specified, look at the context stack and use the element
    // there.
    if (!found) {
        if (mContextStack.Depth() == 0) {
            // This is the root element - default to JS
            element->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
        } else {
            // Ask the top-node for its script type (which has already
            // had this function called for it - so no need to recurse
            // until we find it)
            PRUint32 scriptId = 0;
            rv = mContextStack.GetTopNodeScriptType(&scriptId);
            element->mScriptTypeID = scriptId;
        }
    }
    return rv;
}
Ejemplo n.º 2
0
nsresult
nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
{
    // We need a document to evaluate scripts.
    NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);

    // Check to see if scripts has been turned off.
    if (!mEnabled || !mDocument->IsScriptEnabled()) {
        return NS_ERROR_NOT_AVAILABLE;
    }

    NS_ASSERTION(!aElement->IsMalformed(), "Executing malformed script");

    // Check that the script is not an eventhandler
    if (IsScriptEventHandler(aElement)) {
        return NS_CONTENT_SCRIPT_IS_EVENTHANDLER;
    }

    // Script evaluation can also be disabled in the current script
    // context even though it's enabled in the document.
    // XXX - still hard-coded for JS here, even though another language
    // may be specified.  Should this check be made *after* we examine
    // the attributes to locate the script-type?
    // For now though, if JS is disabled we assume every language is
    // disabled.
    // XXX is this different from the mDocument->IsScriptEnabled() call?
    nsIScriptGlobalObject *globalObject = mDocument->GetScriptGlobalObject();
    if (!globalObject) {
        return NS_ERROR_NOT_AVAILABLE;
    }

    nsIScriptContext *context = globalObject->GetScriptContext(
                                    nsIProgrammingLanguage::JAVASCRIPT);

    // If scripts aren't enabled in the current context, there's no
    // point in going on.
    if (!context || !context->GetScriptsEnabled()) {
        return NS_ERROR_NOT_AVAILABLE;
    }

    // Default script language is whatever the root content specifies
    // (which may come from a header or http-meta tag), or if there
    // is no root content, from the script global object.
    nsCOMPtr<nsIContent> rootContent = mDocument->GetRootContent();
    PRUint32 typeID = rootContent ? rootContent->GetScriptTypeID() :
                      context->GetScriptTypeID();
    PRUint32 version = 0;
    nsAutoString language, type, src;
    nsresult rv = NS_OK;

    // Check the type attribute to determine language and version.
    // If type exists, it trumps the deprecated 'language='
    aElement->GetScriptType(type);
    if (!type.IsEmpty()) {
        nsCOMPtr<nsIMIMEHeaderParam> mimeHdrParser =
            do_GetService("@mozilla.org/network/mime-hdrparam;1");
        NS_ENSURE_TRUE(mimeHdrParser, NS_ERROR_FAILURE);

        NS_ConvertUTF16toUTF8 typeAndParams(type);

        nsAutoString mimeType;
        rv = mimeHdrParser->GetParameter(typeAndParams, nsnull,
                                         EmptyCString(), PR_FALSE, nsnull,
                                         mimeType);
        NS_ENSURE_SUCCESS(rv, rv);

        // Javascript keeps the fast path, optimized for most-likely type
        // Table ordered from most to least likely JS MIME types.
        // See bug 62485, feel free to add <script type="..."> survey data to it,
        // or to a new bug once 62485 is closed.
        static const char *jsTypes[] = {
            "text/javascript",
            "text/ecmascript",
            "application/javascript",
            "application/ecmascript",
            "application/x-javascript",
            nsnull
        };

        PRBool isJavaScript = PR_FALSE;
        for (PRInt32 i = 0; jsTypes[i]; i++) {
            if (mimeType.LowerCaseEqualsASCII(jsTypes[i])) {
                isJavaScript = PR_TRUE;
                break;
            }
        }
        if (isJavaScript)
            typeID = nsIProgrammingLanguage::JAVASCRIPT;
        else {
            // Use the object factory to locate a matching language.
            nsCOMPtr<nsIScriptRuntime> runtime;
            rv = NS_GetScriptRuntime(mimeType, getter_AddRefs(runtime));
            if (NS_FAILED(rv) || runtime == nsnull) {
                // Failed to get the explicitly specified language
                NS_WARNING("Failed to find a scripting language");
                typeID = nsIProgrammingLanguage::UNKNOWN;
            } else
                typeID = runtime->GetScriptTypeID();
        }
        if (typeID != nsIProgrammingLanguage::UNKNOWN) {
            // Get the version string, and ensure the language supports it.
            nsAutoString versionName;
            rv = mimeHdrParser->GetParameter(typeAndParams, "version",
                                             EmptyCString(), PR_FALSE, nsnull,
                                             versionName);
            if (NS_FAILED(rv)) {
                // no version attribute - version remains 0.
                if (rv != NS_ERROR_INVALID_ARG)
                    return rv;
            } else {
                nsCOMPtr<nsIScriptRuntime> runtime;
                rv = NS_GetScriptRuntimeByID(typeID, getter_AddRefs(runtime));
                if (NS_FAILED(rv)) {
                    NS_ERROR("Failed to locate the language with this ID");
                    return rv;
                }
                rv = runtime->ParseVersion(versionName, &version);
                if (NS_FAILED(rv)) {
                    NS_WARNING("This script language version is not supported - ignored");
                    typeID = nsIProgrammingLanguage::UNKNOWN;
                }
            }
        }

        // Some js specifics yet to be abstracted.
        if (typeID == nsIProgrammingLanguage::JAVASCRIPT) {
            nsAutoString value;

            rv = mimeHdrParser->GetParameter(typeAndParams, "e4x",
                                             EmptyCString(), PR_FALSE, nsnull,
                                             value);
            if (NS_FAILED(rv)) {
                if (rv != NS_ERROR_INVALID_ARG)
                    return rv;
            } else {
                if (value.Length() == 1 && value[0] == '1')
                    // This means that we need to set JSOPTION_XML in the JS options.
                    // We re-use our knowledge of the implementation to reuse
                    // JSVERSION_HAS_XML as a safe version flag.
                    // If version has JSVERSION_UNKNOWN (-1), then this is still OK.
                    version |= JSVERSION_HAS_XML;
            }
        }
    } else {
        // no 'type=' element
        // "language" is a deprecated attribute of HTML, so we check it only for
        // HTML script elements.
        nsCOMPtr<nsIDOMHTMLScriptElement> htmlScriptElement =
            do_QueryInterface(aElement);
        if (htmlScriptElement) {
            htmlScriptElement->GetAttribute(NS_LITERAL_STRING("language"), language);
            if (!language.IsEmpty()) {
                if (nsParserUtils::IsJavaScriptLanguage(language, &version))
                    typeID = nsIProgrammingLanguage::JAVASCRIPT;
                else
                    typeID = nsIProgrammingLanguage::UNKNOWN;
                // IE, Opera, etc. do not respect language version, so neither should
                // we at this late date in the browser wars saga.  Note that this change
                // affects HTML but not XUL or SVG (but note also that XUL has its own
                // code to check nsParserUtils::IsJavaScriptLanguage -- that's probably
                // a separate bug, one we may not be able to fix short of XUL2).  See
                // bug 255895 (https://bugzilla.mozilla.org/show_bug.cgi?id=255895).
                NS_ASSERTION(JSVERSION_DEFAULT == 0,
                             "We rely on all languages having 0 as a version default");
                version = 0;
            }
        }
    }

    // If we don't know the language, we don't know how to evaluate
    if (typeID == nsIProgrammingLanguage::UNKNOWN) {
        return NS_ERROR_NOT_AVAILABLE;
    }
    // If not from a chrome document (which is always trusted), we need some way
    // of checking the language is "safe".  Currently the only other language
    // impl is Python, and that is *not* safe in untrusted code - so fixing
    // this isn't a priority.!
    // See also similar code in nsXULContentSink.cpp
    if (typeID != nsIProgrammingLanguage::JAVASCRIPT &&
            !nsContentUtils::IsChromeDoc(mDocument)) {
        NS_WARNING("Untrusted language called from non-chrome - ignored");
        return NS_ERROR_NOT_AVAILABLE;
    }

    nsCOMPtr<nsIContent> eltContent(do_QueryInterface(aElement));
    eltContent->SetScriptTypeID(typeID);

    // Create a request object for this script
    nsRefPtr<nsScriptLoadRequest> request = new nsScriptLoadRequest(aElement, version);
    NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);

    // First check to see if this is an external script
    nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
    if (scriptURI) {
        // Check that the containing page is allowed to load this URI.
        rv = nsContentUtils::GetSecurityManager()->
             CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), scriptURI,
                                       nsIScriptSecurityManager::ALLOW_CHROME);

        NS_ENSURE_SUCCESS(rv, rv);

        // After the security manager, the content-policy stuff gets a veto
        PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
        rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
                                       scriptURI,
                                       mDocument->NodePrincipal(),
                                       aElement,
                                       NS_LossyConvertUTF16toASCII(type),
                                       nsnull,    //extra
                                       &shouldLoad,
                                       nsContentUtils::GetContentPolicy(),
                                       nsContentUtils::GetSecurityManager());
        if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
            if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
                return NS_ERROR_CONTENT_BLOCKED;
            }
            return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
        }

        request->mURI = scriptURI;
        request->mIsInline = PR_FALSE;
        request->mLoading = PR_TRUE;

        nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
        nsCOMPtr<nsIStreamLoader> loader;

        nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(globalObject));
        nsIDocShell *docshell = window->GetDocShell();

        nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));

        nsCOMPtr<nsIChannel> channel;
        rv = NS_NewChannel(getter_AddRefs(channel),
                           scriptURI, nsnull, loadGroup,
                           prompter, nsIRequest::LOAD_NORMAL);
        NS_ENSURE_SUCCESS(rv, rv);

        nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
        if (httpChannel) {
            // HTTP content negotation has little value in this context.
            httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                          NS_LITERAL_CSTRING("*/*"),
                                          PR_FALSE);
            httpChannel->SetReferrer(mDocument->GetDocumentURI());
        }

        rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
        NS_ENSURE_SUCCESS(rv, rv);

        rv = channel->AsyncOpen(loader, request);
        NS_ENSURE_SUCCESS(rv, rv);
    } else {
        request->mLoading = PR_FALSE;
        request->mIsInline = PR_TRUE;
        request->mURI = mDocument->GetDocumentURI();

        request->mLineNo = aElement->GetScriptLineNumber();

        // If we've got existing pending requests, add ourselves
        // to this list.
        if (mPendingRequests.Count() == 0 && ReadyToExecuteScripts() &&
                nsContentUtils::IsSafeToRunScript()) {
            return ProcessRequest(request);
        }
    }

    // Add the request to our pending requests list
    NS_ENSURE_TRUE(mPendingRequests.AppendObject(request),
                   NS_ERROR_OUT_OF_MEMORY);

    // If there weren't any pending requests before, and this one is
    // ready to execute, do that as soon as it's safe.
    if (mPendingRequests.Count() == 1 && !request->mLoading &&
            ReadyToExecuteScripts()) {
        nsContentUtils::AddScriptRunner(new nsRunnableMethod<nsScriptLoader>(this,
                                        &nsScriptLoader::ProcessPendingRequests));
    }

    // Added as pending request, now we can send blocking back
    return NS_ERROR_HTMLPARSER_BLOCK;
}
nsresult
XULContentSinkImpl::OpenScript(const PRUnichar** aAttributes,
                               const PRUint32 aLineNumber)
{
  PRUint32 langID;
  nsresult rv = mContextStack.GetTopNodeScriptType(&langID);
  if (NS_FAILED(rv)) return rv;
  PRUint32 version = 0;

  // Look for SRC attribute and look for a LANGUAGE attribute
  nsAutoString src;
  while (*aAttributes) {
      const nsDependentString key(aAttributes[0]);
      if (key.EqualsLiteral("src")) {
          src.Assign(aAttributes[1]);
      }
      else if (key.EqualsLiteral("type")) {
          nsDependentString str(aAttributes[1]);
          nsContentTypeParser parser(str);
          nsAutoString mimeType;
          rv = parser.GetType(mimeType);
          if (NS_FAILED(rv)) {
              if (rv == NS_ERROR_INVALID_ARG) {
                  // Might as well bail out now instead of setting langID to
                  // nsIProgrammingLanguage::UNKNOWN and bailing out later.
                  return NS_OK;
              }
              // We do want the warning here
              NS_ENSURE_SUCCESS(rv, rv);
          }

          // Javascript keeps the fast path, optimized for most-likely type
          // Table ordered from most to least likely JS MIME types. For .xul
          // files that we host, the likeliest type is application/x-javascript.
          // See bug 62485, feel free to add <script type="..."> survey data to it,
          // or to a new bug once 62485 is closed.
          static const char *jsTypes[] = {
              "application/x-javascript",
              "text/javascript",
              "text/ecmascript",
              "application/javascript",
              "application/ecmascript",
              nsnull
          };

          bool isJavaScript = false;
          for (PRInt32 i = 0; jsTypes[i]; i++) {
              if (mimeType.LowerCaseEqualsASCII(jsTypes[i])) {
                  isJavaScript = true;
                  break;
              }
          }

          if (isJavaScript) {
              langID = nsIProgrammingLanguage::JAVASCRIPT;
              version = JSVERSION_LATEST;
          } else {
              // Use the script object factory to locate the language.
              nsCOMPtr<nsIScriptRuntime> runtime;
              rv = NS_GetScriptRuntime(mimeType, getter_AddRefs(runtime));
              if (NS_FAILED(rv) || runtime == nsnull) {
                  // Failed to get the explicitly specified language
                  NS_WARNING("Failed to find a scripting language");
                  langID = nsIProgrammingLanguage::UNKNOWN;
              } else
                  langID = nsIProgrammingLanguage::JAVASCRIPT;
          }

          if (langID != nsIProgrammingLanguage::UNKNOWN) {
            // Get the version string, and ensure the language supports it.
            nsAutoString versionName;
            rv = parser.GetParameter("version", versionName);
            if (NS_FAILED(rv)) {
              if (rv != NS_ERROR_INVALID_ARG)
                return rv;
              // no version specified - version remains the default.
            } else {
              nsCOMPtr<nsIScriptRuntime> runtime;
              rv = NS_GetScriptRuntimeByID(langID, getter_AddRefs(runtime));
              if (NS_FAILED(rv))
                return rv;
              rv = runtime->ParseVersion(versionName, &version);
              if (NS_FAILED(rv)) {
                NS_WARNING("This script language version is not supported - ignored");
                langID = nsIProgrammingLanguage::UNKNOWN;
              }
            }
          }
          // Some js specifics yet to be abstracted.
          if (langID == nsIProgrammingLanguage::JAVASCRIPT) {
              // By default scripts in XUL documents have E4X turned on. We use
              // our implementation knowledge to reuse JSVERSION_HAS_XML as a
              // safe version flag. This is still OK if version is
              // JSVERSION_UNKNOWN (-1),
              version = js::VersionSetXML(JSVersion(version), true);

              nsAutoString value;
              rv = parser.GetParameter("e4x", value);
              if (NS_FAILED(rv)) {
                  if (rv != NS_ERROR_INVALID_ARG)
                      return rv;
              } else {
                  if (value.Length() == 1 && value[0] == '0')
                    version = js::VersionSetXML(JSVersion(version), false);
              }
          }
      }
      else if (key.EqualsLiteral("language")) {
          // Language is deprecated, and the impl in nsScriptLoader ignores the
          // various version strings anyway.  So we make no attempt to support
          // languages other than JS for language=
          nsAutoString lang(aAttributes[1]);
          if (nsContentUtils::IsJavaScriptLanguage(lang, &version)) {
              langID = nsIProgrammingLanguage::JAVASCRIPT;

              // Even when JS version < 1.6 is specified, E4X is
              // turned on in XUL.
              version = js::VersionSetXML(JSVersion(version), true);
          }
      }
      aAttributes += 2;
  }
  // Not all script languages have a "sandbox" concept.  At time of
  // writing, Python is the only other language, and it does not.
  // For such languages, neither any inline script nor remote script are
  // safe to execute from untrusted sources.
  // So for such languages, we only allow script when the document
  // itself is from chrome.  We then don't bother to check the
  // "src=" tag - we trust chrome to do the right thing.
  // (See also similar code in nsScriptLoader.cpp)
  nsCOMPtr<nsIDocument> doc(do_QueryReferent(mDocument));
  if (langID != nsIProgrammingLanguage::UNKNOWN && 
      langID != nsIProgrammingLanguage::JAVASCRIPT &&
      doc && !nsContentUtils::IsChromeDoc(doc)) {
      langID = nsIProgrammingLanguage::UNKNOWN;
      NS_WARNING("Non JS language called from non chrome - ignored");
  }
  // Don't process scripts that aren't known
  if (langID != nsIProgrammingLanguage::UNKNOWN) {
      nsIScriptGlobalObject* globalObject = nsnull; // borrowed reference
      if (doc)
          globalObject = doc->GetScriptGlobalObject();
      nsRefPtr<nsXULPrototypeScript> script =
          new nsXULPrototypeScript(langID, aLineNumber, version);
      if (! script)
          return NS_ERROR_OUT_OF_MEMORY;

      // If there is a SRC attribute...
      if (! src.IsEmpty()) {
          // Use the SRC attribute value to load the URL
          rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nsnull, mDocumentURL);

          // Check if this document is allowed to load a script from this source
          // NOTE: if we ever allow scripts added via the DOM to run, we need to
          // add a CheckLoadURI call for that as well.
          if (NS_SUCCEEDED(rv)) {
              if (!mSecMan)
                  mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
              if (NS_SUCCEEDED(rv)) {
                  nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument, &rv);

                  if (NS_SUCCEEDED(rv)) {
                      rv = mSecMan->
                          CheckLoadURIWithPrincipal(doc->NodePrincipal(),
                                                    script->mSrcURI,
                                                    nsIScriptSecurityManager::ALLOW_CHROME);
                  }
              }
          }

          if (NS_FAILED(rv)) {
              return rv;
          }

          // Attempt to deserialize an out-of-line script from the FastLoad
          // file right away.  Otherwise we'll end up reloading the script and
          // corrupting the FastLoad file trying to serialize it, in the case
          // where it's already there.
          if (globalObject)
                script->DeserializeOutOfLine(nsnull, globalObject);
      }

      nsPrototypeArray* children = nsnull;
      rv = mContextStack.GetTopChildren(&children);
      if (NS_FAILED(rv)) {
          return rv;
      }

      children->AppendElement(script);

      mConstrainSize = false;

      mContextStack.Push(script, mState);
      mState = eInScript;
  }

  return NS_OK;
}