#include "config.h"
#include "ScriptElement.h"
#include "CachedScript.h"
#include "DocLoader.h"
#include "Document.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "HTMLNames.h"
#include "HTMLScriptElement.h"
#include "MIMETypeRegistry.h"
#include "ScriptController.h"
#include "ScriptSourceCode.h"
#include "ScriptValue.h"
#include "StringHash.h"
#include "Text.h"
#include <wtf/StdLibExtras.h>
#if ENABLE(SVG)
#include "SVGNames.h"
#include "SVGScriptElement.h"
#endif
namespace WebCore {
void ScriptElement::insertedIntoDocument(ScriptElementData& data, const String& sourceUrl)
{
if (data.createdByParser())
return;
if (!sourceUrl.isEmpty()) {
data.requestScript(sourceUrl);
return;
}
data.evaluateScript(ScriptSourceCode(data.scriptContent(), data.element()->document()->url())); }
void ScriptElement::removedFromDocument(ScriptElementData& data)
{
data.stopLoadRequest();
}
void ScriptElement::childrenChanged(ScriptElementData& data)
{
if (data.createdByParser())
return;
Element* element = data.element();
if (element->inDocument() && element->firstChild())
data.evaluateScript(ScriptSourceCode(data.scriptContent(), element->document()->url())); }
void ScriptElement::finishParsingChildren(ScriptElementData& data, const String& sourceUrl)
{
if (sourceUrl.isEmpty() && data.scriptContent().isEmpty())
data.setCreatedByParser(false);
}
void ScriptElement::handleSourceAttribute(ScriptElementData& data, const String& sourceUrl)
{
if (data.ignoresLoadRequest() || sourceUrl.isEmpty())
return;
data.requestScript(sourceUrl);
}
static bool isSupportedJavaScriptLanguage(const String& language)
{
typedef HashSet<String, CaseFoldingHash> LanguageSet;
DEFINE_STATIC_LOCAL(LanguageSet, languages, ());
if (languages.isEmpty()) {
languages.add("javascript");
languages.add("javascript");
languages.add("javascript1.0");
languages.add("javascript1.1");
languages.add("javascript1.2");
languages.add("javascript1.3");
languages.add("javascript1.4");
languages.add("javascript1.5");
languages.add("javascript1.6");
languages.add("javascript1.7");
languages.add("livescript");
languages.add("ecmascript");
languages.add("jscript");
}
return languages.contains(language);
}
ScriptElementData::ScriptElementData(ScriptElement* scriptElement, Element* element)
: m_scriptElement(scriptElement)
, m_element(element)
, m_cachedScript(0)
, m_createdByParser(false)
, m_requested(false)
, m_evaluated(false)
, m_firedLoad(false)
{
ASSERT(m_scriptElement);
ASSERT(m_element);
}
ScriptElementData::~ScriptElementData()
{
stopLoadRequest();
}
void ScriptElementData::requestScript(const String& sourceUrl)
{
Document* document = m_element->document();
if (!document->frame())
return;
ASSERT(!m_cachedScript);
m_cachedScript = document->docLoader()->requestScript(sourceUrl, scriptCharset());
m_requested = true;
m_firedLoad = false;
if (m_cachedScript) {
m_cachedScript->addClient(this);
return;
}
m_scriptElement->dispatchErrorEvent();
}
void ScriptElementData::evaluateScript(const ScriptSourceCode& sourceCode)
{
if (m_evaluated || sourceCode.isEmpty() || !shouldExecuteAsJavaScript())
return;
if (Frame* frame = m_element->document()->frame()) {
if (!frame->script()->isEnabled())
return;
m_evaluated = true;
frame->script()->evaluate(sourceCode);
Document::updateStyleForAllDocuments();
}
}
void ScriptElementData::stopLoadRequest()
{
if (m_cachedScript) {
m_cachedScript->removeClient(this);
m_cachedScript = 0;
}
}
void ScriptElementData::execute(CachedScript* cachedScript)
{
ASSERT(cachedScript);
if (cachedScript->errorOccurred())
m_scriptElement->dispatchErrorEvent();
else {
evaluateScript(ScriptSourceCode(cachedScript));
m_scriptElement->dispatchLoadEvent();
}
cachedScript->removeClient(this);
}
void ScriptElementData::notifyFinished(CachedResource* o)
{
ASSERT_UNUSED(o, o == m_cachedScript);
m_element->document()->executeScriptSoon(this, m_cachedScript);
m_cachedScript = 0;
}
bool ScriptElementData::ignoresLoadRequest() const
{
return m_evaluated || m_requested || m_createdByParser || !m_element->inDocument();
}
bool ScriptElementData::shouldExecuteAsJavaScript() const
{
String type = m_scriptElement->typeAttributeValue();
if (!type.isEmpty())
return MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower());
String language = m_scriptElement->languageAttributeValue();
if (!language.isEmpty())
return isSupportedJavaScriptLanguage(language);
String forAttribute = m_scriptElement->forAttributeValue();
return forAttribute.isEmpty();
}
String ScriptElementData::scriptCharset() const
{
String charset = m_scriptElement->charsetAttributeValue().stripWhiteSpace();
if (charset.isEmpty()) {
if (Frame* frame = m_element->document()->frame())
charset = frame->loader()->encoding();
}
return charset;
}
String ScriptElementData::scriptContent() const
{
Vector<UChar> val;
Text* firstTextNode = 0;
bool foundMultipleTextNodes = false;
for (Node* n = m_element->firstChild(); n; n = n->nextSibling()) {
if (!n->isTextNode())
continue;
Text* t = static_cast<Text*>(n);
if (foundMultipleTextNodes)
append(val, t->data());
else if (firstTextNode) {
append(val, firstTextNode->data());
append(val, t->data());
foundMultipleTextNodes = true;
} else
firstTextNode = t;
}
if (firstTextNode && !foundMultipleTextNodes)
return firstTextNode->data();
return String::adopt(val);
}
ScriptElement* toScriptElement(Element* element)
{
if (element->isHTMLElement() && element->hasTagName(HTMLNames::scriptTag))
return static_cast<HTMLScriptElement*>(element);
#if ENABLE(SVG)
if (element->isSVGElement() && element->hasTagName(SVGNames::scriptTag))
return static_cast<SVGScriptElement*>(element);
#endif
return 0;
}
}