diff --git a/src/ecmascript/Makefile b/src/ecmascript/Makefile
index ab936a025..f8bbc2f85 100644
--- a/src/ecmascript/Makefile
+++ b/src/ecmascript/Makefile
@@ -18,6 +18,6 @@ endif
 
 OBJS-$(CONFIG_ANY_SPIDERMONKEY) += spidermonkey-shared.o
 
-OBJS	= ecmascript.o
+OBJS	= ecmascript.o location-goto.o
 
 include $(top_srcdir)/Makefile.lib
diff --git a/src/ecmascript/ecmascript.h b/src/ecmascript/ecmascript.h
index e8d84b57e..ebbc00776 100644
--- a/src/ecmascript/ecmascript.h
+++ b/src/ecmascript/ecmascript.h
@@ -8,6 +8,7 @@
 #include "main/module.h"
 #include "util/time.h"
 
+struct document_view;
 struct form_state;
 struct form_view;
 struct string;
@@ -95,4 +96,6 @@ void ecmascript_set_timeout(struct ecmascript_interpreter *interpreter, unsigned
 
 extern struct module ecmascript_module;
 
+void location_goto(struct document_view *doc_view, unsigned char *url);
+
 #endif
diff --git a/src/ecmascript/location-goto.c b/src/ecmascript/location-goto.c
new file mode 100644
index 000000000..a5330127a
--- /dev/null
+++ b/src/ecmascript/location-goto.c
@@ -0,0 +1,74 @@
+/** Let web scripts navigate to a different location.
+ * Used for both SpiderMonkey and SEE.
+ * @file */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "elinks.h"
+
+#include "document/document.h"
+#include "document/view.h"
+#include "ecmascript/ecmascript.h" /* declares location_goto() defined here */
+#include "main/select.h"
+#include "protocol/uri.h"
+#include "session/task.h"
+#include "util/conv.h"
+#include "util/error.h"
+#include "viewer/text/vs.h"
+
+static void delayed_goto(void *);
+
+struct delayed_goto {
+	/* It might look more convenient to pass doc_view around but it could
+	 * disappear during wild dances inside of frames or so. */
+	struct view_state *vs;
+	struct uri *uri;
+};
+
+static void
+delayed_goto(void *data)
+{
+	struct delayed_goto *deg = data;
+
+	assert(deg);
+	if (deg->vs->doc_view
+	    && deg->vs->doc_view == deg->vs->doc_view->session->doc_view) {
+		goto_uri_frame(deg->vs->doc_view->session, deg->uri,
+		               deg->vs->doc_view->name,
+			       CACHE_MODE_NORMAL);
+	}
+	done_uri(deg->uri);
+	mem_free(deg);
+}
+
+void
+location_goto(struct document_view *doc_view, unsigned char *url)
+{
+	unsigned char *new_abs_url;
+	struct uri *new_uri;
+	struct delayed_goto *deg;
+
+	/* Workaround for bug 611. Does not crash, but may lead to infinite loop.*/
+	if (!doc_view) return;
+	new_abs_url = join_urls(doc_view->document->uri,
+	                        trim_chars(url, ' ', 0));
+	if (!new_abs_url)
+		return;
+	new_uri = get_uri(new_abs_url, 0);
+	mem_free(new_abs_url);
+	if (!new_uri)
+		return;
+	deg = mem_calloc(1, sizeof(*deg));
+	if (!deg) {
+		done_uri(new_uri);
+		return;
+	}
+	assert(doc_view->vs);
+	deg->vs = doc_view->vs;
+	deg->uri = new_uri;
+	/* It does not seem to be very safe inside of frames to
+	 * call goto_uri() right away. */
+	register_bottom_half(delayed_goto, deg);
+}
diff --git a/src/ecmascript/see/document.c b/src/ecmascript/see/document.c
index 3287146eb..ad0cd6897 100644
--- a/src/ecmascript/see/document.c
+++ b/src/ecmascript/see/document.c
@@ -54,8 +54,6 @@ static int document_hasproperty(struct SEE_interpreter *, struct SEE_object *, s
 static void js_document_write(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
 static void js_document_writeln(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
 
-void location_goto(struct document_view *, unsigned char *);
-
 struct SEE_objectclass js_document_object_class = {
 	"document",
 	document_get,
diff --git a/src/ecmascript/see/location.c b/src/ecmascript/see/location.c
index 88f56a440..6ece0394e 100644
--- a/src/ecmascript/see/location.c
+++ b/src/ecmascript/see/location.c
@@ -47,7 +47,6 @@
 #include "viewer/text/link.h"
 #include "viewer/text/vs.h"
 
-static void delayed_goto(void *);
 static void history_get(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *, struct SEE_value *);
 static int history_hasproperty(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *);
 static void js_history_back(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
@@ -59,8 +58,6 @@ static void location_put(struct SEE_interpreter *, struct SEE_object *, struct S
 static int location_hasproperty(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *);
 static int location_canput(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *);
 
-void location_goto(struct document_view *, unsigned char *);
-
 struct js_history_object {
 	struct SEE_object object;
 	struct SEE_object *back;
@@ -73,13 +70,6 @@ struct js_location_object {
 	struct SEE_object *toString;
 };
 
-struct delayed_goto {
-	/* It might look more convenient to pass doc_view around but it could
-	 * disappear during wild dances inside of frames or so. */
-	struct view_state *vs;
-	struct uri *uri;
-};
-
 struct SEE_objectclass js_history_object_class = {
 	"history",
 	history_get,
@@ -108,52 +98,6 @@ struct SEE_objectclass js_location_object_class = {
 	NULL
 };
 
-static void
-delayed_goto(void *data)
-{
-	struct delayed_goto *deg = data;
-
-	assert(deg);
-	if (deg->vs->doc_view
-	    && deg->vs->doc_view == deg->vs->doc_view->session->doc_view) {
-		goto_uri_frame(deg->vs->doc_view->session, deg->uri,
-		               deg->vs->doc_view->name,
-			       CACHE_MODE_NORMAL);
-	}
-	done_uri(deg->uri);
-	mem_free(deg);
-}
-
-void
-location_goto(struct document_view *doc_view, unsigned char *url)
-{
-	unsigned char *new_abs_url;
-	struct uri *new_uri;
-	struct delayed_goto *deg;
-
-	/* Workaround for bug 611. Does not crash, but may lead to infinite loop.*/
-	if (!doc_view) return;
-	new_abs_url = join_urls(doc_view->document->uri,
-	                        trim_chars(url, ' ', 0));
-	if (!new_abs_url)
-		return;
-	new_uri = get_uri(new_abs_url, 0);
-	mem_free(new_abs_url);
-	if (!new_uri)
-		return;
-	deg = mem_calloc(1, sizeof(*deg));
-	if (!deg) {
-		done_uri(new_uri);
-		return;
-	}
-	assert(doc_view->vs);
-	deg->vs = doc_view->vs;
-	deg->uri = new_uri;
-	/* It does not seem to be very safe inside of frames to
-	 * call goto_uri() right away. */
-	register_bottom_half(delayed_goto, deg);
-}
-
 
 static void
 history_get(struct SEE_interpreter *interp, struct SEE_object *o,
diff --git a/src/ecmascript/see/window.c b/src/ecmascript/see/window.c
index 5b2b44492..f3c34da27 100644
--- a/src/ecmascript/see/window.c
+++ b/src/ecmascript/see/window.c
@@ -57,8 +57,6 @@ static void js_window_alert(struct SEE_interpreter *, struct SEE_object *, struc
 static void js_window_open(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
 static void js_setTimeout(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
 
-void location_goto(struct document_view *, unsigned char *);
-
 struct SEE_objectclass js_window_object_class = {
 	"window",
 	window_get,
diff --git a/src/ecmascript/spidermonkey/location.c b/src/ecmascript/spidermonkey/location.c
index dd5b40b07..b667813d2 100644
--- a/src/ecmascript/spidermonkey/location.c
+++ b/src/ecmascript/spidermonkey/location.c
@@ -236,56 +236,3 @@ location_toString(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval
 {
 	return JS_GetProperty(ctx, obj, "href", rval);
 }
-
-struct delayed_goto {
-	/* It might look more convenient to pass doc_view around but it could
-	 * disappear during wild dances inside of frames or so. */
-	struct view_state *vs;
-	struct uri *uri;
-};
-
-static void
-delayed_goto(void *data)
-{
-	struct delayed_goto *deg = data;
-
-	assert(deg);
-	if (deg->vs->doc_view
-	    && deg->vs->doc_view == deg->vs->doc_view->session->doc_view) {
-		goto_uri_frame(deg->vs->doc_view->session, deg->uri,
-		               deg->vs->doc_view->name,
-			       CACHE_MODE_NORMAL);
-	}
-	done_uri(deg->uri);
-	mem_free(deg);
-}
-
-void
-location_goto(struct document_view *doc_view, unsigned char *url)
-{
-	unsigned char *new_abs_url;
-	struct uri *new_uri;
-	struct delayed_goto *deg;
-
-	/* Workaround for bug 611. Does not crash, but may lead to infinite loop.*/
-	if (!doc_view) return;
-	new_abs_url = join_urls(doc_view->document->uri,
-	                        trim_chars(url, ' ', 0));
-	if (!new_abs_url)
-		return;
-	new_uri = get_uri(new_abs_url, 0);
-	mem_free(new_abs_url);
-	if (!new_uri)
-		return;
-	deg = mem_calloc(1, sizeof(*deg));
-	if (!deg) {
-		done_uri(new_uri);
-		return;
-	}
-	assert(doc_view->vs);
-	deg->vs = doc_view->vs;
-	deg->uri = new_uri;
-	/* It does not seem to be very safe inside of frames to
-	 * call goto_uri() right away. */
-	register_bottom_half(delayed_goto, deg);
-}
diff --git a/src/ecmascript/spidermonkey/location.h b/src/ecmascript/spidermonkey/location.h
index 864eca38a..3664a2591 100644
--- a/src/ecmascript/spidermonkey/location.h
+++ b/src/ecmascript/spidermonkey/location.h
@@ -13,6 +13,4 @@ extern const JSClass location_class;
 extern const spidermonkeyFunctionSpec location_funcs[];
 extern const JSPropertySpec location_props[];
 
-void location_goto(struct document_view *doc_view, unsigned char *url);
-
 #endif
diff --git a/src/ecmascript/spidermonkey/window.c b/src/ecmascript/spidermonkey/window.c
index 5e93a6fc5..0be81085b 100644
--- a/src/ecmascript/spidermonkey/window.c
+++ b/src/ecmascript/spidermonkey/window.c
@@ -250,8 +250,6 @@ found_parent:
 	return JS_TRUE;
 }
 
-void location_goto(struct document_view *doc_view, unsigned char *url);
-
 /* @window_class.setProperty */
 static JSBool
 window_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)