mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-28 03:18:41 +00:00
1467 lines
43 KiB
C
1467 lines
43 KiB
C
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
|
|
#include "xp.h"
|
|
|
|
/*
|
|
* SCRIPT tag support.
|
|
*/
|
|
|
|
#include "libmocha.h"
|
|
#include "lo_ele.h"
|
|
#include "net.h"
|
|
#include "pa_parse.h"
|
|
#include "pa_tags.h"
|
|
#include "layout.h"
|
|
#include "libevent.h"
|
|
#include "css.h"
|
|
#include "laystyle.h"
|
|
#include "mcom_db.h"
|
|
#include "laylayer.h"
|
|
#include "prefapi.h"
|
|
|
|
static char lo_jsAllowFileSrcFromNonFile[]
|
|
= "javascript.allow.file_src_from_non_file";
|
|
|
|
/*
|
|
* XXX Should dynamic layer resize-reload really rerun scripts, potentially
|
|
* XXX destroying and reinitializing JS state? An onResize event handler
|
|
* XXX would be consistent with how windows and static layers resize-reload.
|
|
*/
|
|
#define SCRIPT_EXEC_OK(top_state, state, type, script_type) \
|
|
(top_state->resize_reload == FALSE || \
|
|
type != script_type || \
|
|
(lo_InsideLayer(state) && (lo_IsAnyCurrentAncestorDynamic(state) || \
|
|
lo_IsAnyCurrentAncestorSourced(state))))
|
|
|
|
static void
|
|
lo_ParseScriptLanguage(MWContext * context, PA_Tag * tag, int8 * type,
|
|
JSVersion * version)
|
|
{
|
|
PA_Block buff;
|
|
char * str;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_LANGUAGE);
|
|
if (buff != NULL) {
|
|
PA_LOCK(str, char *, buff);
|
|
if ((XP_STRCASECMP(str, js_language_name) == 0) ||
|
|
(XP_STRCASECMP(str, "LiveScript") == 0) ||
|
|
(XP_STRCASECMP(str, "Mocha") == 0)) {
|
|
*type = SCRIPT_TYPE_MOCHA;
|
|
*version = JSVERSION_DEFAULT;
|
|
}
|
|
else if (XP_STRCASECMP(str, "JavaScript1.1") == 0) {
|
|
*type = SCRIPT_TYPE_MOCHA;
|
|
*version = JSVERSION_1_1;
|
|
}
|
|
else if (XP_STRCASECMP(str, "JavaScript1.2") == 0) {
|
|
*type = SCRIPT_TYPE_MOCHA;
|
|
*version = JSVERSION_1_2;
|
|
}
|
|
else if (XP_STRCASECMP(str, "JavaScript1.3") == 0) {
|
|
*type = SCRIPT_TYPE_MOCHA;
|
|
*version = JSVERSION_1_3;
|
|
}
|
|
|
|
else {
|
|
*type = SCRIPT_TYPE_UNKNOWN;
|
|
}
|
|
PA_FREE(buff);
|
|
}
|
|
}
|
|
|
|
void
|
|
lo_BlockScriptTag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
lo_TopState *top_state;
|
|
pa_DocData *doc_data;
|
|
|
|
if (!LM_CanDoJS(context))
|
|
return;
|
|
|
|
/*
|
|
* Find the parser stream from its private data, doc_data, pointed at by
|
|
* a top_state member for just this situation.
|
|
*/
|
|
top_state = state->top_state;
|
|
doc_data = top_state->doc_data;
|
|
XP_ASSERT(doc_data != NULL && doc_data->url_struct != NULL);
|
|
if (doc_data == NULL || doc_data->url_struct == NULL) /* XXX paranoia */
|
|
return;
|
|
|
|
/*
|
|
* The parser must not parse ahead after a script or style end-tag, or
|
|
* it could get into a brute_tag state (XMP, PRE, etc.) and enter a mode
|
|
* that would mess up document.writes coming from the script or style.
|
|
* So we put it into overflow mode here, and decrement overflow when we
|
|
* unblock.
|
|
*
|
|
* We test tag first to handle the call from lo_ProcessScriptTag, which
|
|
* wants us to create an input state if needed, but not to count comment
|
|
* bytes via lo_data. Sad to say, this means lo_ProcessScriptTag must
|
|
* also increment doc_data->overflow, but only if tag->is_end == 1 (a.k.a.
|
|
* PR_TRUE), meaning the script tag was not blocked by an earlier blocking
|
|
* tag that caused it to go through here and set is_end to 2. XXX we can
|
|
* do this only because all layout and libparse tests of is_end are of the
|
|
* form "tag->is_end == FALSE".
|
|
*/
|
|
if (tag) {
|
|
/* Tag non-null means we're called from lo_BlockTag in layout.c. */
|
|
if (tag->is_end == FALSE) {
|
|
int8 script_type;
|
|
JSVersion ver;
|
|
|
|
/* Keep track of whether we're in a script or style container... */
|
|
script_type = SCRIPT_TYPE_UNKNOWN;
|
|
lo_ParseScriptLanguage(context, tag, &script_type, &ver);
|
|
if (tag->type == P_STYLE || tag->type == P_LINK ||
|
|
script_type != SCRIPT_TYPE_UNKNOWN)
|
|
top_state->in_blocked_script = TRUE;
|
|
}
|
|
else if (top_state->in_blocked_script) {
|
|
/* ...so we can avoid overflowing the parser on superfluous end
|
|
* tags, such as those emitted by libmime.
|
|
*/
|
|
top_state->in_blocked_script = FALSE;
|
|
|
|
if (SCRIPT_EXEC_OK(top_state, state, tag->type, P_SCRIPT)) {
|
|
tag->is_end = (PRPackedBool)2;
|
|
PA_PushOverflow(doc_data);
|
|
doc_data->overflow_depth ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Count the bytes of HTML comments and heuristicly "lost" newlines in
|
|
* tag so lo_ProcessScriptTag can recover it there later, in preference
|
|
* to the (then far-advanced) value of doc_data->comment_bytes. Ensure
|
|
* that this hack is not confused for NULL by adding 1 here and taking
|
|
* it away later. Big XXX for future cleanup, needless to say.
|
|
*/
|
|
if (tag)
|
|
tag->lo_data = (void *)(doc_data->comment_bytes + 1);
|
|
|
|
if (top_state->script_tag_count++ == 0)
|
|
ET_SetDecoderStream(context, doc_data->parser_stream,
|
|
doc_data->url_struct, JS_FALSE);
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
MWContext *context;
|
|
lo_DocState *state;
|
|
PA_Tag *tag;
|
|
char *url, *archiveSrc, *id, *codebase;
|
|
char *buffer;
|
|
uint32 bufferSize;
|
|
JSVersion version;
|
|
JSBool inlineSigned;
|
|
} ScriptData;
|
|
|
|
void
|
|
lo_DestroyScriptData(void *arg)
|
|
{
|
|
ScriptData *data = arg;
|
|
|
|
if (data == NULL)
|
|
return;
|
|
XP_FREEIF(data->url);
|
|
XP_FREEIF(data->archiveSrc);
|
|
XP_FREEIF(data->id);
|
|
XP_FREEIF(data->codebase);
|
|
XP_FREEIF(data->buffer);
|
|
if (data->tag)
|
|
PA_FreeTag(data->tag);
|
|
XP_FREE(data);
|
|
}
|
|
|
|
static void
|
|
lo_script_src_exit_fn(URL_Struct *url_struct, int status, MWContext *context);
|
|
|
|
static void
|
|
lo_script_archive_exit_fn(URL_Struct *url_struct, int status, MWContext *context);
|
|
|
|
static Bool
|
|
lo_create_script_blockage(MWContext *context, lo_DocState *state, int type)
|
|
{
|
|
lo_TopState *top_state;
|
|
LO_Element *block_ele;
|
|
|
|
top_state = state->top_state;
|
|
if (!top_state) {
|
|
XP_ASSERT(0);
|
|
return FALSE;
|
|
}
|
|
|
|
block_ele = lo_NewElement(context, state, LO_SCRIPT, NULL, 0);
|
|
if (block_ele == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
block_ele->type = type;
|
|
block_ele->lo_any.ele_id = NEXT_ELEMENT;
|
|
top_state->layout_blocking_element = block_ele;
|
|
if (type == LO_SCRIPT)
|
|
top_state->current_script = block_ele;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* used for ARCHIVE= and SRC=
|
|
* Create name of form "archive.jar/src.js"
|
|
*/
|
|
static char *
|
|
lo_BuildJSArchiveURL( char *archive_name, char *filename )
|
|
{
|
|
uint32 len = XP_STRLEN(archive_name) + XP_STRLEN(filename) + 2;
|
|
char *path = XP_ALLOC(len);
|
|
if (path)
|
|
{
|
|
XP_STRCPY(path, archive_name);
|
|
XP_STRCAT(path, "/");
|
|
XP_STRCAT(path, filename);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
/*
|
|
* Load a document from an external URL. Assumes that layout is
|
|
* already blocked
|
|
*/
|
|
static void
|
|
lo_GetScriptFromURL(ScriptData *data, int script_type)
|
|
{
|
|
URL_Struct *url_struct;
|
|
lo_TopState *top_state = data->state->top_state;
|
|
PA_Tag *tag = data->tag;
|
|
|
|
url_struct = NET_CreateURLStruct(data->url, top_state->force_reload);
|
|
if (url_struct == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
lo_DestroyScriptData(data);
|
|
return;
|
|
}
|
|
|
|
url_struct->must_cache = TRUE;
|
|
url_struct->preset_content_type = TRUE;
|
|
if (script_type == SCRIPT_TYPE_CSS) {
|
|
StrAllocCopy(url_struct->content_type, TEXT_CSS);
|
|
} else {
|
|
if (tag->type == P_STYLE || tag->type == P_LINK) {
|
|
StrAllocCopy(url_struct->content_type, TEXT_JSSS);
|
|
} else {
|
|
StrAllocCopy(url_struct->content_type, js_content_type);
|
|
}
|
|
}
|
|
|
|
XP_ASSERT(top_state->layout_blocking_element);
|
|
|
|
if (!url_struct->content_type)
|
|
goto out;
|
|
|
|
if (!data->inlineSigned) {
|
|
if (data->archiveSrc) {
|
|
/* ARCHIVE= and SRC= */
|
|
/*
|
|
* Need to set nesting url. Create name of form
|
|
* "archive.jar/src.js"
|
|
*/
|
|
|
|
char *path = lo_BuildJSArchiveURL(url_struct->address,
|
|
data->archiveSrc);
|
|
if (!path)
|
|
goto out;
|
|
ET_SetNestingUrl(data->context, path);
|
|
/* version taken care of in lo_script_archive_exit_fn */
|
|
XP_FREE(path);
|
|
} else {
|
|
/* SRC= but no ARCHIVE= */
|
|
ET_SetNestingUrl(data->context, url_struct->address);
|
|
ET_SetVersion(data->context, data->version);
|
|
}
|
|
}
|
|
|
|
url_struct->fe_data = data;
|
|
|
|
if (data->archiveSrc != NULL || data->inlineSigned) {
|
|
NET_GetURL(url_struct,
|
|
FO_CACHE_ONLY,
|
|
data->context,
|
|
lo_script_archive_exit_fn);
|
|
} else {
|
|
NET_GetURL(url_struct,
|
|
FO_CACHE_AND_PRESENT,
|
|
data->context,
|
|
lo_script_src_exit_fn);
|
|
}
|
|
|
|
return;
|
|
out:
|
|
top_state->out_of_memory = TRUE;
|
|
NET_FreeURLStruct(url_struct);
|
|
lo_DestroyScriptData(data);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* A script has just completed. If we are blocked on that script
|
|
* free the blockage
|
|
*/
|
|
static void
|
|
lo_unblock_script_tag(MWContext * context, Bool messWithParser)
|
|
{
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
LO_Element * block_ele;
|
|
LO_Element * current_script;
|
|
PA_Tag * tag;
|
|
NET_StreamClass stream;
|
|
|
|
top_state = lo_FetchTopState(XP_DOCID(context));
|
|
if (!top_state || !top_state->doc_state)
|
|
return;
|
|
state = top_state->doc_state;
|
|
|
|
/*
|
|
* Remember the fake element we created but clear the current
|
|
* script flag of the top_state since the FlushBlockage call
|
|
* might just turn around and block us on another script
|
|
*/
|
|
current_script = top_state->current_script;
|
|
top_state->current_script = NULL;
|
|
|
|
/*
|
|
* if we are finishing a nested script make sure the current_script
|
|
* points to our parent script
|
|
*/
|
|
for (tag = top_state->tags; tag; tag = tag->next) {
|
|
if (tag->type == P_NSCP_REBLOCK) {
|
|
top_state->current_script = tag->lo_data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Flush tags blocked by this <SCRIPT SRC="URL"> tag. */
|
|
block_ele = top_state->layout_blocking_element;
|
|
|
|
/* no longer in a script */
|
|
top_state->in_script = SCRIPT_TYPE_NOT;
|
|
|
|
/*
|
|
* we must be blocked on something either the script that just ended or
|
|
* some other object
|
|
*/
|
|
if (!block_ele)
|
|
goto done;
|
|
|
|
if (messWithParser && top_state->doc_data &&
|
|
SCRIPT_EXEC_OK(top_state, state, block_ele->type, LO_SCRIPT)) {
|
|
top_state->doc_data->overflow_depth --;
|
|
XP_ASSERT(top_state->doc_data->overflow_depth >= 0);
|
|
}
|
|
|
|
lo_FlushLineBuffer(context, top_state->doc_state);
|
|
|
|
/*
|
|
* unblock on UNKNOWN's as well since that's the type for
|
|
* style attribute scripts
|
|
*/
|
|
if (block_ele->type == LO_SCRIPT || block_ele->type == LO_UNKNOWN) {
|
|
lo_UnblockLayout(context, top_state);
|
|
}
|
|
else {
|
|
/*
|
|
* we're still blocked on something else - make sure
|
|
* there are no reblock tags that are going to hose
|
|
* us. Find the reblock tags that point to our script
|
|
* tag and render them hermless
|
|
*/
|
|
PA_Tag * tag;
|
|
for (tag = top_state->tags; tag; tag = tag->next) {
|
|
if (tag->type == P_NSCP_REBLOCK) {
|
|
LO_Element * lo_ele = tag->lo_data;
|
|
if (lo_ele == current_script) {
|
|
tag->lo_data = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* free the fake element we created earlier */
|
|
lo_FreeElement(context, current_script, FALSE);
|
|
|
|
done:
|
|
|
|
/* the parser is now able to receive new data from netlib */
|
|
stream.data_object=(pa_DocData *)top_state->doc_data;
|
|
pa_FlushOverflow(&stream);
|
|
|
|
/*
|
|
* The initial call to lo_UnblockLayout() might have not torn
|
|
* everything down because we were waiting for the last
|
|
* chunk of data to come out of the doc_data->overflow_buf
|
|
* We need to get to lo_FlushedBlockedTags() and lo_FinishLayout()
|
|
* at the bottom of lo_FlushBlockage().
|
|
*/
|
|
if (!top_state->layout_blocking_element)
|
|
lo_FlushBlockage(context, top_state->doc_state, top_state->doc_state);
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
lo_script_src_exit_fn(URL_Struct *url_struct, int status, MWContext *context)
|
|
{
|
|
lo_DestroyScriptData(url_struct->fe_data);
|
|
NET_FreeURLStruct(url_struct);
|
|
|
|
/*
|
|
* If our status is negative that means there was an error and the
|
|
* script won't be executed. Therefore the MochaDecoder's nesting
|
|
* url must be cleared, and layout must be unblocked.
|
|
*/
|
|
if (status < 0) {
|
|
ET_SetNestingUrl(context, NULL);
|
|
lo_unblock_script_tag(context, TRUE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_script_archive_exit_fn(URL_Struct *url_struct, int status, MWContext *context)
|
|
{
|
|
ScriptData *data = NULL;
|
|
#ifdef JAVA
|
|
/* Vars only used in JAVA context */
|
|
char *name;
|
|
JSPrincipals *principals;
|
|
#endif
|
|
ETEvalStuff * stuff;
|
|
|
|
data = (ScriptData *) url_struct->fe_data;
|
|
stuff = (ETEvalStuff *) XP_NEW_ZAP(ETEvalStuff);
|
|
if (!stuff)
|
|
return;
|
|
|
|
stuff->line_no = 1;
|
|
stuff->scope_to = NULL;
|
|
stuff->want_result = JS_FALSE;
|
|
stuff->version = data->version;
|
|
stuff->data = context;
|
|
|
|
#ifndef JAVA
|
|
/* No Java; execute without principals. */
|
|
if (data->buffer) {
|
|
stuff->principals = NULL;
|
|
ET_EvaluateScript(context, data->buffer, stuff, lo_ScriptEvalExitFn);
|
|
}
|
|
else {
|
|
XP_FREE(stuff);
|
|
}
|
|
#else
|
|
name = data->archiveSrc ? data->archiveSrc : data->id;
|
|
principals = LM_NewJSPrincipals(url_struct, name, data->codebase);
|
|
if (principals != NULL) {
|
|
char *src;
|
|
uint srcSize;
|
|
|
|
if (data->archiveSrc) {
|
|
/* Extract from archive using "SRC=" value */
|
|
src = LM_ExtractFromPrincipalsArchive(principals, data->archiveSrc,
|
|
&srcSize);
|
|
|
|
#ifdef JSDEBUGGER
|
|
if( src != NULL && LM_GetJSDebugActive() )
|
|
{
|
|
char *path = lo_BuildJSArchiveURL(url_struct->address,
|
|
data->archiveSrc);
|
|
if (path)
|
|
{
|
|
LM_JamSourceIntoJSDebug( path, src, srcSize, context );
|
|
XP_FREE(path);
|
|
}
|
|
}
|
|
#endif /* JSDEBUGGER */
|
|
|
|
if (src == NULL) {
|
|
/* Unsuccessful extracting from archive. Now try normal SRC= lookup. */
|
|
(*principals->destroy)(NULL, principals);
|
|
data->url = NET_MakeAbsoluteURL(data->state->top_state->base_url,
|
|
data->archiveSrc);
|
|
XP_FREEIF(data->archiveSrc);
|
|
data->archiveSrc = NULL;
|
|
lo_GetScriptFromURL(data, data->state->top_state->in_script);
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* Should be an inline script */
|
|
src = data->buffer;
|
|
srcSize = data->bufferSize;
|
|
}
|
|
|
|
stuff->len = srcSize;
|
|
stuff->principals = principals;
|
|
ET_EvaluateScript(context, src, stuff, lo_ScriptEvalExitFn);
|
|
|
|
if (data->archiveSrc){
|
|
ET_SetNestingUrl(context, NULL);
|
|
XP_FREE(src);
|
|
}
|
|
}
|
|
else {
|
|
XP_FREE(stuff);
|
|
}
|
|
|
|
#endif /* ifdef JAVA */
|
|
|
|
lo_DestroyScriptData(data);
|
|
|
|
#ifdef JAVA /* Label only used in this context */
|
|
out:
|
|
#endif
|
|
|
|
/* Always free (or drop a ref on) the url_struct before returning. */
|
|
NET_FreeURLStruct(url_struct);
|
|
}
|
|
|
|
/*
|
|
* This function gets called when the LM_EvaluateBuffer() operation
|
|
* completes (in the mocha thread, but we run in the mozilla thread
|
|
* here)
|
|
*/
|
|
void
|
|
lo_ScriptEvalExitFn(void * data, char * str, size_t len, char * wysiwyg_url,
|
|
char * base_href, Bool valid)
|
|
{
|
|
|
|
/* we don't care about the strings here just kill them */
|
|
XP_FREEIF(str);
|
|
XP_FREEIF(wysiwyg_url);
|
|
XP_FREEIF(base_href);
|
|
|
|
lo_unblock_script_tag(data, TRUE);
|
|
|
|
}
|
|
|
|
/*
|
|
* This function gets called when the LM_EvaluateBuffer() operation
|
|
* completes (in the mocha thread, but we run in the mozilla thread
|
|
* here)
|
|
*/
|
|
PRIVATE
|
|
void
|
|
lo_StyleEvalExitFn(void * data, char * str, size_t len, char * wysiwyg_url,
|
|
char * base_href, Bool valid)
|
|
{
|
|
|
|
/* we don't care about the strings here just kill them */
|
|
XP_FREEIF(str);
|
|
XP_FREEIF(wysiwyg_url);
|
|
XP_FREEIF(base_href);
|
|
|
|
lo_unblock_script_tag(data, FALSE);
|
|
|
|
}
|
|
|
|
|
|
void
|
|
lo_UnblockLayout(MWContext *context, lo_TopState *top_state)
|
|
{
|
|
XP_ASSERT(top_state && top_state->doc_state);
|
|
if (!top_state || !top_state->doc_state)
|
|
return;
|
|
|
|
top_state->layout_blocking_element = NULL;
|
|
lo_FlushBlockage(context, top_state->doc_state, top_state->doc_state);
|
|
}
|
|
|
|
|
|
Bool
|
|
lo_CloneTagAndBlockLayout(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
|
|
PA_Tag *new_tag;
|
|
lo_TopState * top_state = state->top_state;
|
|
|
|
/*
|
|
* Since we're adding tags to the blocked tags list without going through
|
|
* lo_BlockTag, we need to see if this is a flushed to block transition
|
|
* and, if so, add to the doc_data's ref count. We don't want to do this
|
|
* if we're in the process of flushing blockage - in that case, the count
|
|
* has already been incremented.
|
|
*/
|
|
if (top_state->tags == NULL && top_state->flushing_blockage == FALSE)
|
|
PA_HoldDocData(top_state->doc_data);
|
|
|
|
/*
|
|
* stick the current tag onto the front of the tag list
|
|
*/
|
|
new_tag = PA_CloneMDLTag(tag);
|
|
if (!new_tag) {
|
|
top_state->out_of_memory = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
new_tag->next = top_state->tags;
|
|
top_state->tags = new_tag;
|
|
if (top_state->tags_end == &top_state->tags)
|
|
top_state->tags_end = &new_tag->next;
|
|
|
|
return lo_create_script_blockage(context, state, LO_UNKNOWN);
|
|
}
|
|
|
|
/* evaluate the buffer encompassed by the style attribute on any tag.
|
|
* this will be JSS or CSS
|
|
*
|
|
* Returns TRUE if it needs to block layout
|
|
* Returns FALSE if not.
|
|
*/
|
|
XP_Bool
|
|
lo_ProcessStyleAttribute(MWContext *context, lo_DocState *state, PA_Tag *tag, char *script_buff)
|
|
{
|
|
lo_TopState *top_state;
|
|
char *scope_to = NULL;
|
|
int32 script_buff_len;
|
|
char *new_id;
|
|
XP_Bool in_double_quote=FALSE, in_single_quote=FALSE;
|
|
char *ptr, *end;
|
|
ETEvalStuff * stuff;
|
|
|
|
if (!state)
|
|
{
|
|
XP_FREEIF(script_buff);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (!script_buff)
|
|
return FALSE;
|
|
|
|
/* @@@ A bit of a hack.
|
|
* Get rid of the style attribute in the tag->data so that we don't
|
|
* go circular
|
|
*
|
|
* get rid of STYLE by rewriting it as SYTLE so that the parser
|
|
* ignores it :)
|
|
*/
|
|
for (ptr=(char*)tag->data, end = ptr+tag->data_len; ptr < end; ptr++) {
|
|
if (*ptr == '"') {
|
|
in_double_quote = !in_double_quote;
|
|
}
|
|
else if (*ptr == '\'') {
|
|
in_single_quote = !in_single_quote;
|
|
}
|
|
else if (!in_single_quote &&
|
|
!in_double_quote &&
|
|
!strncasecomp(ptr, PARAM_STYLE, sizeof(PARAM_STYLE)-1)) {
|
|
*ptr = 'T'; /* ttyle NOT style */
|
|
break;
|
|
}
|
|
}
|
|
|
|
script_buff_len = XP_STRLEN(script_buff);
|
|
|
|
top_state = state->top_state;
|
|
|
|
/* get the tag ID if it has one */
|
|
new_id = (char *)lo_FetchParamValue(context, tag, PARAM_ID);
|
|
|
|
if (!new_id)
|
|
new_id = PR_smprintf(NSIMPLICITID"%ld", top_state->tag_count);
|
|
|
|
if (!new_id)
|
|
return FALSE;
|
|
|
|
if (top_state->default_style_script_type == SCRIPT_TYPE_JSSS ||
|
|
top_state->default_style_script_type == SCRIPT_TYPE_MOCHA) {
|
|
|
|
StrAllocCopy(scope_to, "document.ids.");
|
|
StrAllocCat(scope_to, new_id);
|
|
|
|
if (!scope_to) {
|
|
XP_FREE(new_id);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else if (top_state->default_style_script_type == SCRIPT_TYPE_CSS) {
|
|
char *new_jss_buff;
|
|
int32 new_jss_buff_len;
|
|
char *new_css_buff;
|
|
int32 new_css_buff_len;
|
|
|
|
/* scope the css to the ns implicit ID */
|
|
new_css_buff = PR_smprintf("#%s { %s }", new_id, script_buff);
|
|
|
|
XP_FREE(script_buff);
|
|
|
|
if (!new_css_buff) {
|
|
XP_FREE(new_id);
|
|
return FALSE;
|
|
}
|
|
|
|
new_css_buff_len = XP_STRLEN(new_css_buff);
|
|
|
|
/* convert to JSS */
|
|
CSS_ConvertToJS(new_css_buff,
|
|
new_css_buff_len,
|
|
&new_jss_buff,
|
|
&new_jss_buff_len);
|
|
|
|
XP_FREE(new_css_buff);
|
|
|
|
if (!new_jss_buff) {
|
|
XP_FREE(new_id);
|
|
return FALSE;
|
|
}
|
|
|
|
script_buff = new_jss_buff;
|
|
script_buff_len = new_jss_buff_len;
|
|
}
|
|
else {
|
|
/* unknown script type, punt */
|
|
XP_FREE(script_buff);
|
|
XP_FREE(new_id);
|
|
return FALSE;
|
|
}
|
|
|
|
/* not needed anymore */
|
|
XP_FREE(new_id);
|
|
|
|
stuff = (ETEvalStuff *) XP_NEW_ZAP(ETEvalStuff);
|
|
if (!stuff) {
|
|
/* what do we free here? */
|
|
XP_FREE(new_id);
|
|
return FALSE;
|
|
}
|
|
|
|
if (lo_CloneTagAndBlockLayout(context, state, tag)) {
|
|
tag->type = P_UNKNOWN;
|
|
stuff->len = XP_STRLEN(script_buff);
|
|
stuff->line_no = top_state->script_lineno;
|
|
stuff->scope_to = scope_to;
|
|
stuff->want_result = JS_FALSE;
|
|
stuff->data = context;
|
|
stuff->version = top_state->version;
|
|
stuff->principals = NULL;
|
|
|
|
ET_EvaluateScript(context, script_buff, stuff, lo_StyleEvalExitFn);
|
|
|
|
/* don't free scope_to, ET_EvaluateScript has taken possession */
|
|
XP_FREE(script_buff);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
XP_FREEIF(scope_to);
|
|
XP_FREE(script_buff);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef DEBUG_ScriptPlugin
|
|
#include "np.h"
|
|
|
|
PA_Tag *
|
|
LO_CreateHiddenEmbedTag(PA_Tag *old_tag, char * newTagStr)
|
|
{
|
|
PA_Tag *new_tag = PA_CloneMDLTag(old_tag);
|
|
if(new_tag)
|
|
{
|
|
char * buf = NULL;
|
|
new_tag->type = P_EMBED;
|
|
XP_FREE(new_tag->data);
|
|
new_tag->data = NULL;
|
|
StrAllocCopy(buf,newTagStr);
|
|
XP_ASSERT(buf != NULL);
|
|
new_tag->data = (PA_Block)buf;
|
|
new_tag->data_len = XP_STRLEN((char*)new_tag->data);
|
|
XP_ASSERT(new_tag->data_len != 0);
|
|
}
|
|
|
|
return(new_tag);
|
|
}
|
|
|
|
/* Generate mimetype string corresponding to this script language.
|
|
Specifically, <SCRIPT language = blah> should yield a mimetype
|
|
of "script/blah"
|
|
*/
|
|
char * npl_Script2mimeType(MWContext *context, PA_Tag *tag)
|
|
{
|
|
PA_Block buff;
|
|
char * language;
|
|
char * preface = "script/";
|
|
uint32 prefLen = XP_STRLEN(preface);
|
|
char * mimebuf = NULL;
|
|
uint32 len;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_LANGUAGE);
|
|
if (buff != NULL) {
|
|
PA_LOCK(language, char *, buff);
|
|
len = prefLen + XP_STRLEN(language);
|
|
mimebuf = XP_ALLOC(len+1);
|
|
if (mimebuf)
|
|
{
|
|
XP_STRCPY(mimebuf, preface);
|
|
XP_STRCAT(mimebuf, language);
|
|
}
|
|
PA_FREE(buff);
|
|
}
|
|
return mimebuf;
|
|
}
|
|
|
|
/* A plugin wants to handle this (otherwise unhandled) script language.
|
|
Now we redirect it to the plugin using the following steps:
|
|
|
|
1. Create a cachefile w/ the appropriate extension and store the contents of the script delims
|
|
2. Build a hidden, semi-internal embed tag pointing to the cachefile.
|
|
3. Cause Layout to initiate the hidden embed tag, which further starts netlib and other plugin
|
|
initialization.
|
|
|
|
*/
|
|
#define ALLOC_CHECK(a,id,abnormal_exit) {id++; if (!a) goto abnormal_exit;}
|
|
void npl_ScriptPlugin(MWContext *context, lo_DocState *state, PA_Tag *tag, size_t line_buf_len, char * mimebuf)
|
|
{
|
|
char * suffix = NULL;
|
|
char * fname = NULL;
|
|
char * embedtagstr = NULL;
|
|
char * NET_suffix = NULL;
|
|
PA_Tag * newTag = NULL;
|
|
NET_StreamClass* viewstream = NULL;
|
|
URL_Struct* urls = NULL;
|
|
int streamStatus = 0;
|
|
int id = 0;
|
|
|
|
if (!context || !state || !tag || (line_buf_len <= 0) || !mimebuf){
|
|
XP_Trace("npl_ScriptPlugin: Invalid input(s).");
|
|
return; /* no need to goto clean_exit, since havn't alloc'd anything */
|
|
}
|
|
|
|
urls = NET_CreateURLStruct("internal_url.", NET_DONT_RELOAD);
|
|
ALLOC_CHECK(urls,id,abnormal_exit);
|
|
|
|
/* NETLib gives me a pointer here instead of a copy of the string, so I have to make
|
|
my own, make checks, etc.
|
|
*/
|
|
NET_suffix = NET_cinfo_find_ext(mimebuf);
|
|
if (!NET_suffix)
|
|
goto abnormal_exit;
|
|
|
|
suffix = PR_smprintf("internal_url.%s", NET_suffix);
|
|
ALLOC_CHECK(suffix,id,abnormal_exit);
|
|
|
|
XP_FREEIF(urls->address);
|
|
StrAllocCopy(urls->address , suffix);
|
|
ALLOC_CHECK(urls->address,id,abnormal_exit);
|
|
|
|
XP_FREEIF(urls->content_name);
|
|
StrAllocCopy(urls->content_name, suffix);
|
|
ALLOC_CHECK(urls->content_name,id,abnormal_exit);
|
|
|
|
XP_FREEIF(urls->content_type);
|
|
StrAllocCopy(urls->content_type, mimebuf);
|
|
ALLOC_CHECK(urls->content_type,id,abnormal_exit);
|
|
|
|
urls->must_cache = 1; /* ugly flag for mailto and saveas */
|
|
|
|
viewstream = NET_StreamBuilder(FO_CACHE_ONLY, urls, context);
|
|
ALLOC_CHECK(viewstream,id,abnormal_exit);
|
|
|
|
streamStatus =
|
|
(*viewstream->put_block)(viewstream,(CONST char*)state->line_buf, line_buf_len);
|
|
if (streamStatus <= 0)
|
|
goto abnormal_exit;
|
|
(*viewstream->complete)(viewstream);
|
|
|
|
/* Now build internal embed tag pointing to this source: */
|
|
fname = WH_FileName(urls->cache_file, xpCache);
|
|
ALLOC_CHECK(fname,id,abnormal_exit);
|
|
embedtagstr = PR_smprintf("<EMBED SRC=file:///%s HIDDEN=TRUE>", fname);
|
|
ALLOC_CHECK(embedtagstr,id,abnormal_exit);
|
|
|
|
newTag = LO_CreateHiddenEmbedTag(tag,embedtagstr);
|
|
ALLOC_CHECK(newTag,id,abnormal_exit);
|
|
|
|
lo_FormatEmbed(context,state,newTag);
|
|
goto normal_exit;
|
|
|
|
abnormal_exit:
|
|
XP_Trace("npl_ScriptPlugin: Abnormal termination, id = %d",id);
|
|
if (urls) {
|
|
XP_FREEIF(urls->content_type);
|
|
XP_FREEIF(urls->content_name);
|
|
XP_FREEIF(urls->address);
|
|
XP_FREE( urls);
|
|
}
|
|
|
|
normal_exit:
|
|
XP_FREEIF(newTag);
|
|
XP_FREEIF(embedtagstr);
|
|
XP_FREEIF(fname);
|
|
XP_FREEIF(viewstream);
|
|
XP_FREEIF(suffix);
|
|
return;
|
|
|
|
}
|
|
#endif /* DEBUG_ScriptPlugin */
|
|
|
|
void
|
|
lo_ProcessScriptTag(MWContext *context, lo_DocState *state, PA_Tag *tag, JSObject *obj)
|
|
{
|
|
lo_TopState *top_state;
|
|
pa_DocData *doc_data;
|
|
XP_Bool type_explicitly_set=FALSE;
|
|
XP_Bool saw_archive=FALSE;
|
|
#ifdef DEBUG_ScriptPlugin
|
|
char * mimebuf = NULL;
|
|
#endif
|
|
|
|
top_state = state->top_state;
|
|
doc_data = (pa_DocData *)top_state->doc_data;
|
|
XP_ASSERT(doc_data != NULL || state->in_relayout || tag->lo_data);
|
|
|
|
if (tag->is_end == FALSE) {
|
|
PA_Block buff;
|
|
char *str, *url, *archiveSrc, *id, *codebase;
|
|
|
|
/* Controversial default language value. */
|
|
top_state->version = JSVERSION_DEFAULT;
|
|
if (tag->type == P_STYLE || tag->type == P_LINK) {
|
|
top_state->in_script = top_state->default_style_script_type;
|
|
}
|
|
else {
|
|
/* in order to get old script behaviour, pretend
|
|
* that the content-type is explicitly set for all scripts
|
|
*/
|
|
type_explicitly_set = TRUE;
|
|
top_state->in_script = SCRIPT_TYPE_MOCHA;
|
|
}
|
|
|
|
/* XXX account for HTML comment bytes and "lost" newlines */
|
|
if (lo_IsAnyCurrentAncestorSourced(state))
|
|
top_state->script_bytes = top_state->layout_bytes;
|
|
else
|
|
top_state->script_bytes = top_state->layout_bytes - tag->true_len;
|
|
if (tag->lo_data != NULL) {
|
|
top_state->script_bytes += (int32)tag->lo_data - 1;
|
|
tag->lo_data = NULL;
|
|
}
|
|
else if (doc_data != NULL) {
|
|
top_state->script_bytes += doc_data->comment_bytes;
|
|
}
|
|
else {
|
|
XP_ASSERT(state->in_relayout);
|
|
}
|
|
|
|
lo_ParseScriptLanguage(context, tag, &top_state->in_script,
|
|
&top_state->version);
|
|
#ifdef DEBUG_ScriptPlugin
|
|
if (top_state->in_script == SCRIPT_TYPE_UNKNOWN)
|
|
{
|
|
char* pluginName;
|
|
mimebuf = npl_Script2mimeType(context,tag);
|
|
if (mimebuf){
|
|
if ((pluginName = NPL_FindPluginEnabledForType(mimebuf)) != NULL){
|
|
top_state->in_script = SCRIPT_TYPE_PLUGIN;
|
|
XP_ASSERT(top_state->mimetype == NULL);
|
|
StrAllocCopy((char *)top_state->mimetype,mimebuf);
|
|
XP_FREE(mimebuf);
|
|
mimebuf = NULL;
|
|
}
|
|
else{
|
|
XP_FREE(mimebuf);
|
|
mimebuf = NULL;
|
|
}
|
|
}
|
|
}
|
|
#endif /* DEBUG_ScriptPlugin */
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_TYPE);
|
|
if (buff != NULL) {
|
|
PA_LOCK(str, char *, buff);
|
|
if ((XP_STRCASECMP(str, js_content_type) == 0) ||
|
|
(!XP_STRCASECMP(str, "text/javascript"))) {
|
|
if(tag->type == P_STYLE || tag->type == P_LINK)
|
|
{
|
|
top_state->in_script = SCRIPT_TYPE_JSSS;
|
|
top_state->default_style_script_type = SCRIPT_TYPE_JSSS;
|
|
}
|
|
else
|
|
{
|
|
top_state->in_script = SCRIPT_TYPE_MOCHA;
|
|
}
|
|
type_explicitly_set = TRUE;
|
|
}
|
|
else if ((XP_STRCASECMP(str, TEXT_CSS) == 0)) {
|
|
top_state->in_script = SCRIPT_TYPE_CSS;
|
|
top_state->default_style_script_type = SCRIPT_TYPE_CSS;
|
|
type_explicitly_set = TRUE;
|
|
}
|
|
else {
|
|
top_state->in_script = SCRIPT_TYPE_UNKNOWN;
|
|
top_state->default_style_script_type = SCRIPT_TYPE_UNKNOWN;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/* check for media=screen
|
|
* don't load the style sheet if there
|
|
* is a media not equal to screen
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_MEDIA);
|
|
if (buff) {
|
|
if (strcasecomp((char*)buff, "screen")) {
|
|
/* set the script type to UNKNOWN
|
|
* so that it will get thrown away
|
|
*/
|
|
top_state->in_script = SCRIPT_TYPE_UNKNOWN;
|
|
}
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* Flush the line buffer so we can start storing Mocha script
|
|
* source lines in there.
|
|
*/
|
|
lo_FlushLineBuffer(context, state);
|
|
|
|
url = archiveSrc = id = codebase = NULL;
|
|
if (top_state->in_script != SCRIPT_TYPE_NOT) {
|
|
/*
|
|
* Check for the archive parameter for known languages.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ARCHIVE);
|
|
if (buff != NULL) {
|
|
saw_archive = TRUE;
|
|
PA_LOCK(str, char *, buff);
|
|
url = NET_MakeAbsoluteURL(top_state->base_url, str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
if (url == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Look for ID attribute. If it's there we have may have
|
|
* an inline signed script.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ID);
|
|
if (buff != NULL) {
|
|
PA_LOCK(str, char *, buff);
|
|
StrAllocCopy(id, str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
if (id == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
XP_FREEIF(url);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now look for a SRC="url" attribute for known languages.
|
|
* If found, synchronously load the url.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SRC); /* XXX overloaded rv */
|
|
if (buff != NULL) {
|
|
XP_Bool allowFileSrc = FALSE;
|
|
char *absUrl;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
|
|
PREF_GetBoolPref(lo_jsAllowFileSrcFromNonFile, &allowFileSrc);
|
|
absUrl = NET_MakeAbsoluteURL(top_state->base_url, str);
|
|
if (absUrl == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
XP_FREEIF(id);
|
|
} else if (allowFileSrc == FALSE &&
|
|
NET_URL_Type(absUrl) == FILE_TYPE_URL &&
|
|
NET_URL_Type(top_state->url) != FILE_TYPE_URL) {
|
|
/*
|
|
* Deny access from http: to file: via SCRIPT SRC=...
|
|
* XXX silently
|
|
*/
|
|
top_state->in_script = SCRIPT_TYPE_UNKNOWN;
|
|
XP_FREE(absUrl);
|
|
XP_FREEIF(url);
|
|
XP_FREEIF(id);
|
|
} else if (url != NULL) {
|
|
XP_FREE(absUrl);
|
|
StrAllocCopy(archiveSrc, str);
|
|
if (archiveSrc == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
XP_FREE(url);
|
|
XP_FREEIF(id);
|
|
}
|
|
} else {
|
|
url = absUrl;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
if (top_state->out_of_memory)
|
|
return;
|
|
|
|
/*
|
|
* If we are doing a <script src=""> mocha script but JS
|
|
* is turned off just ignore the tag
|
|
*/
|
|
if (!LM_CanDoJS(context)) {
|
|
top_state->in_script = SCRIPT_TYPE_UNKNOWN;
|
|
XP_FREE(url);
|
|
XP_FREEIF(id);
|
|
XP_FREEIF(archiveSrc);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set text_divert so we know to accumulate text in line_buf
|
|
* without interpretation.
|
|
*/
|
|
state->text_divert = tag->type;
|
|
|
|
/*
|
|
* XXX need to stack these to handle blocked SCRIPT tags
|
|
*/
|
|
top_state->script_lineno = tag->newline_count + 1;
|
|
|
|
/* if we got here as a result of a LINK tag
|
|
* check to make sure rel=stylesheet and then
|
|
* check for an HREF and if one does not exist
|
|
* fail
|
|
*/
|
|
if (tag->type == P_LINK) {
|
|
char *cbuff = (char*)lo_FetchParamValue(context, tag, PARAM_REL);
|
|
|
|
if (cbuff && !strcasecomp(cbuff, "stylesheet")) {
|
|
XP_FREE(cbuff);
|
|
|
|
cbuff = (char*)lo_FetchParamValue(context, tag, PARAM_HREF);
|
|
|
|
if (cbuff) {
|
|
if (saw_archive && url) {
|
|
archiveSrc = XP_STRDUP(cbuff);
|
|
} else {
|
|
XP_FREEIF(url);
|
|
url = NET_MakeAbsoluteURL(top_state->base_url, cbuff);
|
|
}
|
|
}
|
|
}
|
|
|
|
XP_FREEIF(cbuff);
|
|
}
|
|
|
|
if (url != NULL || id != NULL || codebase != NULL) {
|
|
if ((doc_data != NULL) &&
|
|
(state->in_relayout == FALSE) &&
|
|
SCRIPT_EXEC_OK(top_state, state, tag->type, P_SCRIPT)) {
|
|
ScriptData *data;
|
|
|
|
data = XP_ALLOC(sizeof(ScriptData));
|
|
if (data == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
data->context = context;
|
|
data->state = state;
|
|
data->tag = PA_CloneMDLTag(tag);
|
|
if (data->tag == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
XP_FREE(data);
|
|
return;
|
|
}
|
|
data->url = url;
|
|
data->archiveSrc = archiveSrc;
|
|
data->id = id;
|
|
if (codebase == NULL) {
|
|
StrAllocCopy(codebase, top_state->base_url);
|
|
}
|
|
data->codebase = codebase;
|
|
data->buffer = NULL;
|
|
data->bufferSize = 0;
|
|
data->version = top_state->version;
|
|
|
|
/*
|
|
* Only SCRIPT ARCHIVE= ID= without SRC= is an inline signed
|
|
* script -- if there is a SRC= attribute, archiveSrc will be
|
|
* non-null.
|
|
*/
|
|
data->inlineSigned = (JSBool)
|
|
(url != NULL && archiveSrc == NULL && id != NULL);
|
|
|
|
/* Reset version accumulator */
|
|
top_state->version = JSVERSION_UNKNOWN;
|
|
|
|
XP_ASSERT (tag->type == P_SCRIPT || tag->type == P_STYLE ||
|
|
tag->type == P_LINK);
|
|
|
|
/*
|
|
* Out-of-line included (by src=) or inline signed script.
|
|
* Save continuatation data on top_state. If it's signed,
|
|
* we'll verify the signature once we see </script> and
|
|
* have the inline script to verify.
|
|
*/
|
|
top_state->scriptData = data;
|
|
|
|
}
|
|
else {
|
|
XP_FREE(url);
|
|
XP_FREEIF(id);
|
|
XP_FREEIF(archiveSrc);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
/*
|
|
* We are in the </script> tag now...
|
|
*/
|
|
|
|
size_t line_buf_len;
|
|
intn script_type;
|
|
char *scope_to=NULL;
|
|
char *untransformed = NULL;
|
|
|
|
script_type = top_state->in_script;
|
|
top_state->in_script = SCRIPT_TYPE_NOT;
|
|
|
|
/* guard against superfluous end tags */
|
|
if (script_type == SCRIPT_TYPE_NOT)
|
|
goto end_tag_out;
|
|
|
|
/* convert from CSS to JavaScript here */
|
|
if (tag->type != P_LINK && script_type == SCRIPT_TYPE_CSS) {
|
|
char *new_buffer;
|
|
int32 new_buffer_length;
|
|
|
|
CSS_ConvertToJS((char *)state->line_buf,
|
|
state->line_buf_len,
|
|
&new_buffer,
|
|
&new_buffer_length);
|
|
|
|
if (!new_buffer) {
|
|
/* css translator error, unblock layout and return */
|
|
state->text_divert = P_UNKNOWN;
|
|
state->line_buf_len = 0; /* clear script text */
|
|
goto end_tag_out;
|
|
}
|
|
|
|
untransformed = (char *) state->line_buf;
|
|
state->line_buf = (PA_Block) new_buffer;
|
|
state->line_buf_len = new_buffer_length;
|
|
state->line_buf_size = new_buffer_length;
|
|
|
|
if (state->line_buf_len)
|
|
state->line_buf_len--; /* hack: subtract one to remove final \n */
|
|
|
|
script_type = SCRIPT_TYPE_JSSS;
|
|
}
|
|
|
|
if (tag->type == P_STYLE) {
|
|
/* mocha scoped to document == jsss */
|
|
scope_to = "document";
|
|
}
|
|
|
|
/*
|
|
* Reset these before potentially recursing indirectly through
|
|
* the document.write() built-in function, which writes to the
|
|
* very same doc_data->parser_stream that this <SCRIPT> tag
|
|
* came in on.
|
|
*/
|
|
state->text_divert = P_UNKNOWN;
|
|
line_buf_len = state->line_buf_len;
|
|
state->line_buf_len = 0;
|
|
|
|
if (script_type != SCRIPT_TYPE_UNKNOWN &&
|
|
script_type != SCRIPT_TYPE_NOT) {
|
|
/*
|
|
* If mocha is disabled or can't be done in this context we
|
|
* are going to just ignore the buffer contents
|
|
*/
|
|
if (!LM_CanDoJS(context)) {
|
|
top_state->in_script = SCRIPT_TYPE_UNKNOWN;
|
|
goto end_tag_out;
|
|
}
|
|
|
|
if ((doc_data != NULL) &&
|
|
(state->in_relayout == FALSE) &&
|
|
SCRIPT_EXEC_OK(top_state, state, tag->type, P_SCRIPT)) {
|
|
|
|
/*
|
|
* First off, make sure layout is blocking on us
|
|
*/
|
|
if (lo_create_script_blockage(context, state,
|
|
tag->type == P_SCRIPT ? LO_SCRIPT : LO_UNKNOWN))
|
|
{
|
|
ScriptData *data;
|
|
|
|
/*
|
|
* Extreme hackery. Hideous and shameful. See the comment
|
|
* in lo_BlockScriptTag before similar is_end/overflow code
|
|
* and commence vomiting.
|
|
*/
|
|
lo_BlockScriptTag(context, state, NULL);
|
|
|
|
if (tag->is_end == (PRPackedBool)1) {
|
|
PA_PushOverflow(doc_data);
|
|
doc_data->overflow_depth ++;
|
|
}
|
|
|
|
/*
|
|
* Set the document.write tag insertion point.
|
|
*/
|
|
top_state->input_write_point[top_state->input_write_level] = &top_state->tags;
|
|
|
|
data = top_state->scriptData;
|
|
top_state->scriptData = NULL;
|
|
if (data && data->url) {
|
|
/*
|
|
* Three cases:
|
|
* 1. SCRIPT SRC=: url non-null
|
|
* 2. SCRIPT ARCHIVE= SRC=: url, archiveSrc non-null
|
|
* 3. SCRIPT ARCHIVE= ID=: url, id non-null
|
|
* In the last case, we copy the inline script into
|
|
* data's buffer and let lo_script_archive_exit_fn do
|
|
* the eval. We use an inlineSigned flag to avoid a
|
|
* bunch of (url != NULL && archiveSrc == NULL && id
|
|
* != NULL) tests.
|
|
*/
|
|
if (data->inlineSigned) {
|
|
StrAllocCopy(data->buffer, (char *) state->line_buf);
|
|
data->bufferSize = line_buf_len;
|
|
}
|
|
lo_GetScriptFromURL(data, script_type);
|
|
}
|
|
else {
|
|
JSPrincipals *principals = NULL;
|
|
ETEvalStuff * stuff;
|
|
|
|
if (data) {
|
|
principals = LM_NewJSPrincipals(NULL, data->id,
|
|
data->codebase);
|
|
if (untransformed &&
|
|
!LM_SetUntransformedSource(principals,
|
|
untransformed,
|
|
(char *) state->line_buf))
|
|
{
|
|
top_state->out_of_memory = TRUE;
|
|
}
|
|
lo_DestroyScriptData(data);
|
|
}
|
|
|
|
/*
|
|
* send the buffer off to be evaluated
|
|
*/
|
|
#ifdef DEBUG_ScriptPlugin
|
|
if (script_type == SCRIPT_TYPE_PLUGIN)
|
|
{
|
|
XP_ASSERT(mimebuf == NULL);
|
|
npl_ScriptPlugin(context, state, tag, line_buf_len,top_state->mimetype);
|
|
lo_unblock_script_tag(context, TRUE);
|
|
}
|
|
else
|
|
#endif /* DEBUG_ScriptPlugin */
|
|
|
|
|
|
stuff = (ETEvalStuff *) XP_NEW_ZAP(ETEvalStuff);
|
|
if (!stuff)
|
|
goto end_tag_out;
|
|
|
|
stuff->len = line_buf_len;
|
|
stuff->line_no = top_state->script_lineno;
|
|
if (scope_to)
|
|
stuff->scope_to = XP_STRDUP(scope_to);
|
|
else
|
|
stuff->scope_to = NULL;
|
|
stuff->want_result = JS_FALSE;
|
|
stuff->data = context;
|
|
stuff->version = top_state->version;
|
|
stuff->principals = principals;
|
|
|
|
ET_EvaluateScript(context,
|
|
(char *) state->line_buf,
|
|
stuff,
|
|
lo_ScriptEvalExitFn);
|
|
}
|
|
|
|
/* Reset version accumulator */
|
|
top_state->version = JSVERSION_UNKNOWN;
|
|
}
|
|
}
|
|
}
|
|
end_tag_out:
|
|
/*
|
|
* If we got a </SCRIPT> and still have scriptData set here, it must
|
|
* be left over from an error case above, so we free it.
|
|
*/
|
|
if (top_state->scriptData) {
|
|
XP_ASSERT(!top_state->layout_blocking_element);
|
|
lo_DestroyScriptData(top_state->scriptData);
|
|
top_state->scriptData = NULL;
|
|
}
|
|
XP_FREEIF(untransformed);
|
|
}
|
|
}
|
|
|
|
static char script_reblock_tag[] = "<" PT_NSCP_REBLOCK ">";
|
|
|
|
/*
|
|
* Create a tag that will reblock us
|
|
*/
|
|
void
|
|
LO_CreateReblockTag(MWContext * context, LO_Element * current_script)
|
|
{
|
|
lo_TopState *top_state;
|
|
pa_DocData *doc_data;
|
|
PA_Tag * tag/*, ** tag_ptr*/;
|
|
|
|
top_state = lo_FetchTopState(XP_DOCID(context));
|
|
doc_data = (pa_DocData *)top_state->doc_data;
|
|
|
|
tag = pa_CreateMDLTag(doc_data,
|
|
script_reblock_tag,
|
|
sizeof script_reblock_tag - 1);
|
|
|
|
if (tag == NULL)
|
|
return;
|
|
|
|
tag->lo_data = current_script;
|
|
|
|
/*
|
|
* Kludge in the write level for sanity check in lo_LayoutTag.
|
|
*/
|
|
tag->newline_count = (uint16)top_state->input_write_level;
|
|
|
|
/*
|
|
* Add the reblock tag to the tags list but don't advance the write_point
|
|
* in case we just wrote a nested script that also writes: the inner
|
|
* script must insert before the outer reblock tag, but after all the
|
|
* earlier tags.
|
|
*/
|
|
/* tag_ptr = top_state->input_write_point; */
|
|
lo_BlockTag(context, NULL, tag);
|
|
/* top_state->input_write_point = tag_ptr; */
|
|
}
|