diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-04 08:44:40 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-04 08:44:40 +0000 |
commit | c67455479bbba5d654e103de91bd50a3c694812d (patch) | |
tree | 6586041b8662728b2469f1785f24e7c10d957b63 /chrome | |
parent | cc5da3358b18bb13a0f6963a359b3e0e1b0e8267 (diff) | |
download | chromium_src-c67455479bbba5d654e103de91bd50a3c694812d.zip chromium_src-c67455479bbba5d654e103de91bd50a3c694812d.tar.gz chromium_src-c67455479bbba5d654e103de91bd50a3c694812d.tar.bz2 |
Commit http://codereview.chromium.org/27037
Implement GM_xmlhttpRequest
Review URL: http://codereview.chromium.org/39121
Patch from Steve Krulewitz <skrulx@gmail.com>.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10886 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rwxr-xr-x | chrome/renderer/extensions/greasemonkey_api_unittest.cc | 2 | ||||
-rw-r--r-- | chrome/renderer/resources/greasemonkey_api.js | 47 | ||||
-rw-r--r-- | chrome/test/data/extensions/greasemonkey_api_test.js | 332 | ||||
-rw-r--r-- | chrome/test/v8_unit_test.h | 51 |
4 files changed, 173 insertions, 259 deletions
diff --git a/chrome/renderer/extensions/greasemonkey_api_unittest.cc b/chrome/renderer/extensions/greasemonkey_api_unittest.cc index fef69a8..23bf07a 100755 --- a/chrome/renderer/extensions/greasemonkey_api_unittest.cc +++ b/chrome/renderer/extensions/greasemonkey_api_unittest.cc @@ -67,11 +67,9 @@ TEST_F(GreasemonkeyApiTest, AddStyle) { TestFunction("testAddStyle"); } -/* TEST_F(GreasemonkeyApiTest, XmlhttpRequest) { TestFunction("testXmlhttpRequest"); } -*/ TEST_F(GreasemonkeyApiTest, RegisterMenuCommand) { TestFunction("testRegisterMenuCommand"); diff --git a/chrome/renderer/resources/greasemonkey_api.js b/chrome/renderer/resources/greasemonkey_api.js index 57fa252..320e42a 100644 --- a/chrome/renderer/resources/greasemonkey_api.js +++ b/chrome/renderer/resources/greasemonkey_api.js @@ -63,18 +63,53 @@ function GM_getResourceText(resourceName) { } function GM_addStyle(css) { - var head = document.getElementsByTagName("head")[0]; - if (!head) { - return; + var parent = document.getElementsByTagName("head")[0]; + if (!parent) { + parent = document.documentElement; } var style = document.createElement("style"); style.type = "text/css"; - style.innerHTML = css; - head.appendChild(style); + var textNode = document.createTextNode(css); + style.appendChild(textNode); + parent.appendChild(style); } function GM_xmlhttpRequest(details) { - throw new Error("not implemented."); + function setupEvent(xhr, url, eventName, callback) { + xhr[eventName] = function () { + var isComplete = xhr.readyState == 4; + var responseState = { + responseText: xhr.responseText, + readyState: xhr.readyState, + responseHeaders: isComplete ? xhr.getAllResponseHeaders() : "", + status: isComplete ? xhr.status : 0, + statusText: isComplete ? xhr.statusText : "", + finalUrl: isComplete ? url : "" + }; + callback(responseState); + }; + } + + var xhr = new XMLHttpRequest(); + var eventNames = ["onload", "onerror", "onreadystatechange"]; + for (var i = 0; i < eventNames.length; i++ ) { + var eventName = eventNames[i]; + if (eventName in details) { + setupEvent(xhr, details.url, eventName, details[eventName]); + } + } + + xhr.open(details.method, details.url); + + if (details.overrideMimeType) { + xhr.overrideMimeType(details.overrideMimeType); + } + if (details.headers) { + for (var header in details.headers) { + xhr.setRequestHeader(header, details.headers[header]); + } + } + xhr.send(details.data ? details.data : null); } function GM_registerMenuCommand(commandName, commandFunc, accelKey, diff --git a/chrome/test/data/extensions/greasemonkey_api_test.js b/chrome/test/data/extensions/greasemonkey_api_test.js index d43e2a0..e629cee 100644 --- a/chrome/test/data/extensions/greasemonkey_api_test.js +++ b/chrome/test/data/extensions/greasemonkey_api_test.js @@ -1,6 +1,8 @@ var localStorage; var document; var window; +var XMLHttpRequest; +var lastCreatedXhr; function testGetSetValue() { localStorage = new LocalStorageFake(); @@ -65,19 +67,100 @@ function testGetResourceText() { } function testAddStyle() { + var documentElement = new ElementFake(''); var head = new ElementFake('head'); - document = new DocumentFake(head); + document = new DocumentFake(documentElement, head); var css = 'color: #decaff;'; GM_addStyle(css); + assert(0, documentElement.childNodes.length); assert(1, head.childNodes.length); var style = head.childNodes[0]; assert("style", style.tagName); assert("text/css", style.type); - assert(css, style.innerHTML); + assert(1, style.childNodes.length); + var textNode = style.childNodes[0]; + assert(css, textNode.text); + + document = new DocumentFake(documentElement, null); + GM_addStyle(css); + assert(1, documentElement.childNodes.length); + var style = documentElement.childNodes[0]; + assert("text/css", style.type); + assert(1, style.childNodes.length); + textNode = style.childNodes[0]; + assert(css, textNode.text); } function testXmlhttpRequest() { - var xhr = GM_xmlhttpRequest({}); + XMLHttpRequest = XMLHttpRequestFake; + var url = 'http://example.com'; + + var onLoadCallback = function(state) { + onLoadCallbackResponseState = state; + }; + var onErrorCallback = function(state) { + onErrorCallbackResponseState = state; + }; + var onReadyStateChangeCallback = function(state) { + onReadyStateChangeCallbackResponseState = state; + }; + + var details = { + onload: onLoadCallback, + onerror: onErrorCallback, + onreadystatechange: onReadyStateChangeCallback, + method: 'GET', + url: url, + overrideMimeType: 'text/html', + headers: { + 'X-Header': 'foo' + }, + data: 'data' + }; + + GM_xmlhttpRequest(details); + var xhr = lastCreatedXhr; + + assert('GET', xhr.openedMethod); + assert(url, xhr.openedUrl); + assert('text/html', xhr.overrideMimeType); + assert('foo', xhr.requestHeaders['X-Header']); + assert('data', xhr.sentBody); + + xhr.responseText = 'foo'; + xhr.responseHeaders['X-Response'] = 'foo'; + xhr.status = 200; + xhr.statusText = 'OK'; + + xhr.readyState = 1; + xhr.onreadystatechange(); + var state = onReadyStateChangeCallbackResponseState; + assert(xhr.responseText, state.responseText); + assert(xhr.readyState, state.readyState); + assert('', state.responseHeaders); + assert(0, state.status); + assert('', state.statusText); + assert('', state.finalUrl); + + xhr.readyState = 0; + xhr.onerror(); + state = onErrorCallbackResponseState; + assert(xhr.responseText, state.responseText); + assert(xhr.readyState, state.readyState); + assert('', state.responseHeaders); + assert(0, state.status); + assert('', state.statusText); + assert('', state.finalUrl); + + xhr.readyState = 4; + xhr.onload(); + state = onLoadCallbackResponseState; + assert(xhr.responseText, state.responseText); + assert(xhr.readyState, state.readyState); + assert('X-Response: foo\r\n', state.responseHeaders); + assert(xhr.status, state.status); + assert(xhr.statusText, state.statusText); + assert(url, state.finalUrl); } function testRegisterMenuCommand() { @@ -155,7 +238,8 @@ LocalStorageFake.prototype = { } } -function DocumentFake(head) { +function DocumentFake(documentElement, head) { + this.documentElement = documentElement; this.head_ = head; } DocumentFake.prototype = { @@ -167,224 +251,69 @@ DocumentFake.prototype = { }, createElement: function(tagName) { return new ElementFake(tagName); + }, + createTextNode: function(text) { + return new TextNodeFake(text); } } function ElementFake(tagName) { this.tagName = tagName; + this.childNodes = []; } ElementFake.prototype = { - childNodes: [], appendChild: function(e) { this.childNodes.push(e); return e; - }, -} - -function assert(expected, actual) { - if (expected !== actual) { - throw new Error('Assert failed: "' + expected + '" !== "' + actual + '"'); } } -function fail() { - throw new Error('Fail'); -} - -function assertThrow(f) { - var threw = false; - try { - f(); - } catch(e) { - threw = true; - } - if (!threw) { - throw new Error('Assert failed, expression did not throw.'); - } -} -var localStorage; -var document; -var window; - -function testGetSetValue() { - localStorage = new LocalStorageFake(); - GM_setValue('string', 'value'); - assert('value', GM_getValue('string')); - GM_setValue('integer', 123); - assert(123, GM_getValue('integer')); - GM_setValue('boolean', true); - assert(true, GM_getValue('boolean')); - - assert(undefined, GM_getValue('notset')); - assert('default', GM_getValue('notset', 'default')); - - // illegal values - assertThrow(function() { - GM_setValue('illegal value', null); - }); - assertThrow(function() { - GM_setValue('illegal value', 1.5); - }); -} - -function testDeleteValue() { - localStorage = new LocalStorageFake(); - GM_setValue('delete', 'value'); - assert('value', GM_getValue('delete')); - GM_deleteValue('delete'); - assert(undefined, GM_getValue('delete')); - GM_deleteValue('notset'); -} - -function testListValues() { - localStorage = new LocalStorageFake(); - var a = GM_listValues(); - assert(0, a.length); - - GM_setValue('one', 'first'); - a = GM_listValues(); - assert(1, a.length); - assert('one', a[0]); - - GM_setValue('two', 'second'); - a = GM_listValues(); - - // Test that keys that are not namespaced can't be read. - localStorage.setItem('three', 'third'); - assert(2, a.length); - assert(true, a.indexOf('one') >= 0); - assert(true, a.indexOf('two') >= 0); -} - -function testGetResourceURL() { - assertThrow(function() { - var resource = GM_getResourceURL('urlResourceName'); - }); -} - -function testGetResourceText() { - assertThrow(function() { - var resource = GM_getResourceText('textResourceName'); - }); -} - -function testAddStyle() { - var head = new ElementFake('head'); - document = new DocumentFake(head); - var css = 'color: #decaff;'; - GM_addStyle(css); - assert(1, head.childNodes.length); - var style = head.childNodes[0]; - assert("style", style.tagName); - assert("text/css", style.type); - assert(css, style.innerHTML); -} - -function testXmlhttpRequest() { - var xhr = GM_xmlhttpRequest({}); -} - -function testRegisterMenuCommand() { - assertThrow(function() { - GM_registerMenuCommand('name', function() {}, 'a', '', 'a'); - }); -} - -function testOpenInTab() { - var mockWindow = { - open: function(url, name, opt_options) { - this.openedUrl = url; - this.openedName = name; - this.openedOptions = opt_options; - } - }; - window = mockWindow; - var url = 'http://example.com'; - GM_openInTab(url); - assert(mockWindow.openedUrl, url); -} - -function testLog() { - var mockWindow = { - console: { - message: null, - log: function(message) { - this.message = message; - } - } - }; - window = mockWindow; - var message = 'hello world'; - GM_log(message); - assert(message, mockWindow.console.message); -} - -function LocalStorageFake() { - this.map_ = {}; - this.keys_ = []; -} -LocalStorageFake.prototype = { - length: 0, - key: function(index) { - if (index >= this.length) { - throw new Error('INDEX_SIZE_ERR'); - } - return this.keys_[index]; +function TextNodeFake(text) { + this.text = text; +} + +function XMLHttpRequestFake() { + lastCreatedXhr = this; + this.onload = null; + this.onerror = null; + this.onreadystatechange = null; + this.openedMethod = null; + this.openededUrl = null; + this.overriddenMimeType = null; + this.requestHeaders = {}; + this.sentBody = null; + this.responseText = null; + this.readyState = null; + this.responseHeaders = {}; + this.status = null; + this.statusText = null; +} +XMLHttpRequestFake.prototype = { + open: function(method, url) { + this.openedMethod = method; + this.openedUrl = url; }, - getItem: function(key) { - if (key in this.map_) { - return this.map_[key]; - } - return null; + overrideMimeType: function(mimeType) { + this.overrideMimeType = mimeType; }, - setItem: function(key, data) { - this.map_[key] = data; - this.updateKeys_(); + setRequestHeader: function(header, value) { + this.requestHeaders[header] = value; }, - removeItem: function(key) { - delete this.map_[key]; - this.updateKeys_(); - }, - clear: function() { - this.map_ = {}; - this.updateKeys_(); + send: function(opt_body) { + this.sentBody = opt_body; }, - updateKeys_: function() { - var keys = []; - for (var key in this.map_) { - keys.push(key); - } - this.keys_ = keys; - this.length = keys.length; - } -} - -function DocumentFake(head) { - this.head_ = head; -} -DocumentFake.prototype = { - getElementsByTagName: function(tagName) { - if (tagName == 'head' && this.head_) { - return [this.head_]; + getAllResponseHeaders: function() { + var s = ''; + for (var header in this.responseHeaders) { + // The delimiter used in Webkit's XMLHttpRequest is \r\n, however + // the new Chrome networking code (and Firefox) uses \n, so watch + // out for this! + s += header + ': ' + this.responseHeaders[header] + '\r\n'; } - return []; - }, - createElement: function(tagName) { - return new ElementFake(tagName); + return s; } } -function ElementFake(tagName) { - this.tagName = tagName; -} -ElementFake.prototype = { - childNodes: [], - appendChild: function(e) { - this.childNodes.push(e); - return e; - }, -} - function assert(expected, actual) { if (expected !== actual) { throw new Error('Assert failed: "' + expected + '" !== "' + actual + '"'); @@ -406,3 +335,4 @@ function assertThrow(f) { throw new Error('Assert failed, expression did not throw.'); } } + diff --git a/chrome/test/v8_unit_test.h b/chrome/test/v8_unit_test.h index 5b4c4ce..0ffddd2 100644 --- a/chrome/test/v8_unit_test.h +++ b/chrome/test/v8_unit_test.h @@ -11,7 +11,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "v8/include/v8.h" -// A superclass for unit tests that involve running JavaeScript. This class +// A superclass for unit tests that involve running JavaScript. This class // sets up V8 context and has methods that make it easy to execute scripts in // this context as well as call functions in the context. class V8UnitTest : public testing::Test { @@ -48,53 +48,4 @@ class V8UnitTest : public testing::Test { #endif // CHROME_TEST_V8_UNIT_TEST_H_ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_TEST_V8_UNIT_TEST_H_ -#define CHROME_TEST_V8_UNIT_TEST_H_ - -#include <string> - -#include "base/string_piece.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "v8/include/v8.h" - -// A superclass for unit tests that involve running JavaeScript. This class -// sets up V8 context and has methods that make it easy to execute scripts in -// this context as well as call functions in the context. -class V8UnitTest : public testing::Test { - public: - V8UnitTest() {} - virtual void SetUp(); - - protected: - // Executes the given script source in the context. The specified script - // name is used when reporting errors. - virtual void ExecuteScriptInContext(const StringPiece& script_source, - const StringPiece& script_name); - - // Converts a v8::TryCatch into a human readable string. - virtual std::string ExceptionToString(v8::TryCatch* try_catch); - - // Calls the specified function that resides in the global scope of the - // context. If the function throws an exception, FAIL() is called to - // indicate a unit test failure. This is useful for executing unit test - // functions implemented in JavaScript. - virtual void TestFunction(const std::string& function_name); - - // This method is bound to a global function "log" in the context. - // Scripts running in the context can call this to print out logging - // information to the console. - static v8::Handle<v8::Value> Log(const v8::Arguments& args); - - // Handle scope that is used throughout the life of this class. - v8::HandleScope handle_scope_; - - // Context for the JavaScript in the test. - v8::Handle<v8::Context> context_; -}; - -#endif // CHROME_TEST_V8_UNIT_TEST_H_ |