Reviewed by Sam Weinig
Fixes: http://bugs.webkit.org/show_bug.cgi?id=16523
<rdar://problem/
5657447>
When a frame is created with the URL "about:blank" or "", it should
inherit its SecurityOrigin from its opener. However, once it has
decided on that SecurityOrigin, it should not change its mind.
Prior to this patch, several events could induce the frame to change
its SecurityOrigin, permitting an attacker to inject script into an
arbitrary SecurityOrigin.
This patch makes several changes:
1) Documents refuse to change from one SecurityOrigin to another
unless explicitly instructed to do so.
2) Navigating to a JavaScript URL that produces a value
preserves the current SecurityOrigin explicitly instead of
relying on the URL to preserve the origin (which fails for
about:blank URLs and SecurityOrigins with document.domain set).
Ideally, we should not preserve the URL at all. Instead, the
frame's URL should be the JavaScript URL, as in Firefox, but this
would require changes that are too risky for this patch. I'll
file this as a separate issue.
3) Various methods of navigating to JavaScript URLs were not
properly handling JavaScript that returned a value (and should
therefore replace the current document). This patch unifies
those code paths with the path that works.
There are still a handful of bugs relating to the handling of
JavaScript URLs, but I'll file those as separate issues.
Tests: http/tests/security/aboutBlank/xss-DENIED-navigate-opener-document-write.html
http/tests/security/aboutBlank/xss-DENIED-navigate-opener-javascript-url.html
http/tests/security/aboutBlank/xss-DENIED-set-opener.html
* dom/Document.cpp:
(WebCore::Document::initSecurityOrigin):
* dom/Document.h:
(WebCore::Document::setSecurityOrigin):
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::changeLocation):
(WebCore::FrameLoader::urlSelected):
(WebCore::FrameLoader::requestFrame):
(WebCore::FrameLoader::submitForm):
(WebCore::FrameLoader::executeIfJavaScriptURL):
(WebCore::FrameLoader::begin):
* loader/FrameLoader.h:
* platform/SecurityOrigin.cpp:
(WebCore::SecurityOrigin::setForURL):
(WebCore::SecurityOrigin::createForFrame):
* platform/SecurityOrigin.h:
LayoutTests:
Reviewed by Sam Weinig.
Fixes: http://bugs.webkit.org/show_bug.cgi?id=16523
Adds new LayoutTests for scripting from about:blank windows. These
windows should inherit its SecurityOrigin from its opener and should
refuse to change their origins when their opener changes exogenously
(the navigate-opener tests) or explicitly (the set-opener test).
* http/tests/security/aboutBlank: Added.
* http/tests/security/aboutBlank/xss-DENIED-navigate-opener-document-write-expected.txt: Added.
* http/tests/security/aboutBlank/xss-DENIED-navigate-opener-document-write.html: Added.
* http/tests/security/aboutBlank/xss-DENIED-navigate-opener-javascript-url-expected.txt: Added.
* http/tests/security/aboutBlank/xss-DENIED-navigate-opener-javascript-url.html: Added.
* http/tests/security/aboutBlank/xss-DENIED-set-opener-expected.txt: Added.
* http/tests/security/aboutBlank/xss-DENIED-set-opener.html: Added.
* http/tests/security/resources/innocent-victim-with-notify.html: Added.
* http/tests/security/resources/innocent-victim.html: Added.
* http/tests/security/resources/libwrapjs.js: Added.
* http/tests/security/resources/open-window.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@29266
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2008-01-07 Adam Barth <hk9565@gmail.com>
+
+ Reviewed by Sam Weinig.
+
+ Fixes: http://bugs.webkit.org/show_bug.cgi?id=16523
+
+ Adds new LayoutTests for scripting from about:blank windows. These
+ windows should inherit its SecurityOrigin from its opener and should
+ refuse to change their origins when their opener changes exogenously
+ (the navigate-opener tests) or explicitly (the set-opener test).
+
+ * http/tests/security/aboutBlank: Added.
+ * http/tests/security/aboutBlank/xss-DENIED-navigate-opener-document-write-expected.txt: Added.
+ * http/tests/security/aboutBlank/xss-DENIED-navigate-opener-document-write.html: Added.
+ * http/tests/security/aboutBlank/xss-DENIED-navigate-opener-javascript-url-expected.txt: Added.
+ * http/tests/security/aboutBlank/xss-DENIED-navigate-opener-javascript-url.html: Added.
+ * http/tests/security/aboutBlank/xss-DENIED-set-opener-expected.txt: Added.
+ * http/tests/security/aboutBlank/xss-DENIED-set-opener.html: Added.
+ * http/tests/security/resources/innocent-victim-with-notify.html: Added.
+ * http/tests/security/resources/innocent-victim.html: Added.
+ * http/tests/security/resources/libwrapjs.js: Added.
+ * http/tests/security/resources/open-window.html: Added.
+
2008-01-07 Adele Peterson <adele@apple.com>
Reviewed by Antti, Adam, and Mitz.
--- /dev/null
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/innocent-victim-with-notify.html from frame with URL about:blank. Domains, protocols and ports must match.
+
+CONSOLE MESSAGE: line 1: Undefined value
+This page opens a window to "", injects malicious code, and then navigates its opener to the victim. The opened window then tries to scripts its opener after document.writeing a new document.
+Code injected into window:
+<script>document.write('<script>function write(target, message) { target.document.body.innerHTML = message; }setTimeout(function() {write(window.opener, \'FAIL: XSS was allowed.\');}, 100);setTimeout(function() {write(window.opener.top.frames[1], \'SUCCESS: Window remained in original SecurityOrigin.\');}, 200);setTimeout(function() { if (window.layoutTestController) layoutTestController.globalFlag = true; }, 300);<\/script>');</script>
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+This page doesn't do anything special (except signal that it has finished loading).
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+SUCCESS: Window remained in original SecurityOrigin.
--- /dev/null
+<html>
+<head>
+<script src="../resources/libwrapjs.js"></script>
+<script src="../resources/cross-frame-access.js"></script>
+<script>
+ var code;
+ var openedWindow;
+
+ window.onload = function()
+ {
+ if (window.layoutTestController) {
+ layoutTestController.waitUntilDone();
+ layoutTestController.setCanOpenWindows();
+ layoutTestController.dumpAsText();
+ layoutTestController.dumpChildFramesAsText();
+ }
+
+ var message_fail = 'FAIL: XSS was allowed.';
+ var message_success = 'SUCCESS: Window remained in original SecurityOrigin.';
+
+ var write_func = 'function write(target, message) { target.document.body.innerHTML = message; }';
+
+ var try_attack = 'write(window.opener, ' + libwrapjs.in_string(message_fail) + ');';
+ var attack = 'setTimeout(function() {' + try_attack + '}, 100);';
+
+ var try_control = 'write(window.opener.top.frames[1], ' + libwrapjs.in_string(message_success) + ');';
+ var control = 'setTimeout(function() {' + try_control + '}, 200);';
+
+ var sigDone = 'setTimeout(function() { if (window.layoutTestController) layoutTestController.globalFlag = true; }, 300);';
+
+ var payload = 'document.write(' + libwrapjs.in_string(libwrapjs.in_script_tag(write_func + attack + control + sigDone)) + ');';
+ code = libwrapjs.in_script_tag(payload);
+ log("Code injected into window:");
+ log(code);
+
+ if (window.layoutTestController) {
+ setTimeout(pollForTest1, 1);
+ } else {
+ log("To run the test, click the button below when the frames finish loading.");
+ var button = document.createElement("button");
+ button.appendChild(document.createTextNode("Run Test"));
+ button.onclick = runTest;
+ document.body.appendChild(button);
+ }
+ }
+
+ pollForTest1 = function()
+ {
+ if (!layoutTestController.globalFlag) {
+ setTimeout(pollForTest1, 1);
+ return;
+ }
+ runTest1();
+ }
+
+ runTest1 = function() {
+ frames[0].openWindow();
+ openedWindow = frames[0].openedWindow;
+
+ if (window.layoutTestController)
+ layoutTestController.globalFlag = false;
+
+ frames[0].location = 'http://localhost:8000/security/resources/innocent-victim-with-notify.html';
+
+ setTimeout(pollForTest2, 1);
+ }
+
+ pollForTest2 = function()
+ {
+ if (!layoutTestController.globalFlag) {
+ setTimeout(pollForTest2, 1);
+ return;
+ }
+ runTest2();
+ }
+
+ runTest2 = function()
+ {
+ openedWindow.document.write(code);
+ openedWindow.document.close();
+ if (window.layoutTestController) {
+ layoutTestController.globalFlag = false;
+ setTimeout(pollForDone, 1);
+ }
+ }
+
+ pollForDone = function()
+ {
+ if (!layoutTestController.globalFlag) {
+ setTimeout(pollForDone, 1);
+ return;
+ }
+ closeWindowAndNotifyDone(openedWindow);
+ }
+</script>
+</head>
+<body>
+<div>This page opens a window to "", injects malicious code, and
+then navigates its opener to the victim. The opened window then tries to
+scripts its opener after <code>document.write</code>ing a new document.</div>
+<pre id="console"></pre>
+<iframe style="border: solid 3px red;" src="../resources/open-window.html"></iframe>
+<iframe style="border: solid 3px green;" src="../resources/innocent-victim.html"></iframe>
+</body>
+</html>
--- /dev/null
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/innocent-victim-with-notify.html from frame with URL about:blank. Domains, protocols and ports must match.
+
+CONSOLE MESSAGE: line 1: Undefined value
+This page opens a window to "", injects malicious code, and then navigates its opener to the victim. The opened window then tries to scripts its opener after reloading itself as a javascript URL.
+Code injected into window:
+<script>window.location = 'javascript:\'<script>function write(target, message) { target.document.body.innerHTML = message; }setTimeout(function() {write(window.opener, \\\'FAIL: XSS was allowed.\\\');}, 100);setTimeout(function() {write(window.opener.top.frames[1], \\\'SUCCESS: Window remained in original SecurityOrigin.\\\');}, 200);setTimeout(function() { if (window.layoutTestController) layoutTestController.globalFlag = true; }, 300);<\\\/script>\''</script>
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+This page doesn't do anything special (except signal that it has finished loading).
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+SUCCESS: Window remained in original SecurityOrigin.
--- /dev/null
+<html>
+<head>
+<script src="../resources/libwrapjs.js"></script>
+<script src="../resources/cross-frame-access.js"></script>
+<script>
+ var code;
+ var openedWindow;
+
+ window.onload = function()
+ {
+ if (window.layoutTestController) {
+ layoutTestController.waitUntilDone();
+ layoutTestController.setCanOpenWindows();
+ layoutTestController.dumpAsText();
+ layoutTestController.dumpChildFramesAsText();
+ }
+
+ var message_fail = 'FAIL: XSS was allowed.';
+ var message_success = 'SUCCESS: Window remained in original SecurityOrigin.';
+
+ var write_func = 'function write(target, message) { target.document.body.innerHTML = message; }';
+
+ var try_attack = 'write(window.opener, ' + libwrapjs.in_string(message_fail) + ');';
+ var attack = 'setTimeout(function() {' + try_attack + '}, 100);';
+
+ var try_control = 'write(window.opener.top.frames[1], ' + libwrapjs.in_string(message_success) + ');';
+ var control = 'setTimeout(function() {' + try_control + '}, 200);';
+
+ var sigDone = 'setTimeout(function() { if (window.layoutTestController) layoutTestController.globalFlag = true; }, 300);';
+
+ var payload = 'window.location = ' + libwrapjs.in_javascript_document(write_func + attack + control + sigDone);
+ code = libwrapjs.in_script_tag(payload);
+ log("Code injected into window:");
+ log(code);
+
+ if (window.layoutTestController) {
+ setTimeout(pollForTest1, 1);
+ } else {
+ log("To run the test, click the button below when the frames finish loading.");
+ var button = document.createElement("button");
+ button.appendChild(document.createTextNode("Run Test"));
+ button.onclick = runTest;
+ document.body.appendChild(button);
+ }
+ }
+
+ pollForTest1 = function()
+ {
+ if (!layoutTestController.globalFlag) {
+ setTimeout(pollForTest1, 1);
+ return;
+ }
+ runTest1();
+ }
+
+ runTest1 = function() {
+ frames[0].openWindow();
+ openedWindow = frames[0].openedWindow;
+
+ if (window.layoutTestController)
+ layoutTestController.globalFlag = false;
+
+ frames[0].location = 'http://localhost:8000/security/resources/innocent-victim-with-notify.html';
+
+ setTimeout(pollForTest2, 1);
+ }
+
+ pollForTest2 = function()
+ {
+ if (!layoutTestController.globalFlag) {
+ setTimeout(pollForTest2, 1);
+ return;
+ }
+ runTest2();
+ }
+
+ runTest2 = function()
+ {
+ openedWindow.document.write(code);
+ openedWindow.document.close();
+ if (window.layoutTestController) {
+ layoutTestController.globalFlag = false;
+ setTimeout(pollForDone, 1);
+ }
+ }
+
+ pollForDone = function()
+ {
+ if (!layoutTestController.globalFlag) {
+ setTimeout(pollForDone, 1);
+ return;
+ }
+ closeWindowAndNotifyDone(openedWindow);
+ }
+</script>
+</head>
+<body>
+<div>This page opens a window to "", injects malicious code, and
+then navigates its opener to the victim. The opened window then tries to
+scripts its opener after reloading itself as a <code>javascript</code>
+URL.</div>
+<pre id="console"></pre>
+<iframe style="border: solid 3px red;" src="../resources/open-window.html"></iframe>
+<iframe style="border: solid 3px green;" src="../resources/innocent-victim.html"></iframe>
+</body>
+</html>
--- /dev/null
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/innocent-victim.html from frame with URL about:blank. Domains, protocols and ports must match.
+
+CONSOLE MESSAGE: line 1: Undefined value
+This page opens a window to "", injects malicious code, and then uses window.open.call to set its opener to the victim. The opened window then tries to scripts its opener.
+Code injected into window:
+<script>function write(target, message) { target.document.body.innerHTML = message; }
+setTimeout(function() {write(window.opener.top.frames[0], 'FAIL: XSS was allowed.');}, 100);
+setTimeout(function() {write(window.opener.top.frames[1], 'SUCCESS: Window remained in original SecurityOrigin.');}, 200);
+setTimeout(function() { if (window.layoutTestController) layoutTestController.globalFlag = true; }, 300);</script>
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+This page doesn't do anything special.
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+SUCCESS: Window remained in original SecurityOrigin.
--- /dev/null
+<html>
+<head>
+<script src="../resources/libwrapjs.js"></script>
+<script src="../resources/cross-frame-access.js"></script>
+<script>
+ var code;
+ var openedWindow;
+
+ window.onload = function()
+ {
+ if (window.layoutTestController) {
+ layoutTestController.waitUntilDone();
+ layoutTestController.setCanOpenWindows();
+ layoutTestController.dumpAsText();
+ layoutTestController.dumpChildFramesAsText();
+ }
+
+ var message_fail = 'FAIL: XSS was allowed.';
+ var message_success = 'SUCCESS: Window remained in original SecurityOrigin.';
+
+ var write_func = 'function write(target, message) { target.document.body.innerHTML = message; }\n';
+
+ var try_attack = 'write(window.opener.top.frames[0], ' + libwrapjs.in_string(message_fail) + ');';
+ var attack = 'setTimeout(function() {' + try_attack + '}, 100);\n';
+
+ var try_control = 'write(window.opener.top.frames[1], ' + libwrapjs.in_string(message_success) + ');';
+ var control = 'setTimeout(function() {' + try_control + '}, 200);\n';
+
+ var sigDone = 'setTimeout(function() { if (window.layoutTestController) layoutTestController.globalFlag = true; }, 300);';
+
+ var payload = write_func + attack + control + sigDone;
+ code = libwrapjs.in_script_tag(payload);
+ log("Code injected into window:");
+ log(code);
+
+ if (window.layoutTestController) {
+ runTest();
+ } else {
+ log("To run the test, click the button below when the frames finish loading.");
+ var button = document.createElement("button");
+ button.appendChild(document.createTextNode("Run Test"));
+ button.onclick = runTest;
+ document.body.appendChild(button);
+ }
+ }
+
+ runTest = function()
+ {
+ openedWindow = window.open('', 'attacker');
+ openedWindow.document.write(code);
+ openedWindow.document.close();
+
+ window.open.call(frames[0], '', 'attacker');
+
+ setTimeout(pollForDone, 1);
+ }
+
+ pollForDone = function()
+ {
+ if (!layoutTestController.globalFlag) {
+ setTimeout(pollForDone, 1);
+ return;
+ }
+ closeWindowAndNotifyDone(openedWindow);
+ }
+</script>
+</head>
+<body>
+<div>This page opens a window to "", injects malicious code, and
+then uses <code>window.open.call</code> to set its opener to the victim.
+The opened window then tries to scripts its opener.</div>
+<pre id="console"></pre>
+<iframe style="border: solid 3px red;" src="http://localhost:8000/security/resources/innocent-victim.html"></iframe>
+<iframe style="border: solid 3px green;" src="../resources/innocent-victim.html"></iframe>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+ <script>
+ onload = function()
+ {
+ if (window.layoutTestController)
+ layoutTestController.globalFlag = true;
+ }
+ </script>
+</head>
+<body>
+This page doesn't do anything special (except signal that it has finished loading).
+</body>
+</html>
--- /dev/null
+<html>
+<body>
+This page doesn't do anything special.
+</body>
+</html>
--- /dev/null
+// Library for wraping JavaScript code for different evaluation contexts.
+
+var libwrapjs = {
+ transform_each_character: function(str, transform) {
+ var result = new Array();
+ for (var i=0; i < str.length; ++i)
+ result.push(transform(str.charAt(i)));
+ return result.join('');
+ },
+
+ escape_for_single_quote: function(str) {
+ function transform(ch) {
+ if (ch == "\\")
+ return "\\\\";
+ if (ch == "/")
+ return "\\/";
+ if (ch == "'")
+ return "\\'";
+ return ch;
+ }
+ return this.transform_each_character(str, transform);
+ },
+
+ escape_for_html: function(str) {
+ function transform(ch) {
+ if (ch == "<")
+ return "<";
+ if (ch == ">")
+ return ">";
+ if (ch == "&")
+ return "&";
+ if (ch == "\n")
+ return "<br />"
+ if (ch == "\"")
+ return """;
+ return ch;
+ }
+ return this.transform_each_character(str, transform);
+ },
+
+ in_string: function(code) {
+ return "'" + this.escape_for_single_quote(code) + "'";
+ },
+
+ in_script_tag: function(code) {
+ return "<script>" + code + "</scr" + "ipt>";
+ },
+
+ in_document_write: function(code) {
+ return "document.write(" + this.in_string(this.in_script_tag(code)) + ")";
+ },
+
+ in_javascript_url: function(code) {
+ return this.in_string("javascript:void(" + code + ")");
+ },
+
+ in_javascript_document: function(code) {
+ return this.in_string("javascript:" +
+ this.in_string(this.in_script_tag(code)));
+ }
+};
+
--- /dev/null
+<html>
+<head>
+<script>
+ window.onload = function()
+ {
+ if (window.layoutTestController)
+ layoutTestController.globalFlag = true;
+ }
+
+ var windowURL = '';
+ var openedWindow;
+ function openWindow()
+ {
+ openedWindow = window.open(windowURL);
+ }
+</script>
+</head>
+<body>
+<button onclick="openWindow()">Open Window</button>
+</body>
+</html>
+
http/tests/security/javascriptURL/xss-ALLOWED-to-javascript-url-window-open.html
http/tests/security/javascriptURL/xss-DENIED-from-javascript-url-in-foreign-domain-window-open.html
http/tests/security/javascriptURL/xss-DENIED-to-javascript-url-in-foreign-domain-window-open.html
+http/tests/security/aboutBlank/xss-DENIED-set-opener.html
+http/tests/security/aboutBlank/xss-DENIED-navigate-opener-document-write.html
+http/tests/security/aboutBlank/xss-DENIED-navigate-opener-javascript-url.html
# DRT is not fully implemented in boomer <rdar://problem/5128261>
fast/dom/Window/setting-properties-on-closed-window.html
+2008-01-07 Adam Barth <hk9565@gmail.com>
+
+ Reviewed by Sam Weinig
+
+ Fixes: http://bugs.webkit.org/show_bug.cgi?id=16523
+ <rdar://problem/5657447>
+
+ When a frame is created with the URL "about:blank" or "", it should
+ inherit its SecurityOrigin from its opener. However, once it has
+ decided on that SecurityOrigin, it should not change its mind.
+ Prior to this patch, several events could induce the frame to change
+ its SecurityOrigin, permitting an attacker to inject script into an
+ arbitrary SecurityOrigin.
+
+ This patch makes several changes:
+
+ 1) Documents refuse to change from one SecurityOrigin to another
+ unless explicitly instructed to do so.
+
+ 2) Navigating to a JavaScript URL that produces a value
+ preserves the current SecurityOrigin explicitly instead of
+ relying on the URL to preserve the origin (which fails for
+ about:blank URLs and SecurityOrigins with document.domain set).
+
+ Ideally, we should not preserve the URL at all. Instead, the
+ frame's URL should be the JavaScript URL, as in Firefox, but this
+ would require changes that are too risky for this patch. I'll
+ file this as a separate issue.
+
+ 3) Various methods of navigating to JavaScript URLs were not
+ properly handling JavaScript that returned a value (and should
+ therefore replace the current document). This patch unifies
+ those code paths with the path that works.
+
+ There are still a handful of bugs relating to the handling of
+ JavaScript URLs, but I'll file those as separate issues.
+
+ Tests: http/tests/security/aboutBlank/xss-DENIED-navigate-opener-document-write.html
+ http/tests/security/aboutBlank/xss-DENIED-navigate-opener-javascript-url.html
+ http/tests/security/aboutBlank/xss-DENIED-set-opener.html
+
+ * dom/Document.cpp:
+ (WebCore::Document::initSecurityOrigin):
+ * dom/Document.h:
+ (WebCore::Document::setSecurityOrigin):
+ * loader/FrameLoader.cpp:
+ (WebCore::FrameLoader::changeLocation):
+ (WebCore::FrameLoader::urlSelected):
+ (WebCore::FrameLoader::requestFrame):
+ (WebCore::FrameLoader::submitForm):
+ (WebCore::FrameLoader::executeIfJavaScriptURL):
+ (WebCore::FrameLoader::begin):
+ * loader/FrameLoader.h:
+ * platform/SecurityOrigin.cpp:
+ (WebCore::SecurityOrigin::setForURL):
+ (WebCore::SecurityOrigin::createForFrame):
+ * platform/SecurityOrigin.h:
+
2008-01-07 Adele Peterson <adele@apple.com>
Forgot to check in these changes in my last checkin.
void Document::initSecurityOrigin()
{
+ if (m_securityOrigin && !m_securityOrigin->isEmpty())
+ return; // m_securityOrigin has already been initialized.
+
m_securityOrigin = SecurityOrigin::createForFrame(m_frame);
}
void initSecurityOrigin();
SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); }
+ // Explicitly override the security origin for this document.
+ // Note: It is dangerous to change the security origin of a document
+ // that already contains content.
+ void setSecurityOrigin(SecurityOrigin* o) { m_securityOrigin = o; }
+
bool processingLoadEvent() const { return m_processingLoadEvent; }
#if ENABLE(DATABASE)
void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool userGesture)
{
- if (url.deprecatedString().find("javascript:", 0, false) == 0) {
- String script = KURL::decode_string(url.deprecatedString().mid(strlen("javascript:")));
- JSValue* result = executeScript(script, userGesture);
- String scriptResult;
- if (getString(result, scriptResult)) {
- begin(m_URL);
- write(scriptResult);
- end();
- }
- return;
- }
-
ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh)
? ReloadIgnoringCacheData : UseProtocolCachePolicy;
ResourceRequest request(url, referrer, policy);
void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool userGesture)
{
+ if (executeIfJavaScriptURL(request.url(), userGesture))
+ return;
+
String target = _target;
if (target.isEmpty() && m_frame->document())
target = m_frame->document()->baseTarget();
- const KURL& url = request.url();
- if (url.deprecatedString().startsWith("javascript:", false)) {
- executeScript(KURL::decode_string(url.deprecatedString().mid(strlen("javascript:"))), true);
- return;
- }
-
FrameLoadRequest frameRequest(request, target);
if (frameRequest.resourceRequest().httpReferrer().isEmpty())
return false;
if (!scriptURL.isEmpty())
- frame->loader()->replaceContentsWithScriptResult(scriptURL);
+ frame->loader()->executeIfJavaScriptURL(scriptURL);
return true;
}
DeprecatedString urlString = u.deprecatedString();
if (urlString.startsWith("javascript:", false)) {
m_isExecutingJavaScriptFormAction = true;
- executeScript(KURL::decode_string(urlString.mid(strlen("javascript:"))));
+ executeIfJavaScriptURL(u);
m_isExecutingJavaScriptFormAction = false;
return;
}
m_URL = m_frame->document()->url();
}
-void FrameLoader::replaceContentsWithScriptResult(const KURL& url)
+bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture)
{
- JSValue* result = executeScript(KURL::decode_string(url.deprecatedString().mid(strlen("javascript:"))));
+ if (!url.deprecatedString().startsWith("javascript:", false))
+ return false;
+
+ String script = KURL::decode_string(url.deprecatedString().mid(strlen("javascript:")));
+ JSValue* result = executeScript(script, userGesture);
+
String scriptResult;
if (!getString(result, scriptResult))
- return;
- begin();
+ return true;
+
+ SecurityOrigin* currentSecurityOrigin = 0;
+ if (m_frame->document())
+ currentSecurityOrigin = m_frame->document()->securityOrigin();
+
+ begin(m_URL, true, currentSecurityOrigin);
write(scriptResult);
end();
+
+ return true;
}
JSValue* FrameLoader::executeScript(const String& script, bool forceUserGesture)
begin(KURL());
}
-void FrameLoader::begin(const KURL& url, bool dispatch)
+void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin)
{
+ // We need to take a reference to the security origin because |clear|
+ // might destroy the document that owns it.
+ RefPtr<SecurityOrigin> forcedSecurityOrigin = origin;
+
bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url));
clear(resetScripting, resetScripting);
if (dispatch)
document->setBaseURL(baseurl.deprecatedString());
if (m_decoder)
document->setDecoder(m_decoder.get());
+ if (forcedSecurityOrigin)
+ document->setSecurityOrigin(forcedSecurityOrigin.get());
updatePolicyBaseURL();
class ResourceLoader;
class ResourceRequest;
class ResourceResponse;
+ class SecurityOrigin;
class SharedBuffer;
class SubstituteData;
class TextResourceDecoder;
KURL historyURL(int distance);
void begin();
- void begin(const KURL&, bool dispatchWindowObjectAvailable = true);
+ void begin(const KURL&, bool dispatchWindowObjectAvailable = true, SecurityOrigin* forcedSecurityOrigin = 0);
void write(const char* str, int len = -1, bool flush = false);
void write(const String&);
void setEncoding(const String& encoding, bool userChosen);
String encoding() const;
+ // Returns true if url is a JavaScript URL.
+ bool executeIfJavaScriptURL(const KURL& url, bool userGesture = false);
+
KJS::JSValue* executeScript(const String& url, int baseLine, const String& script);
KJS::JSValue* executeScript(const String& script, bool forceUserGesture = false);
void updatePolicyBaseURL();
void setPolicyBaseURL(const String&);
- void replaceContentsWithScriptResult(const KURL&);
-
// Also not cool.
void stopLoadingSubframes();
namespace WebCore {
-SecurityOrigin::SecurityOrigin()
+SecurityOrigin::SecurityOrigin(const KURL& url)
: m_port(0)
, m_portSet(false)
, m_noAccess(false)
, m_domainWasSetInDOM(false)
{
-}
+ if (url.isEmpty())
+ return;
-void SecurityOrigin::clear()
-{
- m_protocol = String();
- m_host = String();
- m_port = 0;
- m_portSet = false;
- m_noAccess = false;
- m_domainWasSetInDOM = false;
-}
+ m_protocol = url.protocol().lower();
-bool SecurityOrigin::isEmpty() const
-{
- return m_protocol.isEmpty();
-}
+ // These protocols do not represent principals.
+ if (m_protocol == "about" || m_protocol == "javascript")
+ m_protocol = String();
-void SecurityOrigin::setForURL(const KURL& url)
-{
- clear();
+ if (m_protocol.isEmpty())
+ return;
- if (url.isEmpty())
- return;
+ // data: URLs are not allowed access to anything other than themselves.
+ if (m_protocol == "data")
+ m_noAccess = true;
- m_protocol = url.protocol().lower();
m_host = url.host().lower();
m_port = url.port();
if (m_port)
m_portSet = true;
+}
- // data: URLs are not allowed access to anything other than themselves.
- if (m_protocol == "data")
- m_noAccess = true;
+bool SecurityOrigin::isEmpty() const
+{
+ return m_protocol.isEmpty();
}
PassRefPtr<SecurityOrigin> SecurityOrigin::createForFrame(Frame* frame)
{
- RefPtr<SecurityOrigin> origin = new SecurityOrigin();
-
if (!frame)
- return origin;
+ return new SecurityOrigin(KURL());
FrameLoader* loader = frame->loader();
- const KURL& securityPolicyURL = loader->url();
-
- origin->setForURL(securityPolicyURL);
- if (!origin->isEmpty() && origin->m_protocol != "about")
+ RefPtr<SecurityOrigin> origin = new SecurityOrigin(loader->url());
+ if (!origin->isEmpty())
return origin;
- // In the case of about:blank or javascript: URLs (which create
- // documents using the "about" protocol) do we want to use the
- // parent or openers origin.
+ // If we do not obtain a principal from the URL, then we try to find a
+ // principal via the frame hierarchy.
Frame* openerFrame = frame->tree()->parent();
if (!openerFrame) {
bool canAccess(const SecurityOrigin*) const;
bool isSecureTransitionTo(const KURL&) const;
+ bool isEmpty() const;
String toString() const;
SecurityOriginData securityOriginData() const;
private:
- SecurityOrigin();
- bool isEmpty() const;
-
- void clear();
- void setForURL(const KURL& url);
+ SecurityOrigin(const KURL& url);
String m_protocol;
String m_host;