This commit is contained in:
Richard Newman 2011-12-14 11:28:32 -08:00
Родитель 089dbf05e1 8fff220ee1
Коммит 7b7f96fa9b
136 изменённых файлов: 10486 добавлений и 5459 удалений

Просмотреть файл

@ -42,7 +42,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = chrome locales app
DIRS = chrome components locales app
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/testing/testsuite-targets.mk

Просмотреть файл

@ -65,11 +65,7 @@ pref("browser.cache.memory.capacity", 1024); // kilobytes
pref("image.cache.size", 1048576); // bytes
/* offline cache prefs */
pref("browser.offline-apps.notify", true);
pref("browser.cache.offline.enable", true);
pref("browser.cache.offline.capacity", 5120); // kilobytes
pref("offline-apps.quota.max", 2048); // kilobytes
pref("offline-apps.quota.warn", 1024); // kilobytes
pref("browser.offline-apps.notify", false);
/* protocol warning prefs */
pref("network.protocol-handler.warn-external.tel", false);

Просмотреть файл

@ -68,6 +68,7 @@ function startupHttpd(baseDir, port) {
Services.scriptloader.loadSubScript(httpdURL, httpd);
let server = new httpd.nsHttpServer();
server.registerDirectory('/', new LocalFile(baseDir));
server.registerContentType('appcache', 'text/cache-manifest');
server.start(port);
}
@ -114,6 +115,7 @@ var shell = {
window.controllers.appendController(this);
window.addEventListener('keypress', this);
window.addEventListener('MozApplicationManifest', this);
this.home.addEventListener('load', this, true);
try {
@ -147,6 +149,7 @@ var shell = {
stop: function shell_stop() {
window.controllers.removeController(this);
window.removeEventListener('keypress', this);
window.removeEventListener('MozApplicationManifest', this);
},
supportsCommand: function shell_supportsCommand(cmd) {
@ -195,6 +198,29 @@ var shell = {
this.home.removeEventListener('load', this, true);
this.sendEvent(window, 'ContentStart');
break;
case 'MozApplicationManifest':
try {
let contentWindow = evt.originalTarget.defaultView;
let documentElement = contentWindow.document.documentElement;
if (!documentElement)
return;
let manifest = documentElement.getAttribute("manifest");
if (!manifest)
return;
let documentURI = contentWindow.document.documentURIObject;
let manifestURI = Services.io.newURI(manifest, null, documentURI);
Services.perms.add(documentURI, 'offline-app',
Ci.nsIPermissionManager.ALLOW_ACTION);
let updateService = Cc['@mozilla.org/offlinecacheupdate-service;1']
.getService(Ci.nsIOfflineCacheUpdateService);
updateService.scheduleUpdate(manifestURI, documentURI, window);
} catch (e) {
dump('Error while creating offline cache: ' + e + '\n');
}
break;
}
},
sendEvent: function shell_sendEvent(content, type, details) {

Просмотреть файл

Просмотреть файл

@ -1,4 +1,3 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
@ -12,18 +11,18 @@
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
# The Original Code is Mozilla.
#
# The Initial Developer of the Original Code is
# Netscape Communications Corporation.
# Portions created by the Initial Developer are Copyright (C) 1998
# the Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
@ -35,23 +34,18 @@
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = uconv
LIBRARY_NAME = ucvxutil_s
EXPORT_LIBRARY = 1
FORCE_STATIC_LIB = 1
MODULE = B2GComponents
XPIDL_MODULE = B2GComponents
MODULE_NAME = nsExternalUCUtil
VPATH += $(srcdir)/..
include $(srcdir)/../objs.mk
EXTRA_PP_COMPONENTS = \
B2GComponents.manifest \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -42,6 +42,7 @@ security/manager/locales/Makefile
b2g/app/Makefile
$MOZ_BRANDING_DIRECTORY/Makefile
b2g/chrome/Makefile
b2g/components/Makefile
b2g/installer/Makefile
b2g/locales/Makefile
b2g/Makefile"

Просмотреть файл

@ -945,6 +945,7 @@ pref("services.sync.prefs.sync.browser.safebrowsing.enabled", true);
pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true);
pref("services.sync.prefs.sync.browser.search.selectedEngine", true);
pref("services.sync.prefs.sync.browser.search.update", true);
pref("services.sync.prefs.sync.browser.sessionstore.restore_on_demand", true);
pref("services.sync.prefs.sync.browser.startup.homepage", true);
pref("services.sync.prefs.sync.browser.startup.page", true);
pref("services.sync.prefs.sync.browser.tabs.autoHide", true);

Просмотреть файл

@ -221,8 +221,9 @@ var gEditItemOverlay = {
this._allTags = this._getCommonTags();
this._initTextField("tagsField", this._allTags.join(", "), false);
this._element("itemsCountText").value =
PlacesUIUtils.getFormattedString("detailsPane.multipleItems",
[this._itemIds.length]);
PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
this._itemIds.length,
[this._itemIds.length]);
}
// tags selector

Просмотреть файл

@ -713,8 +713,8 @@ var PlacesOrganizer = {
var itemsCountLabel = document.getElementById("itemsCountText");
selectItemDesc.hidden = false;
itemsCountLabel.value =
PlacesUIUtils.getFormattedString("detailsPane.multipleItems",
[aNodeList.length]);
PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
aNodeList.length, [aNodeList.length]);
infoBox.hidden = true;
return;
}
@ -743,13 +743,9 @@ var PlacesOrganizer = {
}
else {
selectItemDesc.hidden = false;
if (rowCount == 1)
itemsCountLabel.value = PlacesUIUtils.getString("detailsPane.oneItem");
else {
itemsCountLabel.value =
PlacesUIUtils.getFormattedString("detailsPane.multipleItems",
[rowCount]);
}
itemsCountLabel.value =
PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
rowCount, [rowCount]);
}
}
},

Просмотреть файл

@ -50,6 +50,9 @@ var Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
Cu.import("resource://gre/modules/PlacesUtils.jsm");
return PlacesUtils;
@ -79,6 +82,31 @@ var PlacesUIUtils = {
return bundle.formatStringFromName(key, params, params.length);
},
/**
* Get a localized plural string for the specified key name and numeric value
* substituting parameters.
*
* @param aKey
* String, key for looking up the localized string in the bundle
* @param aNumber
* Number based on which the final localized form is looked up
* @param aParams
* Array whose items will substitute #1, #2,... #n parameters
* in the string.
*
* @see https://developer.mozilla.org/en/Localization_and_Plurals
* @return The localized plural string.
*/
getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) {
let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey));
// Replace #1 with aParams[0], #2 with aParams[1], and so on.
return str.replace(/\#(\d+)/g, function (matchedId, matchedNumber) {
let param = aParams[parseInt(matchedNumber, 10) - 1];
return param !== undefined ? param : matchedId;
});
},
getString: function PUIU_getString(key) {
return bundle.GetStringFromName(key);
},

Просмотреть файл

@ -50,7 +50,7 @@ var gMainPane = {
// set up the "use current page" label-changing listener
this._updateUseCurrentButton();
window.addEventListener("focus", this._updateUseCurrentButton, false);
window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
this.updateBrowserStartupLastSession();
this.startupPagePrefChanged();
@ -128,23 +128,13 @@ var gMainPane = {
*/
setHomePageToCurrent: function ()
{
var win;
if (document.documentElement.instantApply) {
// If we're in instant-apply mode, use the most recent browser window
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
win = wm.getMostRecentWindow("navigator:browser");
}
else
win = window.opener;
let homePage = document.getElementById("browser.startup.homepage");
let tabs = this._getTabsForHomePage();
function getTabURI(t) t.linkedBrowser.currentURI.spec;
if (win) {
var homePage = document.getElementById("browser.startup.homepage");
var tabs = win.gBrowser.visibleTabs;
function getTabURI(t) t.linkedBrowser.currentURI.spec;
// FIXME Bug 244192: using dangerous "|" joiner!
// FIXME Bug 244192: using dangerous "|" joiner!
if (tabs.length)
homePage.value = tabs.map(getTabURI).join("|");
}
},
/**
@ -170,10 +160,26 @@ var gMainPane = {
* forms.
*/
_updateUseCurrentButton: function () {
var useCurrent = document.getElementById("useCurrent");
let useCurrent = document.getElementById("useCurrent");
var windowIsPresent;
let tabs = this._getTabsForHomePage();
if (tabs.length > 1)
useCurrent.label = useCurrent.getAttribute("label2");
else
useCurrent.label = useCurrent.getAttribute("label1");
// In this case, the button's disabled state is set by preferences.xml.
if (document.getElementById
("pref.browser.homepage.disable_button.current_page").locked)
return;
useCurrent.disabled = !tabs.length
},
_getTabsForHomePage: function ()
{
var win;
var tabs = [];
if (document.documentElement.instantApply) {
const Cc = Components.classes, Ci = Components.interfaces;
// If we're in instant-apply mode, use the most recent browser window
@ -181,30 +187,17 @@ var gMainPane = {
.getService(Ci.nsIWindowMediator);
win = wm.getMostRecentWindow("navigator:browser");
}
else
else {
win = window.opener;
}
if (win && win.document.documentElement
.getAttribute("windowtype") == "navigator:browser") {
windowIsPresent = true;
var tabbrowser = win.document.getElementById("content");
if (tabbrowser.browsers.length > 1)
useCurrent.label = useCurrent.getAttribute("label2");
else
useCurrent.label = useCurrent.getAttribute("label1");
}
else {
windowIsPresent = false;
useCurrent.label = useCurrent.getAttribute("label1");
// We should only include visible & non-pinned tabs
tabs = win.gBrowser.visibleTabs.slice(win.gBrowser._numPinnedTabs);
}
// In this case, the button's disabled state is set by preferences.xml.
if (document.getElementById
("pref.browser.homepage.disable_button.current_page").locked)
return;
useCurrent.disabled = !windowIsPresent;
return tabs;
},
/**

Просмотреть файл

@ -38,11 +38,19 @@ function test() {
function windowObserver(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function() {
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
if (win.Scratchpad) {
let state = win.Scratchpad.getState();
win.close();
addState(state);
win.Scratchpad.addObserver({
onReady: function() {
win.Scratchpad.removeObserver(this);
let state = win.Scratchpad.getState();
win.close();
addState(state);
},
});
}
}, false);
}
@ -56,4 +64,4 @@ function statesMatch(restored, states) {
state.executionContext == restoredState.executionContext;
})
});
}
}

Просмотреть файл

@ -2,7 +2,7 @@ browser.jar:
* content/browser/inspector.html (highlighter/inspector.html)
content/browser/NetworkPanel.xhtml (webconsole/NetworkPanel.xhtml)
* content/browser/scratchpad.xul (scratchpad/scratchpad.xul)
* content/browser/scratchpad.js (scratchpad/scratchpad.js)
content/browser/scratchpad.js (scratchpad/scratchpad.js)
* content/browser/styleeditor.xul (styleeditor/styleeditor.xul)
content/browser/splitview.css (styleeditor/splitview.css)
content/browser/styleeditor.css (styleeditor/styleeditor.css)

Просмотреть файл

@ -104,7 +104,7 @@ var ScratchpadManager = {
let enumerator = Services.wm.getEnumerator("devtools:scratchpad");
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
if (!win.closed) {
if (!win.closed && win.Scratchpad.initialized) {
this._scratchpads.push(win.Scratchpad.getState());
}
}

Просмотреть файл

@ -86,6 +86,13 @@ var Scratchpad = {
*/
executionContext: SCRATCHPAD_CONTEXT_CONTENT,
/**
* Tells if this Scratchpad is initialized and ready for use.
* @boolean
* @see addObserver
*/
initialized: false,
/**
* Retrieve the xul:notificationbox DOM element. It notifies the user when
* the current code execution context is SCRATCHPAD_CONTEXT_BROWSER.
@ -733,7 +740,7 @@ var Scratchpad = {
},
/**
* The Scratchpad window DOMContentLoaded event handler. This method
* The Scratchpad window load event handler. This method
* initializes the Scratchpad window and source editor.
*
* @param nsIDOMEvent aEvent
@ -784,7 +791,9 @@ var Scratchpad = {
this.onContextMenu);
this.editor.focus();
this.editor.setCaretOffset(this.editor.getCharCount());
this.initialized = true;
if (this.filename && !this.saved) {
this.onTextChanged();
}
@ -866,7 +875,7 @@ var Scratchpad = {
if (aStatus && !Components.isSuccessCode(aStatus)) {
return;
}
if (!document) {
if (!document || !this.initialized) {
return; // file saved to disk after window has closed
}
document.title = document.title.replace(/^\*/, "");
@ -904,6 +913,7 @@ var Scratchpad = {
this.onContextMenu);
this.editor.destroy();
this.editor = null;
this.initialized = false;
},
/**
@ -1033,6 +1043,6 @@ XPCOMUtils.defineLazyGetter(Scratchpad, "strings", function () {
return Services.strings.createBundle(SCRATCHPAD_L10N);
});
addEventListener("DOMContentLoaded", Scratchpad.onLoad.bind(Scratchpad), false);
addEventListener("load", Scratchpad.onLoad.bind(Scratchpad), false);
addEventListener("unload", Scratchpad.onUnload.bind(Scratchpad), false);
addEventListener("close", Scratchpad.onClose.bind(Scratchpad), false);

Просмотреть файл

@ -2,24 +2,21 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let gOldPref;
let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
function test()
{
waitForExplicitFinish();
gOldPref = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, true);
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
ok(Scratchpad, "Scratchpad variable exists");
ok(window.Scratchpad, "Scratchpad variable exists");
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,Scratchpad test for bug 646070 - chrome context preference";
@ -27,8 +24,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
let sp = gScratchpadWindow.Scratchpad;
ok(sp, "Scratchpad object exists in new window");
@ -50,7 +45,7 @@ function runTests()
ok(!chromeContextCommand.hasAttribute("disabled"),
"Chrome context command is disabled");
Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, gOldPref);
Services.prefs.clearUserPref(DEVTOOLS_CHROME_ENABLED);
finish();
}

Просмотреть файл

@ -17,10 +17,10 @@ function done()
}
}
var ScratchpadManager = Scratchpad.ScratchpadManager;
var gFile;
var oldPrompt = Services.prompt;
var promptButton = -1;
function test()
{
@ -29,6 +29,12 @@ function test()
gFile = createTempFile("fileForBug653427.tmp");
writeFile(gFile, "text", testUnsaved.call(this));
Services.prompt = {
confirmEx: function() {
return promptButton;
}
};
testNew();
testSavedFile();
@ -37,28 +43,23 @@ function test()
function testNew()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function() {
openScratchpad(function(win) {
win.Scratchpad.close();
ok(win.closed, "new scratchpad window should close without prompting")
done();
});
}, {noFocus: true});
}
function testSavedFile()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function() {
openScratchpad(function(win) {
win.Scratchpad.filename = "test.js";
win.Scratchpad.saved = true;
win.Scratchpad.close();
ok(win.closed, "scratchpad from file with no changes should close")
done();
});
}, {noFocus: true});
}
function testUnsaved()
@ -70,17 +71,11 @@ function testUnsaved()
function testUnsavedFileCancel()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function() {
openScratchpad(function(win) {
win.Scratchpad.filename = "test.js";
win.Scratchpad.saved = false;
Services.prompt = {
confirmEx: function() {
return win.BUTTON_POSITION_CANCEL;
}
}
promptButton = win.BUTTON_POSITION_CANCEL;
win.Scratchpad.close();
@ -88,14 +83,12 @@ function testUnsavedFileCancel()
win.close();
done();
});
}, {noFocus: true});
}
function testUnsavedFileSave()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function() {
openScratchpad(function(win) {
win.Scratchpad.importFromFile(gFile, true, function(status, content) {
win.Scratchpad.filename = gFile.path;
win.Scratchpad.onTextSaved();
@ -103,11 +96,7 @@ function testUnsavedFileSave()
let text = "new text";
win.Scratchpad.setText(text);
Services.prompt = {
confirmEx: function() {
return win.BUTTON_POSITION_SAVE;
}
}
promptButton = win.BUTTON_POSITION_SAVE;
win.Scratchpad.close(function() {
readFile(gFile, function(savedContent) {
@ -118,28 +107,22 @@ function testUnsavedFileSave()
ok(win.closed, 'pressing "Save" in dialog should close scratchpad');
});
});
}, {noFocus: true});
}
function testUnsavedFileDontSave()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function() {
openScratchpad(function(win) {
win.Scratchpad.filename = gFile.path;
win.Scratchpad.saved = false;
Services.prompt = {
confirmEx: function() {
return win.BUTTON_POSITION_DONT_SAVE;
}
}
promptButton = win.BUTTON_POSITION_DONT_SAVE;
win.Scratchpad.close();
ok(win.closed, 'pressing "Don\'t Save" in dialog should close scratchpad');
done();
});
}, {noFocus: true});
}
function cleanup()

Просмотреть файл

@ -2,8 +2,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var ScratchpadManager = Scratchpad.ScratchpadManager;
function test()
{
waitForExplicitFinish();
@ -16,14 +14,7 @@ function test()
Services.prefs.setIntPref("devtools.editor.tabsize", 5);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
gScratchpadWindow.Scratchpad.addObserver({
onReady: runTests
});
}, false);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,Scratchpad test for the Tab key, bug 660560";
@ -34,9 +25,6 @@ function runTests()
let sp = gScratchpadWindow.Scratchpad;
ok(sp, "Scratchpad object exists in new window");
is(this.onReady, runTests, "the handler runs in the context of the observer");
sp.removeObserver(this);
ok(sp.editor.hasFocus(), "the editor has focus");
sp.setText("window.foo;");
@ -70,19 +58,12 @@ function runTests()
Services.prefs.setIntPref("devtools.editor.tabsize", 6);
Services.prefs.setBoolPref("devtools.editor.expandtab", false);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
gScratchpadWindow.Scratchpad.addObserver({
onReady: runTests2
});
}, false);
openScratchpad(runTests2);
}
function runTests2()
{
let sp = gScratchpadWindow.Scratchpad;
sp.removeObserver(this);
sp.setText("window.foo;");
sp.editor.setCaretOffset(0);

Просмотреть файл

@ -30,57 +30,37 @@ function test()
function testListeners()
{
let win = ScratchpadManager.openScratchpad();
openScratchpad(function(aWin, aScratchpad) {
aScratchpad.setText("new text");
ok(!isStar(aWin), "no star if scratchpad isn't from a file");
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
aScratchpad.onTextSaved();
ok(!isStar(aWin), "no star before changing text");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
aScratchpad.setText("new text2");
ok(isStar(aWin), "shows star if scratchpad text changes");
aScratchpad.setText("new text");
ok(!isStar(win), "no star if scratchpad isn't from a file");
aScratchpad.onTextSaved();
ok(!isStar(aWin), "no star if scratchpad was just saved");
aScratchpad.onTextSaved();
ok(!isStar(win), "no star before changing text");
aScratchpad.undo();
ok(isStar(aWin), "star if scratchpad undo");
aScratchpad.setText("new text2");
ok(isStar(win), "shows star if scratchpad text changes");
aScratchpad.onTextSaved();
ok(!isStar(win), "no star if scratchpad was just saved");
aScratchpad.undo();
ok(isStar(win), "star if scratchpad undo");
win.close();
done();
}
});
}, false);
aWin.close();
done();
}, {noFocus: true});
}
function testErrorStatus()
{
let win = ScratchpadManager.openScratchpad();
openScratchpad(function(aWin, aScratchpad) {
aScratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
aScratchpad.setText("new text");
ok(!isStar(aWin), "no star if file save failed");
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
aScratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
aScratchpad.setText("new text");
ok(!isStar(win), "no star if file save failed");
win.close();
done();
}
});
}, false);
aWin.close();
done();
}, {noFocus: true});
}
@ -92,21 +72,13 @@ function testRestoreNotFromFile()
}];
let [win] = ScratchpadManager.restoreSession(session);
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
openScratchpad(function(aWin, aScratchpad) {
aScratchpad.setText("new text");
ok(!isStar(win), "no star if restored scratchpad isn't from a file");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
aScratchpad.setText("new text");
ok(!isStar(win), "no star if restored scratchpad isn't from a file");
win.close();
done();
}
});
}, false);
win.close();
done();
}, {window: win, noFocus: true});
}
function testRestoreFromFileSaved()
@ -119,23 +91,15 @@ function testRestoreFromFileSaved()
}];
let [win] = ScratchpadManager.restoreSession(session);
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
openScratchpad(function(aWin, aScratchpad) {
ok(!isStar(win), "no star before changing text in scratchpad restored from file");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
aScratchpad.setText("new text");
ok(isStar(win), "star when text changed from scratchpad restored from file");
ok(!isStar(win), "no star before changing text in scratchpad restored from file");
aScratchpad.setText("new text");
ok(isStar(win), "star when text changed from scratchpad restored from file");
win.close();
done();
}
});
}, false);
win.close();
done();
}, {window: win, noFocus: true});
}
function testRestoreFromFileUnsaved()
@ -148,20 +112,12 @@ function testRestoreFromFileUnsaved()
}];
let [win] = ScratchpadManager.restoreSession(session);
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
openScratchpad(function() {
ok(isStar(win), "star with scratchpad restored with unsaved text");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
ok(isStar(win), "star with scratchpad restored with unsaved text");
win.close();
done();
}
});
}, false);
win.close();
done();
}, {window: win, noFocus: true});
}
function isStar(win)

Просмотреть файл

@ -2,28 +2,21 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", testFalsy, false);
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
openScratchpad(testFalsy);
}, true);
content.location = "data:text/html,<p>test falsy display() values in Scratchpad";
}
function testFalsy(sp)
function testFalsy()
{
gScratchpadWindow.removeEventListener("load", testFalsy, false);
let sp = gScratchpadWindow.Scratchpad;
verifyFalsies(sp);

Просмотреть файл

@ -13,9 +13,7 @@ function test()
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,test Edit menu updates Scratchpad - bug 699130";
@ -23,8 +21,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", runTests, false);
let sp = gScratchpadWindow.Scratchpad;
let doc = gScratchpadWindow.document;
let winUtils = gScratchpadWindow.QueryInterface(Ci.nsIInterfaceRequestor).

Просмотреть файл

@ -2,19 +2,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,test context switch in Scratchpad";
@ -22,8 +17,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
let sp = gScratchpadWindow.Scratchpad;
let contentMenu = gScratchpadWindow.document.getElementById("sp-menu-content");
@ -42,7 +35,7 @@ function runTests()
is(contentMenu.getAttribute("checked"), "true",
"content menuitem is checked");
ok(!chromeMenu.hasAttribute("checked"),
isnot(chromeMenu.getAttribute("checked"), "true",
"chrome menuitem is not checked");
ok(!notificationBox.currentNotification,
@ -66,7 +59,7 @@ function runTests()
is(chromeMenu.getAttribute("checked"), "true",
"chrome menuitem is checked");
ok(!contentMenu.hasAttribute("checked"),
isnot(contentMenu.getAttribute("checked"), "true",
"content menuitem is not checked");
ok(notificationBox.currentNotification,

Просмотреть файл

@ -7,11 +7,9 @@ function test()
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
gBrowser.selectedBrowser.addEventListener("load", function onTabLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onTabLoad, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,<p>test run() and display() in Scratchpad";
@ -19,8 +17,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
let sp = gScratchpadWindow.Scratchpad;
content.wrappedJSObject.foobarBug636725 = 1;

Просмотреть файл

@ -5,9 +5,6 @@
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
// Reference to the Scratchpad object.
let gScratchpad;
@ -22,11 +19,9 @@ function test()
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,<p>test file open and save in Scratchpad";
@ -34,8 +29,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
gScratchpad = gScratchpadWindow.Scratchpad;
// Create a temporary file.

Просмотреть файл

@ -2,21 +2,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
ok(Scratchpad, "Scratchpad variable exists");
ok(window.Scratchpad, "Scratchpad variable exists");
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,initialization test for Scratchpad";
@ -24,8 +20,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
let sp = gScratchpadWindow.Scratchpad;
ok(sp, "Scratchpad object exists in new window");
is(typeof sp.run, "function", "Scratchpad.run() exists");

Просмотреть файл

@ -2,19 +2,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,<title>foobarBug636725</title>" +
@ -23,8 +18,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
let sp = gScratchpadWindow.Scratchpad;
sp.setText("document");
@ -34,8 +27,8 @@ function runTests()
let propPanel = document.querySelector(".scratchpad_propertyPanel");
ok(propPanel, "property panel is open");
propPanel.addEventListener("popupshown", function() {
propPanel.removeEventListener("popupshown", arguments.callee, false);
propPanel.addEventListener("popupshown", function onPopupShown() {
propPanel.removeEventListener("popupshown", onPopupShown, false);
let tree = propPanel.querySelector("tree");
ok(tree, "property panel tree found");

Просмотреть файл

@ -2,8 +2,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var ScratchpadManager = Scratchpad.ScratchpadManager;
// only finish() when correct number of tests are done
const expected = 3;
var count = 0;
@ -15,7 +13,6 @@ function done()
}
}
function test()
{
waitForExplicitFinish();
@ -26,11 +23,7 @@ function test()
function testOpen()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
openScratchpad(function(win) {
is(win.Scratchpad.filename, undefined, "Default filename is undefined");
is(win.Scratchpad.getText(),
win.Scratchpad.strings.GetStringFromName("scratchpadIntro"),
@ -40,7 +33,7 @@ function testOpen()
win.close();
done();
}, false);
}, {noFocus: true});
}
function testOpenWithState()
@ -51,25 +44,19 @@ function testOpenWithState()
text: "test text"
};
let win = ScratchpadManager.openScratchpad(state);
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
openScratchpad(function(win) {
is(win.Scratchpad.filename, state.filename, "Filename loaded from state");
is(win.Scratchpad.executionContext, state.executionContext, "Execution context loaded from state");
is(win.Scratchpad.getText(), state.text, "Content loaded from state");
win.close();
done();
}, false);
}, {state: state, noFocus: true});
}
function testOpenInvalidState()
{
let state = 7;
let win = ScratchpadManager.openScratchpad(state);
let win = openScratchpad(null, {state: 7});
ok(!win, "no scratchpad opened if state is not an object");
done();
}

Просмотреть файл

@ -48,11 +48,7 @@ function testRestore()
asyncMap(states, function(state, done) {
// Open some scratchpad windows
let win = ScratchpadManager.openScratchpad(state);
win.addEventListener("load", function onScratchpadLoad() {
removeEventListener("load", onScratchpadLoad, false);
done(win);
}, false)
openScratchpad(done, {state: state, noFocus: true});
}, function(wins) {
// Then save the windows to session store
ScratchpadManager.saveOpenWindows();
@ -74,12 +70,11 @@ function testRestore()
is(restoredWins.length, 3, "Three scratchad windows restored");
asyncMap(restoredWins, function(restoredWin, done) {
restoredWin.addEventListener("load", function onScratchpadLoad() {
restoredWin.removeEventListener("load", onScratchpadLoad, false);
let state = restoredWin.Scratchpad.getState();
restoredWin.close();
openScratchpad(function(aWin) {
let state = aWin.Scratchpad.getState();
aWin.close();
done(state);
}, false);
}, {window: restoredWin, noFocus: true});
}, function(restoredStates) {
// Then make sure they were restored with the right states
ok(statesMatch(restoredStates, states),

Просмотреть файл

@ -2,8 +2,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
let tab1;
let tab2;
let sp;
@ -14,15 +12,14 @@ function test()
tab1 = gBrowser.addTab();
gBrowser.selectedTab = tab1;
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.selectedBrowser.addEventListener("load", function onLoad1() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad1, true);
tab2 = gBrowser.addTab();
gBrowser.selectedTab = tab2;
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
gBrowser.selectedBrowser.addEventListener("load", function onLoad2() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad2, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,test context switch in Scratchpad tab 2";
}, true);
@ -32,8 +29,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", runTests, true);
sp = gScratchpadWindow.Scratchpad;
let contentMenu = gScratchpadWindow.document.getElementById("sp-menu-content");
@ -52,7 +47,7 @@ function runTests()
is(contentMenu.getAttribute("checked"), "true",
"content menuitem is checked");
ok(!browserMenu.hasAttribute("checked"),
isnot(browserMenu.getAttribute("checked"), "true",
"chrome menuitem is not checked");
is(notificationBox.currentNotification, null,

Просмотреть файл

@ -2,19 +2,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
openScratchpad(runTests);
}, true);
content.location = "data:text/html,<title>foobarBug636725</title>" +
@ -23,8 +18,6 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
let sp = gScratchpadWindow.Scratchpad;
let doc = gScratchpadWindow.document;

Просмотреть файл

@ -6,6 +6,61 @@
let gScratchpadWindow; // Reference to the Scratchpad chrome window object
/**
* Open a Scratchpad window.
*
* @param function aReadyCallback
* Optional. The function you want invoked when the Scratchpad instance
* is ready.
* @param object aOptions
* Optional. Options for opening the scratchpad:
* - window
* Provide this if there's already a Scratchpad window you want to wait
* loading for.
* - state
* Scratchpad state object. This is used when Scratchpad is open.
* - noFocus
* Boolean that tells you do not want the opened window to receive
* focus.
* @return nsIDOMWindow
* The new window object that holds Scratchpad. Note that the
* gScratchpadWindow global is also updated to reference the new window
* object.
*/
function openScratchpad(aReadyCallback, aOptions)
{
aOptions = aOptions || {};
let win = aOptions.window ||
Scratchpad.ScratchpadManager.openScratchpad(aOptions.state);
if (!win) {
return;
}
let onLoad = function() {
win.removeEventListener("load", onLoad, false);
win.Scratchpad.addObserver({
onReady: function(aScratchpad) {
aScratchpad.removeObserver(this);
if (aOptions.noFocus) {
aReadyCallback(win, aScratchpad);
} else {
waitForFocus(aReadyCallback.bind(null, win, aScratchpad), win);
}
}
});
};
if (aReadyCallback) {
win.addEventListener("load", onLoad, false);
}
gScratchpadWindow = win;
return gScratchpadWindow;
}
function cleanup()
{
if (gScratchpadWindow) {

Просмотреть файл

@ -45,12 +45,17 @@ var js_src = copy.createDataObject();
copy({
source: [
ORION_EDITOR + "/orion/textview/global.js",
ORION_EDITOR + "/orion/textview/eventTarget.js",
ORION_EDITOR + "/orion/editor/regex.js",
ORION_EDITOR + "/orion/textview/keyBinding.js",
ORION_EDITOR + "/orion/textview/rulers.js",
ORION_EDITOR + "/orion/textview/undoStack.js",
ORION_EDITOR + "/orion/textview/textModel.js",
ORION_EDITOR + "/orion/textview/annotations.js",
ORION_EDITOR + "/orion/textview/tooltip.js",
ORION_EDITOR + "/orion/textview/textView.js",
ORION_EDITOR + "/orion/textview/textDND.js",
ORION_EDITOR + "/orion/editor/htmlGrammar.js",
ORION_EDITOR + "/orion/editor/textMateStyler.js",
ORION_EDITOR + "/examples/textview/textStyler.js",
@ -69,6 +74,7 @@ copy({
source: [
ORION_EDITOR + "/orion/textview/textview.css",
ORION_EDITOR + "/orion/textview/rulers.css",
ORION_EDITOR + "/orion/textview/annotations.css",
ORION_EDITOR + "/examples/textview/textstyler.css",
ORION_EDITOR + "/examples/editor/htmlStyles.css",
],

Просмотреть файл

@ -8,25 +8,11 @@ The Orion editor web site: http://www.eclipse.org/orion
To upgrade Orion to a newer version see the UPGRADE file.
Orion version: git clone from 2011-10-26
commit hash 0ab295660e1f7d33ca2bfb8558b3b7492d2c5aa5
+ patch for Eclipse Bug 358623 - Drag and Drop support:
https://github.com/mihaisucan/orion.client/tree/bug-358623
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=358623
+ patch for Eclipse Bug 362286 - Monaco font line height:
https://github.com/mihaisucan/orion.client/tree/bug-362286
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362286
+ patch for Eclipse Bug 362107 - Ctrl-Up/Down failure on Linux:
https://github.com/mihaisucan/orion.client/tree/bug-362107
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362107
+ patch for Eclipse Bug 362428 - _getXToOffset() throws:
https://github.com/mihaisucan/orion.client/tree/bug-362428
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362428
+ patch for Eclipse Bug 362835 - Pasted HTML shows twice:
https://github.com/mihaisucan/orion.client/tree/bug-362835
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362835
+ patch for Eclipse Bug 363508 - Selection is broken after TextView hide/unhide
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=363508
Orion version: git clone from 2011-12-09
commit hash d8a6dc01d9c561d6eb99f03b64c8c78ce785c59d
+ patch for Eclipse Bug 366312 - right-clicking outside of the selection causes the caret to move
https://github.com/mihaisucan/orion.client/tree/bug-366312
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=366312
# License

Просмотреть файл

@ -2,15 +2,124 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
.viewContainer {
font-size: inherit; /* inherit browser's default monospace font size */
background: -moz-Dialog;
font-family: monospace;
font-size: inherit; /* inherit browser's default monospace font size */
}
.view {
background: #fff;
}
.readonly > .view {
background: #f6f6f6;
}
/* Styles for rulers */
.ruler {
background-color: white;
}
.ruler.lines {
border-right: 1px solid lightgray;
text-align: right;
}
/* Styles for the line number ruler */
.rulerLines {
background: -moz-Dialog;
color: -moz-DialogText;
min-width: 1.4em;
padding-left: 4px;
padding-right: 4px;
text-align: end;
background: -moz-Dialog;
color: -moz-DialogText;
min-width: 1.4em;
padding-left: 4px;
padding-right: 4px;
text-align: end;
}
.token_singleline_comment {
color: green;
}
.token_multiline_comment {
color: green;
}
.token_doc_comment {
color: #00008F;
}
.token_doc_html_markup {
color: #7F7F9F;
}
.token_doc_tag {
color: #7F9FBF;
}
.token_task_tag {
color: #7F9FBF;
}
.token_string {
color: blue;
}
.token_keyword {
color: darkred;
font-weight: bold;
}
.token_space {
/* images/white_space.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAABnRSTlMA/wAAAACkwsAdAAAAIUlEQVR4nGP4z8CAC+GUIEXuABhgkTuABEiRw2cmae4EAH05X7xDolNRAAAAAElFTkSuQmCC");
background-repeat: no-repeat;
background-position: center center;
}
.token_tab {
/* images/white_tab.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAJCAIAAACJ2loDAAAABnRSTlMA/wD/AP83WBt9AAAAMklEQVR4nGP4TwRgoK6i52c3bz5w6zMSA6tJn28d2Lx589nnCAYu63AaSLxJRLoJPwAAeNk0aG4opfMAAAAASUVORK5CYII=");
background-repeat: no-repeat;
background-position: left center;
}
.line_caret {
background: #EAF2FE;
}
.readonly .line_caret {
background: #fcfcfc;
}
/* Styling for html syntax highlighting */
.entity-name-tag {
color: #3f7f7f;
}
.entity-other-attribute-name {
color: #7f007f;
}
.punctuation-definition-comment {
color: #3f5fbf;
}
.comment {
color: #3f5fbf
}
.string-quoted {
color: #2a00ff;
font-style: italic;
}
.invalid {
color: red;
font-weight: bold;
}
.annotationRange.currentBracket {
}
.annotationRange.matchingBracket {
outline: 1px solid red;
}

Просмотреть файл

@ -3,9 +3,13 @@
}
.viewContainer {
background-color: #eeeeee;
font-family: monospace;
font-size: 10pt;
}
::-webkit-scrollbar-corner {
background-color: #eeeeee;
}
.viewContent {
}/* Styles for rulers */
@ -25,16 +29,172 @@
text-align: right;
}
.ruler.overview {
border-left: 1px solid lightgray;
width: 14px;
}
/* Styles for the line number ruler */
.rulerLines {
background-color: white;
}
.rulerLines.even
.rulerLines.odd {
}.token_singleline_comment {
}/* Styles for the annotation ruler (all lines) */
.annotation {
}
.annotation.error,
.annotation.warning
.annotation.task,
.annotation.bookmark,
.annotation.breakpoint,
.annotation.collapsed
.annotation.expanded {
}
/* Styles for the annotation ruler (first line) */
.annotationHTML {
cursor: pointer;
width: 16px;
height: 16px;
display: inline-block;
vertical-align: middle;
background-position: center;
background-repeat: no-repeat;
}
.annotationHTML.error {
/* images/error.gif */
background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs=");
}
.annotationHTML.warning {
/* images/warning.gif */
background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw==");
}
.annotationHTML.task {
/* images/task.gif */
background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQAAN7s4uTy6ICvY423c2WdP2ugR3mqWYeza2ejOl6VNVqPM1aJMURsJ2GaOnKlT8PbsbPDqGmmO1OCLk98LEhxKGWfOWKaN0t2KkJoJf///////wAAAAAAAAAAAAAAAAAAACH5BAEAABoALAAAAAAQABAAAAVmoCaOZDk+UaquDxkNcCxHJHLceI6QleD/vkCmQrIYjkiDMGAhJRzQ6NKRICkKgYJ2qVWQFktCmEBYkCSNZSbQaDckpAl5TCZMSBdtAaDXX0gUUYJRFCQMSYgGDCQQGI6PkBAmkyUhADs=");
}
.annotationHTML.bookmark {
/* images/bookmark.gif */
background-image: url("data:image/gif;base64,R0lGODlhEAAQALMAAP7//+/VNPzZS/vifeumAPrBOOSlHOSuRP///wAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAgALAAAAAAQABAAAARLEMlJq5Xn3EvIrkenfRIhCB5pmKhRdbAJAGhssuc8n6eJoAKdkOaTAIdEQeWoA1oGsiZhYAnIcqiApVPjElyUbkFSgCkn5XElLYkAADs=");
}
.annotationHTML.breakpoint {
/* images/breakpoint.gif */
background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAFheoFxkoFxnpmt0pmZxpnF7rYyWwmJwpnaFs3aDrWt8rXGBrYycwmZ3mXuNs42cu77F03GIs3aJrYGVu2J5oKCuxeDj6LK/03GLrYieu3aIoIygu6m4zcLN3MTM1m6Rs2aLriRgkSZilXGXtoGcs7LD0QBLhSZikihol3ScubrO2Yaqu5q4xpO0wpm7yabF0ZO9yaXI0r3X3tHj6P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAADQALAAAAAAQABAAAAafQJpwSCwWLYZBIDAwWIw0A+FFpW6aRUPCxe1yE4ahhdCCxWSzmSwGgxGeUceKpUqhUCkVa7UK0wgkJCUjJoUmIyWBBEIEGhoeJ4YmJx6OAUIADQ0QIZIhEJoAQgEUFBUgkiAVpZdRCxIPFx8iIh8XDw4FfhYHDhgZHB0dHBkYEwdwUQoTEc3OEwp+QwYHCBMMDBMIB9JESAJLAk5Q5EVBADs=");
}
.annotationHTML.collapsed {
/* images/collapsed.png */
width: 14px;
height: 14px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWBJREFUeNpi/P//PwMlgImBQkCxASzoAp++fo+6de+Z+fXbD/Jev/nAICoiwKCpqrBBTUlqNR835zJ09YzIYfDxy7eo/cevLmXlYGNQUJAEahZieP3mHcODB08Zfv/4w+BoqR3Nz8O1DKcXzt94HPqXmZlBU1+LgZNfkMHazIOBA0hr6uswgMTP33gYijcMLlx/EMAnLs7w7sc/hg9AG0HgPZB+B8S84hJA+UcBeMPg+at3DJIMnAxZzt5wsUhnXzDdsmIVWB6vAcLCfAys3z4wzN64huEfkJ/uH8IwexOQDQymD2/fgeXxekFLRWHD51evGDhZGRi4WSFSnCwgNjB2Xr1m0AbK4zXAQkdhNdPf3wx3r91g+PruLcOqnasYvn54x3Dv2k0G5r+/GMyB8nijEQTefvoadeH6w9Cbtx8GvH//kUFQkJ9BQ1V+g76m/GphPu5lBA0YenmBYgMAAgwA34GIKjmLxOUAAAAASUVORK5CYII=");
}
.annotationHTML.expanded {
/* images/expanded.png */
width: 14px;
height: 14px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAT5JREFUeNrUksFKw0AURW+mTWw67SSEiG209U90r4jddFO34l+5U0HdZCHiFwiCOz9AlMSmGEpMOqk1TWJSFGyFbATR2dyZd+Dw3mOENE3xkyP8PYHrBT3OX7uW43ZefA6FUaw1dJPSyrmu1k8KBYOh37Od4XFZLEPXFdRrFMGIw3U9TKMYqw1tb0VjcxLy9eEF425CCIxWE5JcxSQGxCyNloG87gXhwWIHc4J767lTZQw8ShFGSZbxRyaQmZJxd3NRUJ6ffwQNEi6PzG/L2tjdmvFCgcKqKL2F2Olu43MzggDka+IjPuOFI7Sbujn2fUglYKkkzFIi+R0I/QDrGS8UqDX5QkhiOHYfE84hkhSTkGNgOyDJFCzjhYLTq+vDtrG8r1LZtB6fcHtzB+uhD5VWzLx+lvF/8JV/XfAuwADsrJbMGG4l4AAAAABJRU5ErkJggg==");
}
.annotationHTML.multiple {
/* images/multiple.gif */
background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAOdpa+yJiuFYXOFYXeBYXONwded8f+NwdmhwkHB4iPr7/ezx+fP2+2h4kOzy+Wh4iPr8/gCBwTaczjaXyjaYyjaXyTaYyfr8/QCMzQCMzACHxzao2jal2Dak1zag03iAgI/Ckn64fZrHmX+4fZLCianPopPCiarOoqbLlafLlbnXq7nWq6fLlMTcsoCIeJCQcIiIeKCYaJiQcO16ee16evGVlfGWlfahn/ahoPWhn/WhoPe1tP///////wAAAAAAACH5BAEAAD0ALAAAAAAQABAAAAaRwJ5wSCwaj8WYcslcDmObaDTGq1Zjzw4mk+FQIRcFTzaUeTRoj4zHaI+HL0lkLnnxFgsH7zWEWSoTFBMwVlUwQy6JMDCJjYwuQx8tk5MfOzk4OjcfkSssKCkqHzY0MzQ1nEIJJSYkJCcJAQCzAQlDDyIjISMiCQYEAgMGD0MNIMfHDQUHBc3EQgjR0tPSSNY9QQA7");
}
.annotationHTML.overlay {
/* images/plus.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII=");
background-position: right bottom;
position: relative;
top: -16px;
}
.annotationHTML.currentBracket {
/* images/currentBracket.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sLEBULCGQmEKAAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAnklEQVQ4y7VTsRHDIBATJg1HCUzAHEzFBExAzwZsRMkE9gifKhc72ODYibr/+xcnoQdugq0LAujEwmbn0UxQh4OxpjX1XgshwFqLnPM5PQTQGlprWpbl3RhJ/CSQUm7qPYLp7i8cEpRSoJT6ju0lIaVEQgiKMQ4lHHpQayVjzHWCn5jIOcc8z9dMBADvPZxz3SC1tzCI8vgWdvL+VzwB8JSj2GFTyxIAAAAASUVORK5CYII=");
}
.annotationHTML.matchingBracket {
/* images/matchingBracket.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sLEBUMAsuyb3kAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAoklEQVQ4y61TsQ3EIAw80DcI0USKGIApWIsB2IGGKbJPugxBR3VfvfRRCOSTvw7LPuPzGXgI8f0gwAsFu5rXIYMdDiEOIdnKW5YFzjnEGH+bhwA/KKVwmibu0BhRnpEZY1BrHTaVT7fQJZjnGeu63tOAJFNKVEox53yqQZfAWstt27oidgm01ve3UEqBaBjnspG89wgh3LiFgZXHt3Dh23/FGxKViehm0X85AAAAAElFTkSuQmCC");
}
.annotationHTML.currentLine {
/* images/currentLine.gif */
background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQAALxe0bNWzbdZzrlb0KpPx61RybBTy6VLxadNxZGctIeUroyYsG92hHyMqIKRq2l9nmyAoHGDonaIpStXj6q80k1aXf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABYALAAAAAAQABAAAAVCoCWOZGmeKDql5ppOMGXBk/zOoltSNO6XrlXwxIPNYiMGq8SoLC2MaNPygEQkDYdikUg6LQcEoWAICAaA5HPNLoUAADs=");
}
/* Styles for the overview ruler */
.annotationOverview {
cursor: pointer;
border-radius: 2px;
left: 2px;
width: 8px;
}
.annotationOverview.task {
background-color: lightgreen;
border: 1px solid green;
}
.annotationOverview.breakpoint {
background-color: lightblue;
border: 1px solid blue;
}
.annotationOverview.bookmark {
background-color: yellow;
border: 1px solid orange;
}
.annotationOverview.error {
background-color: lightcoral;
border: 1px solid darkred;
}
.annotationOverview.warning {
background-color: Gold;
border: 1px solid black;
}
.annotationOverview.currentBracket {
background-color: lightgray;
border: 1px solid red;
}
.annotationOverview.matchingBracket {
background-color: lightgray;
border: 1px solid red;
}
.annotationOverview.currentLine {
background-color: #EAF2FE;
border: 1px solid black;
}
/* Styles for text range */
.annotationRange {
background-repeat: repeat-x;
background-position: left bottom;
}
.annotationRange.task {
/* images/squiggly_task.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sLDhEoIrb7JmcAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAGUlEQVQI12NggIH/DGdhDCM45z/DfyiBAADgdQjGhI/4DAAAAABJRU5ErkJggg==");
}
.annotationRange.breakpoint {
/* images/squiggly_breakpoint.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sLDhEqHTKradgAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAIklEQVQI11XJMQ0AMAzAMGMafwrFlD19+sUKIJTFo9k+B/kQ+Qr2bIVKOgAAAABJRU5ErkJggg==");
}
.annotationRange.bookmark {
/* images/squiggly_bookmark.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
}
.annotationRange.error {
/* images/squiggly_error.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==");
}
.annotationRange.warning {
/* images/squiggly_warning.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
}
.annotationRange.currentBracket {
}
.annotationRange.matchingBracket {
outline: 1px solid red;
}
/* Styles for lines of text */
.annotationLine {
}
.annotationLine.currentLine {
background-color: #EAF2FE;
}
.token_singleline_comment {
color: green;
}
@ -67,15 +227,6 @@
font-weight: bold;
}
.token_bracket_outline {
outline: 1px solid red;
}
.token_bracket {
color: white;
background-color: grey;
}
.token_space {
/* images/white_space.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAABnRSTlMA/wAAAACkwsAdAAAAIUlEQVR4nGP4z8CAC+GUIEXuABhgkTuABEiRw2cmae4EAH05X7xDolNRAAAAAElFTkSuQmCC");

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -42,6 +42,11 @@ const Cu = Components.utils;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1",
"nsIClipboardHelper");
const ORION_SCRIPT = "chrome://browser/content/orion.js";
const ORION_IFRAME = "data:text/html;charset=utf8,<!DOCTYPE html>" +
@ -57,8 +62,7 @@ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
* SourceEditor.THEMES to Orion CSS files.
*/
const ORION_THEMES = {
textmate: ["chrome://browser/content/orion.css",
"chrome://browser/content/orion-mozilla.css"],
mozilla: ["chrome://browser/content/orion-mozilla.css"],
};
/**
@ -71,6 +75,13 @@ const ORION_EVENTS = {
Selection: "Selection",
};
/**
* Known Orion annotation types.
*/
const ORION_ANNOTATION_TYPES = {
currentBracket: "orion.annotation.currentBracket",
matchingBracket: "orion.annotation.matchingBracket",
};
var EXPORTED_SYMBOLS = ["SourceEditor"];
@ -90,17 +101,24 @@ function SourceEditor() {
Services.prefs.getIntPref(SourceEditor.PREFS.TAB_SIZE);
SourceEditor.DEFAULTS.EXPAND_TAB =
Services.prefs.getBoolPref(SourceEditor.PREFS.EXPAND_TAB);
this._onOrionSelection = this._onOrionSelection.bind(this);
}
SourceEditor.prototype = {
_view: null,
_iframe: null,
_model: null,
_undoStack: null,
_lines_ruler: null,
_linesRuler: null,
_styler: null,
_annotationStyler: null,
_annotationModel: null,
_dragAndDrop: null,
_mode: null,
_expandTab: null,
_tabSize: null,
_iframeWindow: null,
/**
* The editor container element.
@ -152,11 +170,7 @@ SourceEditor.prototype = {
let onIframeLoad = (function() {
this._iframe.removeEventListener("load", onIframeLoad, true);
Services.scriptloader.loadSubScript(ORION_SCRIPT,
this._iframe.contentWindow.wrappedJSObject, "utf8");
this._onLoad(aCallback);
this._onIframeLoad();
}).bind(this);
this._iframe.addEventListener("load", onIframeLoad, true);
@ -166,20 +180,23 @@ SourceEditor.prototype = {
aElement.appendChild(this._iframe);
this.parentElement = aElement;
this._config = aConfig;
this._onReadyCallback = aCallback;
},
/**
* The editor iframe load event handler.
*
* @private
* @param function [aCallback]
* Optional function invoked when the editor completes loading.
*/
_onLoad: function SE__onLoad(aCallback)
_onIframeLoad: function SE__onIframeLoad()
{
this._iframeWindow = this._iframe.contentWindow.wrappedJSObject;
let window = this._iframeWindow;
let config = this._config;
let window = this._iframe.contentWindow.wrappedJSObject;
let textview = window.orion.textview;
Services.scriptloader.loadSubScript(ORION_SCRIPT, window, "utf8");
let TextModel = window.require("orion/textview/textModel").TextModel;
let TextView = window.require("orion/textview/textView").TextView;
this._expandTab = typeof config.expandTab != "undefined" ?
config.expandTab : SourceEditor.DEFAULTS.EXPAND_TAB;
@ -188,73 +205,77 @@ SourceEditor.prototype = {
let theme = config.theme || SourceEditor.DEFAULTS.THEME;
let stylesheet = theme in ORION_THEMES ? ORION_THEMES[theme] : theme;
this._view = new textview.TextView({
model: new textview.TextModel(config.placeholderText),
this._model = new TextModel(config.placeholderText);
this._view = new TextView({
model: this._model,
parent: "editor",
stylesheet: stylesheet,
tabSize: this._tabSize,
expandTab: this._expandTab,
readonly: config.readOnly,
themeClass: "mozilla" + (config.readOnly ? " readonly" : ""),
});
let onOrionLoad = function() {
this._view.removeEventListener("Load", onOrionLoad);
this._onOrionLoad();
}.bind(this);
this._view.addEventListener("Load", onOrionLoad);
if (Services.appinfo.OS == "Linux") {
this._view.addEventListener("Selection", this._onOrionSelection);
}
let KeyBinding = window.require("orion/textview/keyBinding").KeyBinding;
let TextDND = window.require("orion/textview/textDND").TextDND;
let LineNumberRuler = window.require("orion/textview/rulers").LineNumberRuler;
let UndoStack = window.require("orion/textview/undoStack").UndoStack;
let AnnotationModel = window.require("orion/textview/annotations").AnnotationModel;
this._annotationModel = new AnnotationModel(this._model);
if (config.showLineNumbers) {
this._lines_ruler = new textview.LineNumberRuler(null, "left",
this._linesRuler = new LineNumberRuler(this._annotationModel, "left",
{styleClass: "rulerLines"}, {styleClass: "rulerLine odd"},
{styleClass: "rulerLine even"});
this._view.addRuler(this._lines_ruler);
this._view.addRuler(this._linesRuler);
}
this.setMode(config.mode || SourceEditor.DEFAULTS.MODE);
this._undoStack = new textview.UndoStack(this._view,
this._undoStack = new UndoStack(this._view,
config.undoLimit || SourceEditor.DEFAULTS.UNDO_LIMIT);
this._initEditorFeatures();
this._dragAndDrop = new TextDND(this._view, this._undoStack);
this._view.setAction("tab", this._doTab.bind(this));
let shiftTabKey = new KeyBinding(Ci.nsIDOMKeyEvent.DOM_VK_TAB, false, true);
this._view.setAction("Unindent Lines", this._doUnindentLines.bind(this));
this._view.setKeyBinding(shiftTabKey, "Unindent Lines");
this._view.setAction("enter", this._doEnter.bind(this));
(config.keys || []).forEach(function(aKey) {
let binding = new textview.KeyBinding(aKey.code, aKey.accel, aKey.shift,
aKey.alt);
let binding = new KeyBinding(aKey.code, aKey.accel, aKey.shift, aKey.alt);
this._view.setKeyBinding(binding, aKey.action);
if (aKey.callback) {
this._view.setAction(aKey.action, aKey.callback);
}
}, this);
if (aCallback) {
let iframe = this._view._frame;
let document = iframe.contentDocument;
if (!document || document.readyState != "complete") {
let onIframeLoad = function () {
iframe.contentWindow.removeEventListener("load", onIframeLoad, false);
aCallback(this);
}.bind(this);
iframe.contentWindow.addEventListener("load", onIframeLoad, false);
} else {
aCallback(this);
}
}
},
/**
* Initialize the custom Orion editor features.
* The Orion "Load" event handler. This is called when the Orion editor
* completes the initialization.
* @private
*/
_initEditorFeatures: function SE__initEditorFeatures()
_onOrionLoad: function SE__onOrionLoad()
{
let window = this._iframe.contentWindow.wrappedJSObject;
let textview = window.orion.textview;
this._view.setAction("tab", this._doTab.bind(this));
let shiftTabKey = new textview.KeyBinding(Ci.nsIDOMKeyEvent.DOM_VK_TAB,
false, true);
this._view.setAction("Unindent Lines", this._doUnindentLines.bind(this));
this._view.setKeyBinding(shiftTabKey, "Unindent Lines");
this._view.setAction("enter", this._doEnter.bind(this));
if (this._expandTab) {
this._view.setAction("deletePrevious", this._doDeletePrevious.bind(this));
if (this._onReadyCallback) {
this._onReadyCallback(this);
this._onReadyCallback = null;
}
},
@ -302,34 +323,9 @@ SourceEditor.prototype = {
this._view.setSelection(newSelectionStart, newSelectionEnd);
this.endCompoundChange();
} else {
this.setText(indent, selection.start, selection.end);
return true;
}
return true;
},
/**
* The "deletePrevious" editor action implementation. This adds unindentation
* support to the Backspace key implementation.
* @private
*/
_doDeletePrevious: function SE__doDeletePrevious()
{
let selection = this.getSelection();
if (selection.start == selection.end && this._expandTab) {
let model = this._model;
let lineIndex = model.getLineAtOffset(selection.start);
let lineStart = model.getLineStart(lineIndex);
let offset = selection.start - lineStart;
if (offset >= this._tabSize && (offset % this._tabSize) == 0) {
let text = this.getText(lineStart, selection.start);
if (!/[^ ]/.test(text)) {
this.setText("", selection.start - this._tabSize, selection.end);
return true;
}
}
}
return false;
},
@ -357,7 +353,7 @@ SourceEditor.prototype = {
for (let line, i = firstLine; i <= lastLine; i++) {
line = model.getLine(i, true);
if (line.indexOf(indent) != 0) {
return false;
return true;
}
lines.push(line.substring(indent.length));
}
@ -426,12 +422,22 @@ SourceEditor.prototype = {
},
/**
* Get the Orion Model, the TextModel object instance we use.
* Orion Selection event handler for the X Window System users. This allows
* one to select text and have it copied into the X11 PRIMARY.
*
* @private
* @type object
* @param object aEvent
* The Orion Selection event object.
*/
get _model() {
return this._view.getModel();
_onOrionSelection: function SE__onOrionSelection(aEvent)
{
let text = this.getText(aEvent.newValue.start, aEvent.newValue.end);
if (!text) {
return;
}
clipboardHelper.copyStringToClipboard(text,
Ci.nsIClipboard.kSelectionClipboard);
},
/**
@ -453,15 +459,12 @@ SourceEditor.prototype = {
* The event type you want to listen for.
* @param function aCallback
* The function you want executed when the event is triggered.
* @param mixed [aData]
* Optional data to pass to the callback when the event is triggered.
*/
addEventListener:
function SE_addEventListener(aEventType, aCallback, aData)
function SE_addEventListener(aEventType, aCallback)
{
if (aEventType in ORION_EVENTS) {
this._view.addEventListener(ORION_EVENTS[aEventType], true,
aCallback, aData);
this._view.addEventListener(ORION_EVENTS[aEventType], aCallback);
} else {
throw new Error("SourceEditor.addEventListener() unknown event " +
"type " + aEventType);
@ -478,15 +481,12 @@ SourceEditor.prototype = {
* The event type you have a listener for.
* @param function aCallback
* The function you have as the event handler.
* @param mixed [aData]
* The optional data passed to the callback.
*/
removeEventListener:
function SE_removeEventListener(aEventType, aCallback, aData)
function SE_removeEventListener(aEventType, aCallback)
{
if (aEventType in ORION_EVENTS) {
this._view.removeEventListener(ORION_EVENTS[aEventType], true,
aCallback, aData);
this._view.removeEventListener(ORION_EVENTS[aEventType], aCallback);
} else {
throw new Error("SourceEditor.removeEventListener() unknown event " +
"type " + aEventType);
@ -587,7 +587,7 @@ SourceEditor.prototype = {
*/
hasFocus: function SE_hasFocus()
{
return this._iframe.ownerDocument.activeElement === this._iframe;
return this._view.hasFocus();
},
/**
@ -703,6 +703,47 @@ SourceEditor.prototype = {
this._view.setCaretOffset(aOffset, true);
},
/**
* Get the caret position.
*
* @return object
* An object that holds two properties:
* - line: the line number, counting from 0.
* - col: the column number, counting from 0.
*/
getCaretPosition: function SE_getCaretPosition()
{
let offset = this.getCaretOffset();
let line = this._model.getLineAtOffset(offset);
let lineStart = this._model.getLineStart(line);
let column = offset - lineStart;
return {line: line, col: column};
},
/**
* Set the caret position: line and column.
*
* @param number aLine
* The new caret line location. Line numbers start from 0.
* @param number [aColumn=0]
* Optional. The new caret column location. Columns start from 0.
*/
setCaretPosition: function SE_setCaretPosition(aLine, aColumn)
{
this.setCaretOffset(this._model.getLineStart(aLine) + (aColumn || 0));
},
/**
* Get the line count.
*
* @return number
* The number of lines in the document being edited.
*/
getLineCount: function SE_getLineCount()
{
return this._model.getLineCount();
},
/**
* Get the line delimiter used in the document being edited.
*
@ -726,21 +767,41 @@ SourceEditor.prototype = {
this._styler.destroy();
this._styler = null;
}
if (this._annotationStyler) {
this._annotationStyler.destroy();
this._annotationStyler = null;
}
let window = this._iframe.contentWindow.wrappedJSObject;
let TextStyler = window.examples.textview.TextStyler;
let TextMateStyler = window.orion.editor.TextMateStyler;
let HtmlGrammar = window.orion.editor.HtmlGrammar;
let window = this._iframeWindow;
switch (aMode) {
case SourceEditor.MODES.JAVASCRIPT:
case SourceEditor.MODES.CSS:
this._styler = new TextStyler(this._view, aMode);
let TextStyler =
window.require("examples/textview/textStyler").TextStyler;
this._styler = new TextStyler(this._view, aMode, this._annotationModel);
this._styler.setFoldingEnabled(false);
this._styler.setHighlightCaretLine(true);
let AnnotationStyler =
window.require("orion/textview/annotations").AnnotationStyler;
this._annotationStyler =
new AnnotationStyler(this._view, this._annotationModel);
this._annotationStyler.
addAnnotationType(ORION_ANNOTATION_TYPES.matchingBracket);
this._annotationStyler.
addAnnotationType(ORION_ANNOTATION_TYPES.currentBracket);
break;
case SourceEditor.MODES.HTML:
case SourceEditor.MODES.XML:
this._styler = new TextMateStyler(this._view, HtmlGrammar.grammar);
let TextMateStyler =
window.require("orion/editor/textMateStyler").TextMateStyler;
let HtmlGrammar =
window.require("orion/editor/htmlGrammar").HtmlGrammar;
this._styler = new TextMateStyler(this._view, new HtmlGrammar().grammar);
break;
}
@ -765,7 +826,10 @@ SourceEditor.prototype = {
*/
set readOnly(aValue)
{
this._view.readonly = aValue;
this._view.setOptions({
readonly: aValue,
themeClass: "mozilla" + (aValue ? " readonly" : ""),
});
},
/**
@ -774,7 +838,7 @@ SourceEditor.prototype = {
*/
get readOnly()
{
return this._view.readonly;
return this._view.getOptions("readonly");
},
/**
@ -782,14 +846,24 @@ SourceEditor.prototype = {
*/
destroy: function SE_destroy()
{
if (Services.appinfo.OS == "Linux") {
this._view.removeEventListener("Selection", this._onOrionSelection);
}
this._onOrionSelection = null;
this._view.destroy();
this.parentElement.removeChild(this._iframe);
this.parentElement = null;
this._iframeWindow = null;
this._iframe = null;
this._undoStack = null;
this._styler = null;
this._lines_ruler = null;
this._linesRuler = null;
this._dragAndDrop = null;
this._annotationModel = null;
this._annotationStyler = null;
this._view = null;
this._model = null;
this._config = null;
},
};

Просмотреть файл

@ -231,7 +231,7 @@ SourceEditor.prototype = {
let listeners = this._listeners[SourceEditor.EVENTS.SELECTION] || [];
listeners.forEach(function(aListener) {
aListener.callback.call(null, sendEvent, aListener.data);
aListener.callback.call(null, sendEvent);
}, this);
this._lastSelection = selection;
@ -256,7 +256,7 @@ SourceEditor.prototype = {
{
let listeners = this._listeners[SourceEditor.EVENTS.TEXT_CHANGED] || [];
listeners.forEach(function(aListener) {
aListener.callback.call(null, aEvent, aListener.data);
aListener.callback.call(null, aEvent);
}, this);
},
@ -279,16 +279,13 @@ SourceEditor.prototype = {
* The event type you want to listen for.
* @param function aCallback
* The function you want executed when the event is triggered.
* @param mixed [aData]
* Optional data to pass to the callback when the event is triggered.
*/
addEventListener:
function SE_addEventListener(aEventType, aCallback, aData)
function SE_addEventListener(aEventType, aCallback)
{
const EVENTS = SourceEditor.EVENTS;
let listener = {
type: aEventType,
data: aData,
callback: aCallback,
};
@ -316,24 +313,20 @@ SourceEditor.prototype = {
* The event type you have a listener for.
* @param function aCallback
* The function you have as the event handler.
* @param mixed [aData]
* The optional data passed to the callback.
*/
removeEventListener:
function SE_removeEventListener(aEventType, aCallback, aData)
function SE_removeEventListener(aEventType, aCallback)
{
let listeners = this._listeners[aEventType];
if (!listeners) {
throw new Error("SourceEditor.removeEventListener() called for an " +
"unknown event.");
return;
}
const EVENTS = SourceEditor.EVENTS;
this._listeners[aEventType] = listeners.filter(function(aListener) {
let isSameListener = aListener.type == aEventType &&
aListener.callback === aCallback &&
aListener.data === aData;
aListener.callback === aCallback;
if (isSameListener && aListener.domType) {
aListener.target.removeEventListener(aListener.domType,
aListener.handler, false);
@ -366,7 +359,7 @@ SourceEditor.prototype = {
};
aDOMEvent.preventDefault();
aListener.callback.call(null, sendEvent, aListener.data);
aListener.callback.call(null, sendEvent);
},
/**
@ -601,6 +594,41 @@ SourceEditor.prototype = {
this.setSelection(aOffset, aOffset);
},
/**
* Set the caret position: line and column.
*
* @param number aLine
* The new caret line location. Line numbers start from 0.
* @param number [aColumn=0]
* Optional. The new caret column location. Columns start from 0.
*/
setCaretPosition: function SE_setCaretPosition(aLine, aColumn)
{
aColumn = aColumn || 0;
let text = this._textbox.value;
let i = 0, n = text.length, c0, c1;
let line = 0, col = 0;
while (i < n) {
c1 = text.charAt(i++);
if (line < aLine && (c1 == "\r" || (c0 != "\r" && c1 == "\n"))) {
// Count lines and reset the column only until we reach the desired line
// such that if the desired column is out of boundaries we will stop
// after the given number of characters from the line start.
line++;
col = 0;
} else {
col++;
}
if (line == aLine && col == aColumn) {
this.setCaretOffset(i);
return;
}
c0 = c1;
}
},
/**
* Get the line delimiter used in the document being edited.
*

Просмотреть файл

@ -90,7 +90,7 @@ SourceEditor.MODES = {
* Predefined themes for syntax highlighting.
*/
SourceEditor.THEMES = {
TEXTMATE: "textmate",
MOZILLA: "mozilla",
};
/**
@ -98,7 +98,7 @@ SourceEditor.THEMES = {
*/
SourceEditor.DEFAULTS = {
MODE: SourceEditor.MODES.TEXT,
THEME: SourceEditor.THEMES.TEXTMATE,
THEME: SourceEditor.THEMES.MOZILLA,
UNDO_LIMIT: 200,
TAB_SIZE: 4, // overriden by pref
EXPAND_TAB: true, // overriden by pref

Просмотреть файл

@ -50,6 +50,9 @@ _BROWSER_TEST_FILES = \
browser_bug687573_vscroll.js \
browser_bug687568_pagescroll.js \
browser_bug687580_drag_and_drop.js \
browser_bug695035_middle_click_paste.js \
browser_bug687160_line_api.js \
head.js \
libs:: $(_BROWSER_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

Просмотреть файл

@ -0,0 +1,79 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource:///modules/source-editor.jsm");
let testWin;
let editor;
function test()
{
waitForExplicitFinish();
const windowUrl = "data:text/xml,<?xml version='1.0'?>" +
"<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
" title='test for bug 660784' width='600' height='500'><hbox flex='1'/></window>";
const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
testWin.addEventListener("load", function onWindowLoad() {
testWin.removeEventListener("load", onWindowLoad, false);
waitForFocus(initEditor, testWin);
}, false);
}
function initEditor()
{
let hbox = testWin.document.querySelector("hbox");
editor = new SourceEditor();
editor.init(hbox, {}, editorLoaded);
}
function editorLoaded()
{
let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
editor.focus();
editor.setText("line1\nline2\nline3");
if (component != "textarea") {
is(editor.getLineCount(), 3, "getLineCount() works");
}
editor.setCaretPosition(1);
is(editor.getCaretOffset(), 6, "setCaretPosition(line) works");
let pos;
if (component != "textarea") {
pos = editor.getCaretPosition();
ok(pos.line == 1 && pos.col == 0, "getCaretPosition() works");
}
editor.setCaretPosition(1, 3);
is(editor.getCaretOffset(), 9, "setCaretPosition(line, column) works");
if (component != "textarea") {
pos = editor.getCaretPosition();
ok(pos.line == 1 && pos.col == 3, "getCaretPosition() works");
}
editor.setCaretPosition(2);
is(editor.getCaretOffset(), 12, "setCaretLine() works, confirmed");
if (component != "textarea") {
pos = editor.getCaretPosition();
ok(pos.line == 2 && pos.col == 0, "setCaretPosition(line) works, again");
}
editor.destroy();
testWin.close();
testWin = editor = null;
waitForFocus(finish, window);
}

Просмотреть файл

@ -16,7 +16,6 @@ function test()
ok(true, "skip test for bug 687580: only applicable for Orion");
return; // Testing for the fix requires direct Orion API access.
}
waitForExplicitFinish();
const windowUrl = "data:application/vnd.mozilla.xul+xml,<?xml version='1.0'?>" +
@ -71,7 +70,7 @@ function editorLoaded()
let ds = Cc["@mozilla.org/widget/dragservice;1"].
getService(Ci.nsIDragService);
let target = view._dragNode;
let target = view._clientDiv;
let targetWin = target.ownerDocument.defaultView;
let dataTransfer = null;
@ -93,7 +92,9 @@ function editorLoaded()
target.removeEventListener("drop", onDrop, false);
let selection = editor.getSelection();
is(selection.start, selection.end, "selection is collapsed");
is(selection.end - selection.start,
initialSelection.end - initialSelection.start,
"selection is correct");
is(editor.getText(0, 2), "l3", "drag and drop worked");
let offset = editor.getCaretOffset();

Просмотреть файл

@ -0,0 +1,97 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource:///modules/source-editor.jsm");
let testWin;
let editor;
function test()
{
if (Services.appinfo.OS != "Linux") {
ok(true, "this test only applies to Linux, skipping.")
return;
}
waitForExplicitFinish();
const windowUrl = "data:text/xml,<?xml version='1.0'?>" +
"<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
" title='test for bug 695035' width='600' height='500'><hbox flex='1'/></window>";
const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
testWin.addEventListener("load", function onWindowLoad() {
testWin.removeEventListener("load", onWindowLoad, false);
waitForFocus(initEditor, testWin);
}, false);
}
function initEditor()
{
let hbox = testWin.document.querySelector("hbox");
editor = new SourceEditor();
editor.init(hbox, {}, editorLoaded);
}
function editorLoaded()
{
editor.focus();
let initialText = "initial text!";
editor.setText(initialText);
let expectedString = "foobarBug695035-" + Date.now();
let doCopy = function() {
let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].
getService(Ci.nsIClipboardHelper);
clipboardHelper.copyStringToClipboard(expectedString,
Ci.nsIClipboard.kSelectionClipboard);
};
let onCopy = function() {
editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste);
EventUtils.synthesizeMouse(editor.editorElement, 10, 10, {}, testWin);
EventUtils.synthesizeMouse(editor.editorElement, 11, 11, {button: 1}, testWin);
};
let onPaste = function() {
editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste);
let text = editor.getText();
isnot(text.indexOf(expectedString), -1, "middle-click paste works");
isnot(text, initialText, "middle-click paste works (confirmed)");
executeSoon(doTestBug695032);
};
let doTestBug695032 = function() {
info("test for bug 695032 - editor selection should be placed in the X11 primary selection buffer");
let text = "foobarBug695032 test me, test me!";
editor.setText(text);
waitForSelection(text, function() {
EventUtils.synthesizeKey("a", {accelKey: true}, testWin);
}, testEnd, testEnd);
};
waitForSelection(expectedString, doCopy, onCopy, testEnd);
}
function testEnd()
{
editor.destroy();
testWin.close();
testWin = editor = null;
waitForFocus(finish, window);
}

Просмотреть файл

@ -412,16 +412,16 @@ function testEclipseBug362107()
editor.setCaretOffset(16);
EventUtils.synthesizeKey("VK_UP", {ctrlKey: true}, testWin);
is(editor.getCaretOffset(), 7, "Ctrl-Up works");
is(editor.getCaretOffset(), 9, "Ctrl-Up works");
EventUtils.synthesizeKey("VK_UP", {ctrlKey: true}, testWin);
is(editor.getCaretOffset(), 0, "Ctrl-Up works twice");
is(editor.getCaretOffset(), 2, "Ctrl-Up works twice");
EventUtils.synthesizeKey("VK_DOWN", {ctrlKey: true}, testWin);
is(editor.getCaretOffset(), 13, "Ctrl-Down works");
is(editor.getCaretOffset(), 9, "Ctrl-Down works");
EventUtils.synthesizeKey("VK_DOWN", {ctrlKey: true}, testWin);
is(editor.getCaretOffset(), 20, "Ctrl-Down works twice");
is(editor.getCaretOffset(), 16, "Ctrl-Down works twice");
}
function testBug687577()

Просмотреть файл

@ -0,0 +1,107 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* Polls the X11 primary selection buffer waiting for the expected value. A known
* value different than the expected value is put on the clipboard first (and
* also polled for) so we can be sure the value we get isn't just the expected
* value because it was already in the buffer.
*
* @param aExpectedStringOrValidatorFn
* The string value that is expected to be in the X11 primary selection buffer
* or a validator function getting clipboard data and returning a bool.
* @param aSetupFn
* A function responsible for setting the primary selection buffer to the
* expected value, called after the known value setting succeeds.
* @param aSuccessFn
* A function called when the expected value is found in the primary
* selection buffer.
* @param aFailureFn
* A function called if the expected value isn't found in the primary
* selection buffer within 5s. It can also be called if the known value
* can't be found.
* @param aFlavor [optional] The flavor to look for. Defaults to "text/unicode".
*/
function waitForSelection(aExpectedStringOrValidatorFn, aSetupFn,
aSuccessFn, aFailureFn, aFlavor) {
let requestedFlavor = aFlavor || "text/unicode";
// Build a default validator function for common string input.
var inputValidatorFn = typeof(aExpectedStringOrValidatorFn) == "string"
? function(aData) aData == aExpectedStringOrValidatorFn
: aExpectedStringOrValidatorFn;
let clipboard = Cc["@mozilla.org/widget/clipboard;1"].
getService(Ci.nsIClipboard);
// reset for the next use
function reset() {
waitForSelection._polls = 0;
}
function wait(validatorFn, successFn, failureFn, flavor) {
if (++waitForSelection._polls > 50) {
// Log the failure.
ok(false, "Timed out while polling the X11 primary selection buffer.");
reset();
failureFn();
return;
}
let transferable = Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
transferable.addDataFlavor(requestedFlavor);
clipboard.getData(transferable, clipboard.kSelectionClipboard);
let str = {};
let strLength = {};
transferable.getTransferData(requestedFlavor, str, strLength);
let data = null;
if (str.value) {
let strValue = str.value.QueryInterface(Ci.nsISupportsString);
data = strValue.data.substring(0, strLength.value / 2);
}
if (validatorFn(data)) {
// Don't show the success message when waiting for preExpectedVal
if (preExpectedVal) {
preExpectedVal = null;
} else {
ok(true, "The X11 primary selection buffer has the correct value");
}
reset();
successFn();
} else {
setTimeout(function() wait(validatorFn, successFn, failureFn, flavor), 100);
}
}
// First we wait for a known value different from the expected one.
var preExpectedVal = waitForSelection._monotonicCounter +
"-waitForSelection-known-value";
let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].
getService(Ci.nsIClipboardHelper);
clipboardHelper.copyStringToClipboard(preExpectedVal,
Ci.nsIClipboard.kSelectionClipboard);
wait(function(aData) aData == preExpectedVal,
function() {
// Call the original setup fn
aSetupFn();
wait(inputValidatorFn, aSuccessFn, aFailureFn, requestedFlavor);
}, aFailureFn, "text/unicode");
}
waitForSelection._polls = 0;
waitForSelection.__monotonicCounter = 0;
waitForSelection.__defineGetter__("_monotonicCounter", function () {
return waitForSelection.__monotonicCounter++;
});

Просмотреть файл

@ -221,6 +221,9 @@ AutocompletePopup.prototype = {
*/
clearItems: function AP_clearItems()
{
// Reset the selectedIndex to -1 before clearing the list
this.selectedIndex = -1;
while (this._list.hasChildNodes()) {
this._list.removeChild(this._list.firstChild);
}

Просмотреть файл

@ -5223,7 +5223,7 @@ JSTerm.prototype = {
break;
case Ci.nsIDOMKeyEvent.DOM_VK_RETURN:
if (this.autocompletePopup.isOpen) {
if (this.autocompletePopup.isOpen && this.autocompletePopup.selectedIndex > -1) {
this.acceptProposedCompletion();
}
else {
@ -5439,13 +5439,10 @@ JSTerm.prototype = {
popup.hidePopup();
}
if (items.length > 0) {
popup.selectedIndex = 0;
if (items.length == 1) {
// onSelect is not fired when the popup is not open.
this.onAutocompleteSelect();
}
if (items.length == 1) {
popup.selectedIndex = 0;
}
this.onAutocompleteSelect();
}
let accepted = false;

Просмотреть файл

@ -142,6 +142,7 @@ _BROWSER_TEST_FILES = \
browser_webconsole_bug_659907_console_dir.js \
browser_webconsole_bug_678816.js \
browser_webconsole_bug_664131_console_group.js \
browser_webconsole_bug_704295.js \
browser_gcli_inspect.js \
browser_gcli_integrate.js \
browser_gcli_require.js \

Просмотреть файл

@ -74,6 +74,9 @@ function tabLoaded() {
return aItem.label == "item" + aIndex;
}), true, "getItems returns back the same items");
is(popup.selectedIndex, -1, "no index is selected");
EventUtils.synthesizeKey("VK_DOWN", {});
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
is(popup.selectedIndex, 0, "index 0 is selected");
@ -124,6 +127,9 @@ function autocompletePopupHidden()
is(popup.itemCount, 4, "popup.itemCount is correct");
is(popup.selectedIndex, -1, "no index is selected");
EventUtils.synthesizeKey("VK_DOWN", {});
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
is(popup.selectedIndex, 0, "index 0 is selected");
@ -167,6 +173,9 @@ function testReturnKey()
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 4, "popup.itemCount is correct");
is(popup.selectedIndex, -1, "no index is selected");
EventUtils.synthesizeKey("VK_DOWN", {});
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");

Просмотреть файл

@ -0,0 +1,76 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is DevTools test code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mihai Sucan <mihai.sucan@gmail.com>
* Brijesh Patel <brijesh3105@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// Tests for bug 704295
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//browser/test-console.html";
function test() {
addTab(TEST_URI);
browser.addEventListener("DOMContentLoaded", testCompletion, false);
}
function testCompletion() {
browser.removeEventListener("DOMContentLoaded", testCompletion, false);
openConsole();
var jsterm = HUDService.getHudByWindow(content).jsterm;
var input = jsterm.inputNode;
// Test typing 'var d = 5;' and press RETURN
jsterm.setInputValue("var d = ");
EventUtils.synthesizeKey("5", {});
EventUtils.synthesizeKey(";", {});
is(input.value, "var d = 5;", "var d = 5;");
is(jsterm.completeNode.value, "", "no completion");
EventUtils.synthesizeKey("VK_ENTER", {});
is(jsterm.completeNode.value, "", "clear completion on execute()");
// Test typing 'var a = d' and press RETURN
jsterm.setInputValue("var a = ");
EventUtils.synthesizeKey("d", {});
is(input.value, "var a = d", "var a = d");
is(jsterm.completeNode.value, "", "no completion");
EventUtils.synthesizeKey("VK_ENTER", {});
is(jsterm.completeNode.value, "", "clear completion on execute()");
jsterm = input = null;
finishTest();
}

Просмотреть файл

@ -74,7 +74,7 @@ function testCompletion() {
// Test typing 'document.getElem'.
input.value = "document.getElem";
input.setSelectionRange(16, 16);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY);
jsterm.complete(jsterm.COMPLETE_FORWARD);
is(input.value, "document.getElem", "'document.getElem' completion");
is(jsterm.completeNode.value, " entById", "'document.getElem' completion");

Просмотреть файл

@ -51,9 +51,6 @@
#else
@BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
#endif
#ifdef XP_WIN
@BINPATH@/components/@DLL_PREFIX@uconvd@DLL_SUFFIX@
#endif
#ifdef XP_MACOSX
@BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/
@BINPATH@/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@

Просмотреть файл

@ -72,6 +72,7 @@ components/sidebar.xpt
components/dom_telephony.xpt
components/dom_system_b2g.xpt
#endif
components/uconvd.dll
components/WeaveCrypto.js
components/WeaveCrypto.manifest
components/xmlextras.xpt

Просмотреть файл

@ -32,16 +32,6 @@ rule.sourceElement=element
# e.g "Inherited from body#bodyID (styles.css:20)"
rule.inheritedSource=Inherited from %S (%S)
# LOCALIZATION NOTE (group): Style properties are displayed in categories and
# these are the category names.
group.Text_Fonts_and_Color=Text, Fonts & Color
group.Background=Background
group.Dimensions=Dimensions
group.Positioning_and_Page_Flow=Positioning and Page Flow
group.Borders=Borders
group.Lists=Lists
group.Effects_and_Other=Effects and Other
# LOCALIZATION NOTE (style.highlighter.button): These strings are used inside
# sidebar of the Highlighter for the style inspector button
style.highlighter.button.label1=Properties

Просмотреть файл

@ -59,8 +59,11 @@ saveSearch.inputLabel=Name:
saveSearch.inputDefaultText=New Search
detailsPane.noItems=No items
detailsPane.oneItem=One item
detailsPane.multipleItems=%S items
# LOCALIZATION NOTE (detailsPane.itemsCountLabel): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of items
# example: 111 items
detailsPane.itemsCountLabel=One item;#1 items
mostVisitedTitle=Most Visited
recentlyBookmarkedTitle=Recently Bookmarked

Просмотреть файл

@ -548,7 +548,7 @@ toolbarbutton.bookmark-item[open="true"] {
-moz-padding-end: 2px;
}
.bookmark-item > .toolbarbutton-icon {
.bookmark-item:not(#bookmarks-menu-button) > .toolbarbutton-icon {
width: 16px;
height: 16px;
}

Просмотреть файл

@ -478,6 +478,7 @@ MOZ_GRAPHITE_LIBS = @MOZ_GRAPHITE_LIBS@
MOZ_GRAPHITE = @MOZ_GRAPHITE@
MOZ_OTS_LIBS = @MOZ_OTS_LIBS@
MOZ_SKIA_LIBS = @MOZ_SKIA_LIBS@
MOZ_ENABLE_SKIA = @MOZ_ENABLE_SKIA@
MOZ_NATIVE_SQLITE = @MOZ_NATIVE_SQLITE@
SQLITE_CFLAGS = @SQLITE_CFLAGS@

Просмотреть файл

@ -8018,6 +8018,21 @@ AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
AC_SUBST(GLIB_GMODULE_LIBS)
dnl ========================================================
dnl Graphics checks.
dnl ========================================================
if test "${OS_ARCH}" = "Darwin" -o "${OS_TARGET}" = "Android"; then
MOZ_ENABLE_SKIA=1
else
MOZ_ENABLE_SKIA=
fi
MOZ_ARG_ENABLE_BOOL(skia,
[ --enable-skia Enable use of Skia],
MOZ_ENABLE_SKIA=1,
MOZ_ENABLE_SKIA=)
dnl ========================================================
dnl Check for cairo
dnl ========================================================
@ -8198,7 +8213,13 @@ AC_SUBST(MOZ_OTS_LIBS)
dnl ========================================================
dnl Skia
dnl ========================================================
MOZ_SKIA_LIBS='$(DEPTH)/gfx/skia/$(LIB_PREFIX)skia.$(LIB_SUFFIX)'
if test "$MOZ_ENABLE_SKIA"; then
MOZ_SKIA_LIBS='$(DEPTH)/gfx/skia/$(LIB_PREFIX)skia.$(LIB_SUFFIX)'
AC_DEFINE(MOZ_ENABLE_SKIA)
else
MOZ_SKIA_LIBS=
fi
AC_SUBST(MOZ_ENABLE_SKIA)
AC_SUBST(MOZ_SKIA_LIBS)
dnl ========================================================

Просмотреть файл

@ -162,7 +162,6 @@ const PRInt32 kBackward = 1;
//#define DEBUG_charset
#define NS_USE_NEW_VIEW_SOURCE 1
#define NS_USE_NEW_PLAIN_TEXT 1
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
@ -657,8 +656,7 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand,
nsCAutoString contentType;
aChannel->GetContentType(contentType);
bool viewSource = aCommand && !nsCRT::strcmp(aCommand, "view-source") &&
NS_USE_NEW_VIEW_SOURCE;
bool viewSource = aCommand && !nsCRT::strcmp(aCommand, "view-source");
bool plainText = (contentType.EqualsLiteral(TEXT_PLAIN) ||
contentType.EqualsLiteral(TEXT_CSS) ||
contentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||

Просмотреть файл

@ -100,12 +100,10 @@ BatteryManager::Init(nsPIDOMWindow *aWindow, nsIScriptContext* aScriptContext)
hal::RegisterBatteryObserver(this);
hal::BatteryInformation* batteryInfo = new hal::BatteryInformation();
hal::GetCurrentBatteryInformation(batteryInfo);
hal::BatteryInformation batteryInfo;
hal::GetCurrentBatteryInformation(&batteryInfo);
UpdateFromBatteryInfo(*batteryInfo);
delete batteryInfo;
UpdateFromBatteryInfo(batteryInfo);
}
void

Просмотреть файл

@ -2001,19 +2001,6 @@ _releasevariantvalue(NPVariant* variant)
VOID_TO_NPVARIANT(*variant);
}
bool NP_CALLBACK
_tostring(NPObject* npobj, NPVariant *result)
{
NS_ERROR("Write me!");
if (!NS_IsMainThread()) {
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_tostring called from the wrong thread\n"));
return false;
}
return false;
}
void NP_CALLBACK
_setexception(NPObject* npobj, const NPUTF8 *message)
{

Просмотреть файл

@ -75,25 +75,29 @@ CPPSRCS = \
DEFINES += -DMOZ_GFX -DUSE_CAIRO
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
ifdef MOZ_ENABLE_SKIA
CPPSRCS += \
SourceSurfaceSkia.cpp \
DrawTargetSkia.cpp \
PathSkia.cpp \
SourceSurfaceSkia.cpp \
DrawTargetSkia.cpp \
PathSkia.cpp \
ScaledFontSkia.cpp \
ScaledFontMac.cpp \
$(NULL)
DEFINES += -DUSE_SKIA
endif
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
ifdef MOZ_ENABLE_SKIA
CPPSRCS += \
ScaledFontMac.cpp \
$(NULL)
endif
endif
ifeq (android,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
SourceSurfaceSkia.cpp \
DrawTargetSkia.cpp \
ScaledFontSkia.cpp \
PathSkia.cpp \
$(NULL)
DEFINES += -DUSE_SKIA -DSK_BUILD_FOR_ANDROID_NDK
DEFINES += -DSK_BUILD_FOR_ANDROID_NDK
endif
DEFINES += -DSK_A32_SHIFT=24 -DSK_R32_SHIFT=16 -DSK_G32_SHIFT=8 -DSK_B32_SHIFT=0
@ -109,13 +113,14 @@ CPPSRCS += \
SourceSurfaceD2DTarget.cpp \
PathD2D.cpp \
ScaledFontDWrite.cpp \
SourceSurfaceSkia.cpp \
DrawTargetSkia.cpp \
PathSkia.cpp \
ScaledFontSkia.cpp \
$(NULL)
DEFINES += -DWIN32
ifdef MOZ_ENABLE_SKIA
CPPSRCS += \
ScaledFontWin.cpp \
$(NULL)
DEFINES += -DWIN32 -DUSE_SKIA
endif
endif
#ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)

Просмотреть файл

@ -54,7 +54,7 @@ endif
DIRS += 2d ycbcr angle src qcms gl layers harfbuzz/src ots/src thebes ipc
ifeq (,$(filter-out cocoa android windows,$(MOZ_WIDGET_TOOLKIT)))
ifdef MOZ_ENABLE_SKIA
DIRS += skia
endif

Просмотреть файл

@ -1,96 +0,0 @@
# vim:set noet ts=8:
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Netscape Communications Corporation.
# Portions created by the Initial Developer are Copyright (C) 1998
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = uconvd
LIBRARY_NAME = uconvd
EXPORT_LIBRARY = 1
IS_COMPONENT = 1
MODULE_NAME = UConvData
ifeq (WINNT,$(OS_TARGET))
FORCE_SHARED_LIB = 1
else
LIBXUL_LIBRARY = 1
endif
# To avoid conflict with OS/2 system uconv.dll
SHORT_LIBNAME = mzuconvd
CPPSRCS = \
nsUConvDataModule.cpp \
$(NULL)
LOCAL_INCLUDES = -I$(srcdir)/../util \
-I$(srcdir)/../ucvlatin \
-I$(srcdir)/../ucvibm \
-I$(srcdir)/../ucvja \
-I$(srcdir)/../ucvtw2 \
-I$(srcdir)/../ucvtw \
-I$(srcdir)/../ucvko \
-I$(srcdir)/../ucvcn \
$(NULL)
EXTRA_DSO_LDOPTS += \
$(XPCOM_GLUE_LDOPTS) \
$(XPCOM_LIBS) \
$(MOZ_JS_LIBS) \
$(NSPR_LIBS) \
$(MOZALLOC_LIB) \
$(NULL)
SHARED_LIBRARY_LIBS += \
../ucvlatin/$(LIB_PREFIX)ucvlatin_s.$(LIB_SUFFIX) \
../ucvibm/$(LIB_PREFIX)ucvibm_s.$(LIB_SUFFIX) \
../ucvja/$(LIB_PREFIX)ucvja_s.$(LIB_SUFFIX) \
../ucvtw2/$(LIB_PREFIX)ucvtw2_s.$(LIB_SUFFIX) \
../ucvtw/$(LIB_PREFIX)ucvtw_s.$(LIB_SUFFIX) \
../ucvko/$(LIB_PREFIX)ucvko_s.$(LIB_SUFFIX) \
../ucvcn/$(LIB_PREFIX)ucvcn_s.$(LIB_SUFFIX) \
$(NULL)
ifdef FORCE_SHARED_LIB
SHARED_LIBRARY_LIBS += \
../util/external/$(LIB_PREFIX)ucvxutil_s.$(LIB_SUFFIX) \
$(NULL)
endif
include $(topsrcdir)/config/rules.mk

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -76,7 +76,25 @@ ifneq (,$(INTEL_ARCHITECTURE))
CPPSRCS += nsUTF8ToUnicodeSSE2.cpp
endif
LOCAL_INCLUDES = -I$(srcdir)/../util
LOCAL_INCLUDES = -I$(srcdir)/../util \
-I$(srcdir)/../ucvlatin \
-I$(srcdir)/../ucvibm \
-I$(srcdir)/../ucvja \
-I$(srcdir)/../ucvtw2 \
-I$(srcdir)/../ucvtw \
-I$(srcdir)/../ucvko \
-I$(srcdir)/../ucvcn \
$(NULL)
SHARED_LIBRARY_LIBS += \
../ucvlatin/$(LIB_PREFIX)ucvlatin_s.$(LIB_SUFFIX) \
../ucvibm/$(LIB_PREFIX)ucvibm_s.$(LIB_SUFFIX) \
../ucvja/$(LIB_PREFIX)ucvja_s.$(LIB_SUFFIX) \
../ucvtw2/$(LIB_PREFIX)ucvtw2_s.$(LIB_SUFFIX) \
../ucvtw/$(LIB_PREFIX)ucvtw_s.$(LIB_SUFFIX) \
../ucvko/$(LIB_PREFIX)ucvko_s.$(LIB_SUFFIX) \
../ucvcn/$(LIB_PREFIX)ucvcn_s.$(LIB_SUFFIX) \
$(NULL)
include $(topsrcdir)/config/rules.mk

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -45,9 +45,8 @@ include $(DEPTH)/config/autoconf.mk
MODULE = ucvcn
LIBRARY_NAME = ucvcn_s
FORCE_STATIC_LIB=1
ifneq (WINNT,$(OS_TARGET))
LIBXUL_LIBRARY = 1
endif
CPPSRCS = \
nsGB2312ToUnicodeV2.cpp \

Просмотреть файл

@ -39,7 +39,6 @@
#include "nsUCSupport.h"
#include "nsICharsetConverterManager.h"
#include "nsIServiceManager.h"
#include "nsServiceManagerUtils.h"
static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);

Просмотреть файл

@ -46,9 +46,7 @@ include $(DEPTH)/config/autoconf.mk
MODULE = ucvibm
LIBRARY_NAME = ucvibm_s
FORCE_STATIC_LIB = 1
ifneq (WINNT,$(OS_TARGET))
LIBXUL_LIBRARY = 1
endif
CPPSRCS = \

Просмотреть файл

@ -46,9 +46,8 @@ include $(DEPTH)/config/autoconf.mk
MODULE = ucvja
LIBRARY_NAME = ucvja_s
FORCE_STATIC_LIB = 1
ifneq (WINNT,$(OS_TARGET))
LIBXUL_LIBRARY = 1
endif
CPPSRCS = \
nsJapaneseToUnicode.cpp \

Просмотреть файл

@ -42,7 +42,6 @@
#include "nsICharsetConverterManager.h"
#include "nsIServiceManager.h"
#include "nsServiceManagerUtils.h"
static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
#ifdef XP_OS2

Просмотреть файл

@ -45,9 +45,8 @@ include $(DEPTH)/config/autoconf.mk
MODULE = ucvko
LIBRARY_NAME = ucvko_s
FORCE_STATIC_LIB=1
ifneq (WINNT,$(OS_TARGET))
LIBXUL_LIBRARY = 1
endif
CPPSRCS = \
nsEUCKRToUnicode.cpp \

Просмотреть файл

@ -39,7 +39,6 @@
#include "nsUCSupport.h"
#include "nsICharsetConverterManager.h"
#include "nsIServiceManager.h"
#include "nsServiceManagerUtils.h"
static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);

Просмотреть файл

@ -45,9 +45,8 @@ include $(DEPTH)/config/autoconf.mk
MODULE = ucvlatin
LIBRARY_NAME = ucvlatin_s
FORCE_STATIC_LIB = 1
ifneq (WINNT,$(OS_TARGET))
LIBXUL_LIBRARY = 1
endif
CPPSRCS = \
nsAsciiToUnicode.cpp \

Просмотреть файл

@ -45,9 +45,8 @@ include $(DEPTH)/config/autoconf.mk
MODULE = ucvtw
LIBRARY_NAME = ucvtw_s
FORCE_STATIC_LIB=1
ifneq (WINNT,$(OS_TARGET))
LIBXUL_LIBRARY = 1
endif
CPPSRCS = \
nsBIG5ToUnicode.cpp \

Просмотреть файл

@ -45,9 +45,8 @@ include $(DEPTH)/config/autoconf.mk
MODULE = ucvtw2
LIBRARY_NAME = ucvtw2_s
FORCE_STATIC_LIB=1
ifneq (WINNT,$(OS_TARGET))
LIBXUL_LIBRARY = 1
endif
CPPSRCS = \
nsEUCTWToUnicode.cpp \

Просмотреть файл

@ -42,8 +42,6 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = external
MODULE = uconv
LIBRARY_NAME = ucvutil_s
EXPORT_LIBRARY = 1
@ -52,7 +50,18 @@ LIBXUL_LIBRARY = 1
MODULE_NAME = nsUCUtil
include $(srcdir)/objs.mk
CSRCS = \
ugen.c \
uscan.c \
umap.c \
$(NULL)
CPPSRCS = \
nsUCSupport.cpp \
nsUCConstructors.cpp \
nsUnicodeDecodeHelper.cpp \
nsUnicodeEncodeHelper.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -1,12 +0,0 @@
CSRCS = \
ugen.c \
uscan.c \
umap.c \
$(NULL)
CPPSRCS = \
nsUCSupport.cpp \
nsUCConstructors.cpp \
nsUnicodeDecodeHelper.cpp \
nsUnicodeEncodeHelper.cpp \
$(NULL)

Просмотреть файл

@ -913,11 +913,6 @@ abstract public class GeckoApp
setLaunchState(GeckoApp.LaunchState.GeckoRunning);
GeckoAppShell.sendPendingEventsToGecko();
connectGeckoLayerClient();
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
Looper.myQueue().addIdleHandler(new UpdateIdleHandler());
}
});
} else if (event.equals("ToggleChrome:Hide")) {
mMainHandler.post(new Runnable() {
public void run() {
@ -1435,7 +1430,32 @@ abstract public class GeckoApp
mSmsReceiver = new GeckoSmsManager();
registerReceiver(mSmsReceiver, smsFilter);
final GeckoApp self = this;
final GeckoApp self = this;
mMainHandler.postDelayed(new Runnable() {
public void run() {
Log.w(LOGTAG, "zerdatime " + new Date().getTime() + " - pre checkLaunchState");
/*
XXXX see bug 635342
We want to disable this code if possible. It is about 145ms in runtime
SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
String localeCode = settings.getString(getPackageName() + ".locale", "");
if (localeCode != null && localeCode.length() > 0)
GeckoAppShell.setSelectedLocale(localeCode);
*/
if (!checkLaunchState(LaunchState.Launched)) {
return;
}
// it would be good only to do this if MOZ_UPDATER was defined
long startTime = new Date().getTime();
checkAndLaunchUpdate();
Log.w(LOGTAG, "checking for an update took " + (new Date().getTime() - startTime) + "ms");
}
}, 50);
}
/**
@ -1700,21 +1720,6 @@ abstract public class GeckoApp
GeckoAppShell.handleNotification(action, alertName, alertCookie);
}
// it would be good only to do this if MOZ_UPDATER was defined
private class UpdateIdleHandler implements MessageQueue.IdleHandler {
public boolean queueIdle() {
mMainHandler.post(new Runnable() {
public void run() {
long startTime = new Date().getTime();
checkAndLaunchUpdate();
Log.w(LOGTAG, "checking for an update took " + (new Date().getTime() - startTime) + "ms");
}
});
// only need to run this once.
return false;
}
}
private void checkAndLaunchUpdate() {
Log.i(LOGTAG, "Checking for an update");

Просмотреть файл

@ -791,6 +791,13 @@ pref("network.http.connection-retry-timeout", 250);
// IPv6 connectivity.
pref("network.http.fast-fallback-to-IPv4", true);
// Try and use SPDY when using SSL
pref("network.http.spdy.enabled", false);
pref("network.http.spdy.chunk-size", 4096);
pref("network.http.spdy.timeout", 180);
pref("network.http.spdy.coalesce-hostnames", true);
pref("network.http.spdy.use-alternate-protocol", true);
// default values for FTP
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
// Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)

Просмотреть файл

@ -552,7 +552,8 @@ nsSocketOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *countWrit
*countWritten = 0;
if (count == 0)
// A write of 0 bytes can be used to force the initial SSL handshake.
if (count == 0 && mByteCount)
return NS_OK;
PRFileDesc *fd;

Просмотреть файл

@ -80,6 +80,7 @@ HttpBaseChannel::HttpBaseChannel()
, mChannelIsForDownload(false)
, mTracingEnabled(true)
, mTimingEnabled(false)
, mAllowSpdy(true)
, mSuspendCount(0)
, mRedirectedCachekeys(nsnull)
{
@ -1310,6 +1311,22 @@ HttpBaseChannel::HTTPUpgrade(const nsACString &aProtocolName,
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetAllowSpdy(bool *aAllowSpdy)
{
NS_ENSURE_ARG_POINTER(aAllowSpdy);
*aAllowSpdy = mAllowSpdy;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetAllowSpdy(bool aAllowSpdy)
{
mAllowSpdy = aAllowSpdy;
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsISupportsPriority
//-----------------------------------------------------------------------------
@ -1619,6 +1636,8 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
if (httpInternal) {
// convey the mForceAllowThirdPartyCookie flag
httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie);
// convey the spdy flag
httpInternal->SetAllowSpdy(mAllowSpdy);
// update the DocumentURI indicator since we are being redirected.
// if this was a top-level document channel, then the new channel

Просмотреть файл

@ -167,6 +167,9 @@ public:
NS_IMETHOD GetLocalPort(PRInt32* port);
NS_IMETHOD GetRemoteAddress(nsACString& addr);
NS_IMETHOD GetRemotePort(PRInt32* port);
NS_IMETHOD GetAllowSpdy(bool *aAllowSpdy);
NS_IMETHOD SetAllowSpdy(bool aAllowSpdy);
inline void CleanRedirectCacheChainIfNecessary()
{
if (mRedirectedCachekeys) {
@ -295,6 +298,7 @@ protected:
PRUint32 mTracingEnabled : 1;
// True if timing collection is enabled
PRUint32 mTimingEnabled : 1;
PRUint32 mAllowSpdy : 1;
// Current suspension depth for this channel object
PRUint32 mSuspendCount;

Просмотреть файл

@ -1079,7 +1079,7 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
mPriority, mRedirectionLimit, mAllowPipelining,
mForceAllowThirdPartyCookie, mSendResumeAt,
mStartPos, mEntityID, mChooseApplicationCache,
appCacheClientId);
appCacheClientId, mAllowSpdy);
return NS_OK;
}

Просмотреть файл

@ -143,7 +143,8 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
const PRUint64& startPos,
const nsCString& entityID,
const bool& chooseApplicationCache,
const nsCString& appCacheClientID)
const nsCString& appCacheClientID,
const bool& allowSpdy)
{
nsCOMPtr<nsIURI> uri(aURI);
nsCOMPtr<nsIURI> originalUri(aOriginalURI);
@ -203,6 +204,7 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
httpChan->SetRedirectionLimit(redirectionLimit);
httpChan->SetAllowPipelining(allowPipelining);
httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
httpChan->SetAllowSpdy(allowSpdy);
nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
do_QueryInterface(mChannel);

Просмотреть файл

@ -97,7 +97,8 @@ protected:
const PRUint64& startPos,
const nsCString& entityID,
const bool& chooseApplicationCache,
const nsCString& appCacheClientID);
const nsCString& appCacheClientID,
const bool& allowSpdy);
virtual bool RecvConnectChannel(const PRUint32& channelId);
virtual bool RecvSetPriority(const PRUint16& priority);

Просмотреть файл

@ -110,6 +110,8 @@ CPPSRCS = \
HttpChannelParent.cpp \
HttpChannelChild.cpp \
HttpChannelParentListener.cpp \
SpdySession.cpp \
SpdyStream.cpp \
$(NULL)
LOCAL_INCLUDES = \

Просмотреть файл

@ -81,7 +81,8 @@ parent:
PRUint64 startPos,
nsCString entityID,
bool chooseApplicationCache,
nsCString appCacheClientID);
nsCString appCacheClientID,
bool allowSpdy);
// Used to connect redirected-to channel on the parent with redirected-to
// channel on the child.

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,322 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick McManus <mcmanus@ducksong.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_net_SpdySession_h
#define mozilla_net_SpdySession_h
// SPDY as defined by
// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2
#include "nsAHttpTransaction.h"
#include "nsAHttpConnection.h"
#include "nsClassHashtable.h"
#include "nsDataHashtable.h"
#include "nsDeque.h"
#include "nsHashKeys.h"
#include "zlib.h"
class nsHttpConnection;
class nsISocketTransport;
namespace mozilla { namespace net {
class SpdyStream;
class SpdySession : public nsAHttpTransaction
, public nsAHttpConnection
, public nsAHttpSegmentReader
, public nsAHttpSegmentWriter
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSAHTTPTRANSACTION
NS_DECL_NSAHTTPCONNECTION
NS_DECL_NSAHTTPSEGMENTREADER
NS_DECL_NSAHTTPSEGMENTWRITER
SpdySession(nsAHttpTransaction *, nsISocketTransport *, PRInt32);
~SpdySession();
bool AddStream(nsAHttpTransaction *, PRInt32);
bool CanReuse() { return !mShouldGoAway && !mClosed; }
void DontReuse();
bool RoomForMoreStreams();
PRUint32 RegisterStreamID(SpdyStream *);
const static PRUint8 kFlag_Control = 0x80;
const static PRUint8 kFlag_Data_FIN = 0x01;
const static PRUint8 kFlag_Data_UNI = 0x02;
const static PRUint8 kFlag_Data_ZLIB = 0x02;
const static PRUint8 kPri00 = 0x00;
const static PRUint8 kPri01 = 0x40;
const static PRUint8 kPri02 = 0x80;
const static PRUint8 kPri03 = 0xC0;
enum
{
CONTROL_TYPE_FIRST = 0,
CONTROL_TYPE_SYN_STREAM = 1,
CONTROL_TYPE_SYN_REPLY = 2,
CONTROL_TYPE_RST_STREAM = 3,
CONTROL_TYPE_SETTINGS = 4,
CONTROL_TYPE_NOOP = 5,
CONTROL_TYPE_PING = 6,
CONTROL_TYPE_GOAWAY = 7,
CONTROL_TYPE_HEADERS = 8,
CONTROL_TYPE_WINDOW_UPDATE = 9, /* no longer in v2 */
CONTROL_TYPE_LAST = 10
};
enum
{
RST_PROTOCOL_ERROR = 1,
RST_INVALID_STREAM = 2,
RST_REFUSED_STREAM = 3,
RST_UNSUPPORTED_VERSION = 4,
RST_CANCEL = 5,
RST_INTERNAL_ERROR = 6,
RST_FLOW_CONTROL_ERROR = 7,
RST_BAD_ASSOC_STREAM = 8
};
enum
{
SETTINGS_TYPE_UPLOAD_BW = 1, // kb/s
SETTINGS_TYPE_DOWNLOAD_BW = 2, // kb/s
SETTINGS_TYPE_RTT = 3, // ms
SETTINGS_TYPE_MAX_CONCURRENT = 4, // streams
SETTINGS_TYPE_CWND = 5, // packets
SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE = 6, // percentage
SETTINGS_TYPE_INITIAL_WINDOW = 7 // bytes. Not used in v2.
};
// This should be big enough to hold all of your control packets,
// but if it needs to grow for huge headers it can do so dynamically.
// About 1% of requests to SPDY google services seem to be > 1000
// with all less than 2000.
const static PRUint32 kDefaultBufferSize = 2000;
const static PRUint32 kDefaultQueueSize = 16000;
const static PRUint32 kQueueTailRoom = 4000;
const static PRUint32 kSendingChunkSize = 4000;
const static PRUint32 kDefaultMaxConcurrent = 100;
const static PRUint32 kMaxStreamID = 0x7800000;
static nsresult HandleSynStream(SpdySession *);
static nsresult HandleSynReply(SpdySession *);
static nsresult HandleRstStream(SpdySession *);
static nsresult HandleSettings(SpdySession *);
static nsresult HandleNoop(SpdySession *);
static nsresult HandlePing(SpdySession *);
static nsresult HandleGoAway(SpdySession *);
static nsresult HandleHeaders(SpdySession *);
static nsresult HandleWindowUpdate(SpdySession *);
static void EnsureBuffer(nsAutoArrayPtr<char> &,
PRUint32, PRUint32, PRUint32 &);
// For writing the SPDY data stream to LOG4
static void LogIO(SpdySession *, SpdyStream *, const char *,
const char *, PRUint32);
private:
enum stateType {
BUFFERING_FRAME_HEADER,
BUFFERING_CONTROL_FRAME,
PROCESSING_DATA_FRAME,
DISCARD_DATA_FRAME,
PROCESSING_CONTROL_SYN_REPLY,
PROCESSING_CONTROL_RST_STREAM
};
PRUint32 WriteQueueSize();
void ChangeDownstreamState(enum stateType);
nsresult DownstreamUncompress(char *, PRUint32);
void zlibInit();
nsresult FindHeader(nsCString, nsDependentCSubstring &);
nsresult ConvertHeaders(nsDependentCSubstring &,
nsDependentCSubstring &);
void GeneratePing(PRUint32);
void GenerateRstStream(PRUint32, PRUint32);
void GenerateGoAway();
void CleanupStream(SpdyStream *, nsresult);
void SetWriteCallbacks(nsAHttpTransaction *);
void FlushOutputQueue();
bool RoomForMoreConcurrent();
void ActivateStream(SpdyStream *);
void ProcessPending();
static PLDHashOperator Shutdown(nsAHttpTransaction *,
nsAutoPtr<SpdyStream> &,
void *);
// This is intended to be nsHttpConnectionMgr:nsHttpConnectionHandle taken
// from the first transcation on this session. That object contains the
// pointer to the real network-level nsHttpConnection object.
nsRefPtr<nsAHttpConnection> mConnection;
// The underlying socket transport object is needed to propogate some events
nsISocketTransport *mSocketTransport;
// These are temporary state variables to hold the argument to
// Read/WriteSegments so it can be accessed by On(read/write)segment
// further up the stack.
nsAHttpSegmentReader *mSegmentReader;
nsAHttpSegmentWriter *mSegmentWriter;
PRUint32 mSendingChunkSize; /* the transmisison chunk size */
PRUint32 mNextStreamID; /* 24 bits */
PRUint32 mConcurrentHighWater; /* max parallelism on session */
stateType mDownstreamState; /* in frame, between frames, etc.. */
// Maintain 5 indexes - one by stream ID, one by transaction ptr,
// one list of streams ready to write, one list of streams that are queued
// due to max parallelism settings, and one list of streams
// that must be given priority to write for window updates. The objects
// are not ref counted - they get destryoed
// by the nsClassHashtable implementation when they are removed from
// there.
nsDataHashtable<nsUint32HashKey, SpdyStream *> mStreamIDHash;
nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>,
SpdyStream> mStreamTransactionHash;
nsDeque mReadyForWrite;
nsDeque mQueuedStreams;
// UrgentForWrite is meant to carry window updates. They were defined in
// the v2 spec but apparently never implemented so are now scheduled to
// be removed. But they will be reintroduced for v3, so we will leave
// this queue in place to ease that transition.
nsDeque mUrgentForWrite;
// If we block while wrting out a frame then this points to the stream
// that was blocked. When writing again that stream must be the first
// one to write. It is null if there is not a partial frame.
SpdyStream *mPartialFrame;
// Compression contexts for header transport using deflate.
// SPDY compresses only HTTP headers and does not reset zlib in between
// frames.
z_stream mDownstreamZlib;
z_stream mUpstreamZlib;
// mFrameBuffer is used to store received control packets and the 8 bytes
// of header on data packets
PRUint32 mFrameBufferSize;
PRUint32 mFrameBufferUsed;
nsAutoArrayPtr<char> mFrameBuffer;
// mFrameDataSize/Read are used for tracking the amount of data consumed
// in a data frame. the data itself is not buffered in spdy
// The frame size is mFrameDataSize + the constant 8 byte header
PRUint32 mFrameDataSize;
PRUint32 mFrameDataRead;
bool mFrameDataLast; // This frame was marked FIN
// When a frame has been received that is addressed to a particular stream
// (e.g. a data frame after the stream-id has been decoded), this points
// to the stream.
SpdyStream *mFrameDataStream;
// A state variable to cleanup a closed stream after the stack has unwound.
SpdyStream *mNeedsCleanup;
// The CONTROL_TYPE value for a control frame
PRUint32 mFrameControlType;
// This reason code in the last processed RESET frame
PRUint32 mDownstreamRstReason;
// These are used for decompressing downstream spdy response headers
// This is done at the session level because sometimes the stream
// has already been canceled but the decompression still must happen
// to keep the zlib state correct for the next state of headers.
PRUint32 mDecompressBufferSize;
PRUint32 mDecompressBufferUsed;
nsAutoArrayPtr<char> mDecompressBuffer;
// for the conversion of downstream http headers into spdy formatted headers
nsCString mFlatHTTPResponseHeaders;
PRUint32 mFlatHTTPResponseHeadersOut;
// when set, the session will go away when it reaches 0 streams
bool mShouldGoAway;
// the session has received a nsAHttpTransaction::Close() call
bool mClosed;
// the session received a GoAway frame with a valid GoAwayID
bool mCleanShutdown;
// If a GoAway message was received this is the ID of the last valid
// stream. 0 otherwise. (0 is never a valid stream id.)
PRUint32 mGoAwayID;
// The limit on number of concurrent streams for this session. Normally it
// is basically unlimited, but the SETTINGS control message from the
// server might bring it down.
PRUint32 mMaxConcurrent;
// The actual number of concurrent streams at this moment. Generally below
// mMaxConcurrent, but the max can be lowered in real time to a value
// below the current value
PRUint32 mConcurrent;
// The number of server initiated SYN-STREAMS, tracked for telemetry
PRUint32 mServerPushedResources;
// This is a output queue of bytes ready to be written to the SSL stream.
// When that streams returns WOULD_BLOCK on direct write the bytes get
// coalesced together here. This results in larger writes to the SSL layer.
// The buffer is not dynamically grown to accomodate stream writes, but
// does expand to accept infallible session wide frames like GoAway and RST.
PRUint32 mOutputQueueSize;
PRUint32 mOutputQueueUsed;
PRUint32 mOutputQueueSent;
nsAutoArrayPtr<char> mOutputQueueBuffer;
};
}} // namespace mozilla::net
#endif // mozilla_net_SpdySession_h

Просмотреть файл

@ -0,0 +1,849 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick McManus <mcmanus@ducksong.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsHttp.h"
#include "SpdySession.h"
#include "SpdyStream.h"
#include "nsAlgorithm.h"
#include "prnetdb.h"
#include "nsHttpRequestHead.h"
#include "mozilla/Telemetry.h"
#include "nsISocketTransport.h"
#include "nsISupportsPriority.h"
#ifdef DEBUG
// defined by the socket transport service while active
extern PRThread *gSocketThread;
#endif
namespace mozilla {
namespace net {
SpdyStream::SpdyStream(nsAHttpTransaction *httpTransaction,
SpdySession *spdySession,
nsISocketTransport *socketTransport,
PRUint32 chunkSize,
z_stream *compressionContext,
PRInt32 priority)
: mUpstreamState(GENERATING_SYN_STREAM),
mTransaction(httpTransaction),
mSession(spdySession),
mSocketTransport(socketTransport),
mSegmentReader(nsnull),
mSegmentWriter(nsnull),
mStreamID(0),
mChunkSize(chunkSize),
mSynFrameComplete(0),
mBlockedOnWrite(0),
mRequestBlockedOnRead(0),
mSentFinOnData(0),
mRecvdFin(0),
mFullyOpen(0),
mTxInlineFrameAllocation(SpdySession::kDefaultBufferSize),
mTxInlineFrameSize(0),
mTxInlineFrameSent(0),
mTxStreamFrameSize(0),
mTxStreamFrameSent(0),
mZlib(compressionContext),
mRequestBodyLen(0),
mPriority(priority)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
LOG3(("SpdyStream::SpdyStream %p", this));
mTxInlineFrame = new char[mTxInlineFrameAllocation];
}
SpdyStream::~SpdyStream()
{
}
// ReadSegments() is used to write data down the socket. Generally, HTTP
// request data is pulled from the approriate transaction and
// converted to SPDY data. Sometimes control data like a window-update is
// generated instead.
nsresult
SpdyStream::ReadSegments(nsAHttpSegmentReader *reader,
PRUint32 count,
PRUint32 *countRead)
{
LOG3(("SpdyStream %p ReadSegments reader=%p count=%d state=%x",
this, reader, count, mUpstreamState));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
nsresult rv = NS_ERROR_UNEXPECTED;
mBlockedOnWrite = 0;
mRequestBlockedOnRead = 0;
switch (mUpstreamState) {
case GENERATING_SYN_STREAM:
case GENERATING_REQUEST_BODY:
case SENDING_REQUEST_BODY:
// Call into the HTTP Transaction to generate the HTTP request
// stream. That stream will show up in OnReadSegment().
mSegmentReader = reader;
rv = mTransaction->ReadSegments(this, count, countRead);
mSegmentReader = nsnull;
if (NS_SUCCEEDED(rv) &&
mUpstreamState == GENERATING_SYN_STREAM &&
!mSynFrameComplete)
mBlockedOnWrite = 1;
// Mark that we are blocked on read if we the http transaction
// is going to get us going again.
if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mBlockedOnWrite)
mRequestBlockedOnRead = 1;
if (!mBlockedOnWrite && NS_SUCCEEDED(rv) && (!*countRead)) {
LOG3(("ReadSegments %p Send Request data complete from %x",
this, mUpstreamState));
if (mSentFinOnData) {
ChangeState(UPSTREAM_COMPLETE);
}
else {
GenerateDataFrameHeader(0, true);
ChangeState(SENDING_FIN_STREAM);
mBlockedOnWrite = 1;
rv = NS_BASE_STREAM_WOULD_BLOCK;
}
}
break;
case SENDING_SYN_STREAM:
// We were trying to send the SYN-STREAM but only got part of it out
// before being blocked. Try and send more.
mSegmentReader = reader;
rv = TransmitFrame(nsnull, nsnull);
mSegmentReader = nsnull;
*countRead = 0;
if (NS_SUCCEEDED(rv))
rv = NS_BASE_STREAM_WOULD_BLOCK;
if (!mTxInlineFrameSize) {
if (mSentFinOnData) {
ChangeState(UPSTREAM_COMPLETE);
rv = NS_OK;
}
else {
ChangeState(GENERATING_REQUEST_BODY);
mBlockedOnWrite = 1;
}
}
break;
case SENDING_FIN_STREAM:
// We were trying to send the SYN-STREAM but only got part of it out
// before being blocked. Try and send more.
if (!mSentFinOnData) {
mSegmentReader = reader;
rv = TransmitFrame(nsnull, nsnull);
mSegmentReader = nsnull;
if (!mTxInlineFrameSize)
ChangeState(UPSTREAM_COMPLETE);
}
else {
rv = NS_OK;
mTxInlineFrameSize = 0; // cancel fin data packet
ChangeState(UPSTREAM_COMPLETE);
}
*countRead = 0;
// don't change OK to WOULD BLOCK. we are really done sending if OK
break;
default:
NS_ABORT_IF_FALSE(false, "SpdyStream::ReadSegments unknown state");
break;
}
return rv;
}
// WriteSegments() is used to read data off the socket. Generally this is
// just the SPDY frame header and from there the appropriate SPDYStream
// is identified from the Stream-ID. The http transaction associated with
// that read then pulls in the data directly.
nsresult
SpdyStream::WriteSegments(nsAHttpSegmentWriter *writer,
PRUint32 count,
PRUint32 *countWritten)
{
LOG3(("SpdyStream::WriteSegments %p count=%d state=%x",
this, count, mUpstreamState));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(!mSegmentWriter, "segment writer in progress");
mSegmentWriter = writer;
nsresult rv = mTransaction->WriteSegments(writer, count, countWritten);
mSegmentWriter = nsnull;
return rv;
}
PLDHashOperator
SpdyStream::hdrHashEnumerate(const nsACString &key,
nsAutoPtr<nsCString> &value,
void *closure)
{
SpdyStream *self = static_cast<SpdyStream *>(closure);
self->CompressToFrame(key);
self->CompressToFrame(value.get());
return PL_DHASH_NEXT;
}
nsresult
SpdyStream::ParseHttpRequestHeaders(const char *buf,
PRUint32 avail,
PRUint32 *countUsed)
{
// Returns NS_OK even if the headers are incomplete
// set mSynFrameComplete flag if they are complete
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(mUpstreamState == GENERATING_SYN_STREAM, "wrong state");
LOG3(("SpdyStream::ParseHttpRequestHeaders %p avail=%d state=%x",
this, avail, mUpstreamState));
mFlatHttpRequestHeaders.Append(buf, avail);
// We can use the simple double crlf because firefox is the
// only client we are parsing
PRInt32 endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
if (endHeader == -1) {
// We don't have all the headers yet
LOG3(("SpdyStream::ParseHttpRequestHeaders %p "
"Need more header bytes. Len = %d",
this, mFlatHttpRequestHeaders.Length()));
*countUsed = avail;
return NS_OK;
}
// We have recvd all the headers, trim the local
// buffer of the final empty line, and set countUsed to reflect
// the whole header has been consumed.
PRUint32 oldLen = mFlatHttpRequestHeaders.Length();
mFlatHttpRequestHeaders.SetLength(endHeader + 2);
*countUsed = avail - (oldLen - endHeader) + 4;
mSynFrameComplete = 1;
// It is now OK to assign a streamID that we are assured will
// be monotonically increasing amongst syn-streams on this
// session
mStreamID = mSession->RegisterStreamID(this);
NS_ABORT_IF_FALSE(mStreamID & 1,
"Spdy Stream Channel ID must be odd");
if (mStreamID >= 0x80000000) {
// streamID must fit in 31 bits. This is theoretically possible
// because stream ID assignment is asynchronous to stream creation
// because of the protocol requirement that the ID in syn-stream
// be monotonically increasing. In reality this is really not possible
// because new streams stop being added to a session with 0x10000000 / 2
// IDs still available and no race condition is going to bridge that gap,
// so we can be comfortable on just erroring out for correctness in that
// case.
LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
return NS_ERROR_UNEXPECTED;
}
// Now we need to convert the flat http headers into a set
// of SPDY headers.. writing to mTxInlineFrame{sz}
mTxInlineFrame[0] = SpdySession::kFlag_Control;
mTxInlineFrame[1] = 2; /* version */
mTxInlineFrame[2] = 0;
mTxInlineFrame[3] = SpdySession::CONTROL_TYPE_SYN_STREAM;
// 4 to 7 are length and flags, we'll fill that in later
PRUint32 networkOrderID = PR_htonl(mStreamID);
memcpy(mTxInlineFrame + 8, &networkOrderID, 4);
// this is the associated-to field, which is not used sending
// from the client in the http binding
memset (mTxInlineFrame + 12, 0, 4);
// Priority flags are the C0 mask of byte 16.
// From low to high: 00 40 80 C0
// higher raw priority values are actually less important
//
// The other 6 bits of 16 are unused. Spdy/3 will expand
// priority to 4 bits.
//
// When Spdy/3 implements WINDOW_UPDATE the lowest priority
// streams over a threshold (32?) should be given tiny
// receive windows, separate from their spdy priority
//
if (mPriority >= nsISupportsPriority::PRIORITY_LOW)
mTxInlineFrame[16] = SpdySession::kPri00;
else if (mPriority >= nsISupportsPriority::PRIORITY_NORMAL)
mTxInlineFrame[16] = SpdySession::kPri01;
else if (mPriority >= nsISupportsPriority::PRIORITY_HIGH)
mTxInlineFrame[16] = SpdySession::kPri02;
else
mTxInlineFrame[16] = SpdySession::kPri03;
mTxInlineFrame[17] = 0; /* unused */
// nsCString methodHeader;
// mTransaction->RequestHead()->Method()->ToUTF8String(methodHeader);
const char *methodHeader = mTransaction->RequestHead()->Method().get();
nsCString hostHeader;
mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
nsCString versionHeader;
if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1)
versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
else
versionHeader = NS_LITERAL_CSTRING("HTTP/1.0");
nsClassHashtable<nsCStringHashKey, nsCString> hdrHash;
// use mRequestHead() to get a sense of how big to make the hash,
// even though we are parsing the actual text stream because
// it is legit to append headers.
hdrHash.Init(1 + (mTransaction->RequestHead()->Headers().Count() * 2));
const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
// need to hash all the headers together to remove duplicates, special
// headers, etc..
PRInt32 crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
while (true) {
PRInt32 startIndex = crlfIndex + 2;
crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex);
if (crlfIndex == -1)
break;
PRInt32 colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex,
crlfIndex - startIndex);
if (colonIndex == -1)
break;
nsDependentCSubstring name = Substring(beginBuffer + startIndex,
beginBuffer + colonIndex);
// all header names are lower case in spdy
ToLowerCase(name);
if (name.Equals("method") ||
name.Equals("version") ||
name.Equals("scheme") ||
name.Equals("keep-alive") ||
name.Equals("accept-encoding") ||
name.Equals("te") ||
name.Equals("connection") ||
name.Equals("proxy-connection") ||
name.Equals("url"))
continue;
nsCString *val = hdrHash.Get(name);
if (!val) {
val = new nsCString();
hdrHash.Put(name, val);
}
PRInt32 valueIndex = colonIndex + 1;
while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
++valueIndex;
nsDependentCSubstring v = Substring(beginBuffer + valueIndex,
beginBuffer + crlfIndex);
if (!val->IsEmpty())
val->Append(static_cast<char>(0));
val->Append(v);
if (name.Equals("content-length")) {
PRInt64 len;
if (nsHttp::ParseInt64(val->get(), nsnull, &len))
mRequestBodyLen = len;
}
}
mTxInlineFrameSize = 18;
LOG3(("http request headers to encode are: \n%s",
mFlatHttpRequestHeaders.get()));
// The header block length
PRUint16 count = hdrHash.Count() + 4; /* method, scheme, url, version */
CompressToFrame(count);
// method, scheme, url, and version headers for request line
CompressToFrame(NS_LITERAL_CSTRING("method"));
CompressToFrame(methodHeader, strlen(methodHeader));
CompressToFrame(NS_LITERAL_CSTRING("scheme"));
CompressToFrame(NS_LITERAL_CSTRING("https"));
CompressToFrame(NS_LITERAL_CSTRING("url"));
CompressToFrame(mTransaction->RequestHead()->RequestURI());
CompressToFrame(NS_LITERAL_CSTRING("version"));
CompressToFrame(versionHeader);
hdrHash.Enumerate(hdrHashEnumerate, this);
CompressFlushFrame();
// 4 to 7 are length and flags, which we can now fill in
(reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
PR_htonl(mTxInlineFrameSize - 8);
NS_ABORT_IF_FALSE(!mTxInlineFrame[4],
"Size greater than 24 bits");
// For methods other than POST and PUT, we will set the fin bit
// right on the syn stream packet.
if (mTransaction->RequestHead()->Method() != nsHttp::Post &&
mTransaction->RequestHead()->Method() != nsHttp::Put) {
mSentFinOnData = 1;
mTxInlineFrame[4] = SpdySession::kFlag_Data_FIN;
}
Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameSize - 18);
// The size of the input headers is approximate
PRUint32 ratio =
(mTxInlineFrameSize - 18) * 100 /
(11 + mTransaction->RequestHead()->RequestURI().Length() +
mFlatHttpRequestHeaders.Length());
Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
return NS_OK;
}
nsresult
SpdyStream::TransmitFrame(const char *buf,
PRUint32 *countUsed)
{
NS_ABORT_IF_FALSE(mTxInlineFrameSize, "empty stream frame in transmit");
NS_ABORT_IF_FALSE(mSegmentReader, "TransmitFrame with null mSegmentReader");
PRUint32 transmittedCount;
nsresult rv;
LOG3(("SpdyStream::TransmitFrame %p inline=%d of %d stream=%d of %d",
this, mTxInlineFrameSent, mTxInlineFrameSize,
mTxStreamFrameSent, mTxStreamFrameSize));
if (countUsed)
*countUsed = 0;
mBlockedOnWrite = 0;
// In the (relatively common) event that we have a small amount of data
// split between the inlineframe and the streamframe, then move the stream
// data into the inlineframe via copy in order to coalesce into one write.
// Given the interaction with ssl this is worth the small copy cost.
if (mTxStreamFrameSize && mTxInlineFrameSize &&
!mTxInlineFrameSent && !mTxStreamFrameSent &&
mTxStreamFrameSize < SpdySession::kDefaultBufferSize &&
mTxInlineFrameSize + mTxStreamFrameSize < mTxInlineFrameAllocation) {
LOG3(("Coalesce Transmit"));
memcpy (mTxInlineFrame + mTxInlineFrameSize,
buf, mTxStreamFrameSize);
mTxInlineFrameSize += mTxStreamFrameSize;
mTxStreamFrameSent = 0;
mTxStreamFrameSize = 0;
}
// This function calls mSegmentReader->OnReadSegment to report the actual SPDY
// bytes through to the SpdySession and then the HttpConnection which calls
// the socket write function.
while (mTxInlineFrameSent < mTxInlineFrameSize) {
rv = mSegmentReader->OnReadSegment(mTxInlineFrame + mTxInlineFrameSent,
mTxInlineFrameSize - mTxInlineFrameSent,
&transmittedCount);
LOG3(("SpdyStream::TransmitFrame for inline session=%p "
"stream=%p result %x len=%d",
mSession, this, rv, transmittedCount));
SpdySession::LogIO(mSession, this, "Writing from Inline Buffer",
mTxInlineFrame + mTxInlineFrameSent,
transmittedCount);
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
mBlockedOnWrite = 1;
if (NS_FAILED(rv)) // this will include WOULD_BLOCK
return rv;
mTxInlineFrameSent += transmittedCount;
}
PRUint32 offset = 0;
NS_ABORT_IF_FALSE(mTxStreamFrameSize >= mTxStreamFrameSent,
"negative unsent");
PRUint32 avail = mTxStreamFrameSize - mTxStreamFrameSent;
while (avail) {
NS_ABORT_IF_FALSE(countUsed, "null countused pointer in a stream context");
rv = mSegmentReader->OnReadSegment(buf + offset, avail, &transmittedCount);
LOG3(("SpdyStream::TransmitFrame for regular session=%p "
"stream=%p result %x len=%d",
mSession, this, rv, transmittedCount));
SpdySession::LogIO(mSession, this, "Writing from Transaction Buffer",
buf + offset, transmittedCount);
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
mBlockedOnWrite = 1;
if (NS_FAILED(rv)) // this will include WOULD_BLOCK
return rv;
if (mUpstreamState == SENDING_REQUEST_BODY) {
mTransaction->OnTransportStatus(mSocketTransport,
nsISocketTransport::STATUS_SENDING_TO,
transmittedCount);
}
*countUsed += transmittedCount;
avail -= transmittedCount;
offset += transmittedCount;
mTxStreamFrameSent += transmittedCount;
}
if (!avail) {
mTxInlineFrameSent = 0;
mTxInlineFrameSize = 0;
mTxStreamFrameSent = 0;
mTxStreamFrameSize = 0;
}
return NS_OK;
}
void
SpdyStream::ChangeState(enum stateType newState)
{
LOG3(("SpdyStream::ChangeState() %p from %X to %X",
this, mUpstreamState, newState));
mUpstreamState = newState;
return;
}
void
SpdyStream::GenerateDataFrameHeader(PRUint32 dataLength, bool lastFrame)
{
LOG3(("SpdyStream::GenerateDataFrameHeader %p len=%d last=%d",
this, dataLength, lastFrame));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(!mTxInlineFrameSize, "inline frame not empty");
NS_ABORT_IF_FALSE(!mTxInlineFrameSent, "inline partial send not 0");
NS_ABORT_IF_FALSE(!mTxStreamFrameSize, "stream frame not empty");
NS_ABORT_IF_FALSE(!mTxStreamFrameSent, "stream partial send not 0");
NS_ABORT_IF_FALSE(!(dataLength & 0xff000000), "datalength > 24 bits");
(reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[0] = PR_htonl(mStreamID);
(reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
PR_htonl(dataLength);
NS_ABORT_IF_FALSE(!(mTxInlineFrame[0] & 0x80),
"control bit set unexpectedly");
NS_ABORT_IF_FALSE(!mTxInlineFrame[4], "flag bits set unexpectedly");
mTxInlineFrameSize = 8;
mTxStreamFrameSize = dataLength;
if (lastFrame) {
mTxInlineFrame[4] |= SpdySession::kFlag_Data_FIN;
if (dataLength)
mSentFinOnData = 1;
}
}
void
SpdyStream::CompressToFrame(const nsACString &str)
{
CompressToFrame(str.BeginReading(), str.Length());
}
void
SpdyStream::CompressToFrame(const nsACString *str)
{
CompressToFrame(str->BeginReading(), str->Length());
}
// Dictionary taken from
// http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2
// Name/Value Header Block Format
// spec indicates that the compression dictionary is not null terminated
// but in reality it is. see:
// https://groups.google.com/forum/#!topic/spdy-dev/2pWxxOZEIcs
const char *SpdyStream::kDictionary =
"optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
"languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
"f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
"-agent10010120020120220320420520630030130230330430530630740040140240340440"
"5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
"glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
"ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
"sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
"oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
"ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
"pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
"ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
".1statusversionurl";
// use for zlib data types
void *
SpdyStream::zlib_allocator(void *opaque, uInt items, uInt size)
{
return moz_xmalloc(items * size);
}
// use for zlib data types
void
SpdyStream::zlib_destructor(void *opaque, void *addr)
{
moz_free(addr);
}
void
SpdyStream::ExecuteCompress(PRUint32 flushMode)
{
// Expect mZlib->avail_in and mZlib->next_in to be set.
// Append the compressed version of next_in to mTxInlineFrame
do
{
PRUint32 avail = mTxInlineFrameAllocation - mTxInlineFrameSize;
if (avail < 1) {
SpdySession::EnsureBuffer(mTxInlineFrame,
mTxInlineFrameAllocation + 2000,
mTxInlineFrameSize,
mTxInlineFrameAllocation);
avail = mTxInlineFrameAllocation - mTxInlineFrameSize;
}
mZlib->next_out = reinterpret_cast<unsigned char *> (mTxInlineFrame.get()) +
mTxInlineFrameSize;
mZlib->avail_out = avail;
deflate(mZlib, flushMode);
mTxInlineFrameSize += avail - mZlib->avail_out;
} while (mZlib->avail_in > 0 || !mZlib->avail_out);
}
void
SpdyStream::CompressToFrame(PRUint16 data)
{
// convert the data to network byte order and write that
// to the compressed stream
data = PR_htons(data);
mZlib->next_in = reinterpret_cast<unsigned char *> (&data);
mZlib->avail_in = 2;
ExecuteCompress(Z_NO_FLUSH);
}
void
SpdyStream::CompressToFrame(const char *data, PRUint32 len)
{
// Format calls for a network ordered 16 bit length
// followed by the utf8 string
// for now, silently truncate headers greater than 64KB. Spdy/3 will
// fix this by making the len a 32 bit quantity
if (len > 0xffff)
len = 0xffff;
PRUint16 networkLen = len;
networkLen = PR_htons(len);
// write out the length
mZlib->next_in = reinterpret_cast<unsigned char *> (&networkLen);
mZlib->avail_in = 2;
ExecuteCompress(Z_NO_FLUSH);
// write out the data
mZlib->next_in = (unsigned char *)data;
mZlib->avail_in = len;
ExecuteCompress(Z_NO_FLUSH);
}
void
SpdyStream::CompressFlushFrame()
{
mZlib->next_in = (unsigned char *) "";
mZlib->avail_in = 0;
ExecuteCompress(Z_SYNC_FLUSH);
}
void
SpdyStream::Close(nsresult reason)
{
mTransaction->Close(reason);
}
//-----------------------------------------------------------------------------
// nsAHttpSegmentReader
//-----------------------------------------------------------------------------
nsresult
SpdyStream::OnReadSegment(const char *buf,
PRUint32 count,
PRUint32 *countRead)
{
LOG3(("SpdyStream::OnReadSegment %p count=%d state=%x",
this, count, mUpstreamState));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(mSegmentReader, "OnReadSegment with null mSegmentReader");
nsresult rv = NS_ERROR_UNEXPECTED;
PRUint32 dataLength;
switch (mUpstreamState) {
case GENERATING_SYN_STREAM:
// The buffer is the HTTP request stream, including at least part of the
// HTTP request header. This state's job is to build a SYN_STREAM frame
// from the header information. count is the number of http bytes available
// (which may include more than the header), and in countRead we return
// the number of those bytes that we consume (i.e. the portion that are
// header bytes)
rv = ParseHttpRequestHeaders(buf, count, countRead);
if (NS_FAILED(rv))
return rv;
LOG3(("ParseHttpRequestHeaders %p used %d of %d.",
this, *countRead, count));
if (mSynFrameComplete) {
NS_ABORT_IF_FALSE(mTxInlineFrameSize,
"OnReadSegment SynFrameComplete 0b");
rv = TransmitFrame(nsnull, nsnull);
if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
rv = NS_OK;
if (mTxInlineFrameSize)
ChangeState(SENDING_SYN_STREAM);
else
ChangeState(GENERATING_REQUEST_BODY);
break;
}
NS_ABORT_IF_FALSE(*countRead == count,
"Header parsing not complete but unused data");
break;
case GENERATING_REQUEST_BODY:
NS_ABORT_IF_FALSE(!mTxInlineFrameSent,
"OnReadSegment in generating_request_body with "
"frame in progress");
if (count < mChunkSize && count < mRequestBodyLen) {
LOG3(("SpdyStream %p id %x has %d to write out of a bodylen %d"
" with a chunk size of %d. Waiting for more.",
this, mStreamID, count, mChunkSize, mRequestBodyLen));
rv = NS_BASE_STREAM_WOULD_BLOCK;
break;
}
dataLength = NS_MIN(count, mChunkSize);
if (dataLength > mRequestBodyLen)
return NS_ERROR_UNEXPECTED;
mRequestBodyLen -= dataLength;
GenerateDataFrameHeader(dataLength, !mRequestBodyLen);
ChangeState(SENDING_REQUEST_BODY);
// NO BREAK
case SENDING_REQUEST_BODY:
NS_ABORT_IF_FALSE(mTxInlineFrameSize, "OnReadSegment Send Data Header 0b");
rv = TransmitFrame(buf, countRead);
LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
"Header is %d/%d Body is %d/%d.",
rv, *countRead,
mTxInlineFrameSent, mTxInlineFrameSize,
mTxStreamFrameSent, mTxStreamFrameSize));
if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
rv = NS_OK;
// If that frame was all sent, look for another one
if (!mTxInlineFrameSize)
ChangeState(GENERATING_REQUEST_BODY);
break;
case SENDING_SYN_STREAM:
rv = NS_BASE_STREAM_WOULD_BLOCK;
break;
case SENDING_FIN_STREAM:
NS_ABORT_IF_FALSE(false,
"resuming partial fin stream out of OnReadSegment");
break;
default:
NS_ABORT_IF_FALSE(false, "SpdyStream::OnReadSegment non-write state");
break;
}
return rv;
}
//-----------------------------------------------------------------------------
// nsAHttpSegmentWriter
//-----------------------------------------------------------------------------
nsresult
SpdyStream::OnWriteSegment(char *buf,
PRUint32 count,
PRUint32 *countWritten)
{
LOG3(("SpdyStream::OnWriteSegment %p count=%d state=%x",
this, count, mUpstreamState));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(mSegmentWriter, "OnWriteSegment with null mSegmentWriter");
return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
}
} // namespace mozilla::net
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,207 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick McManus <mcmanus@ducksong.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_net_SpdyStream_h
#define mozilla_net_SpdyStream_h
#include "nsAHttpTransaction.h"
namespace mozilla { namespace net {
class SpdyStream : public nsAHttpSegmentReader
, public nsAHttpSegmentWriter
{
public:
NS_DECL_NSAHTTPSEGMENTREADER
NS_DECL_NSAHTTPSEGMENTWRITER
SpdyStream(nsAHttpTransaction *,
SpdySession *, nsISocketTransport *,
PRUint32, z_stream *, PRInt32);
~SpdyStream();
PRUint32 StreamID() { return mStreamID; }
nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *);
nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *);
bool BlockedOnWrite()
{
return static_cast<bool>(mBlockedOnWrite);
}
bool RequestBlockedOnRead()
{
return static_cast<bool>(mRequestBlockedOnRead);
}
// returns false if called more than once
bool SetFullyOpen()
{
bool result = !mFullyOpen;
mFullyOpen = 1;
return result;
}
nsAHttpTransaction *Transaction()
{
return mTransaction;
}
void Close(nsresult reason);
void SetRecvdFin(bool aStatus) { mRecvdFin = aStatus ? 1 : 0; }
bool RecvdFin() { return mRecvdFin; }
// The zlib header compression dictionary defined by SPDY,
// and hooks to the mozilla allocator for zlib to use.
static const char *kDictionary;
static void *zlib_allocator(void *, uInt, uInt);
static void zlib_destructor(void *, void *);
private:
enum stateType {
GENERATING_SYN_STREAM,
SENDING_SYN_STREAM,
GENERATING_REQUEST_BODY,
SENDING_REQUEST_BODY,
SENDING_FIN_STREAM,
UPSTREAM_COMPLETE
};
static PLDHashOperator hdrHashEnumerate(const nsACString &,
nsAutoPtr<nsCString> &,
void *);
void ChangeState(enum stateType );
nsresult ParseHttpRequestHeaders(const char *, PRUint32, PRUint32 *);
nsresult TransmitFrame(const char *, PRUint32 *);
void GenerateDataFrameHeader(PRUint32, bool);
void CompressToFrame(const nsACString &);
void CompressToFrame(const nsACString *);
void CompressToFrame(const char *, PRUint32);
void CompressToFrame(PRUint16);
void CompressFlushFrame();
void ExecuteCompress(PRUint32);
// Each stream goes from syn_stream to upstream_complete, perhaps
// looping on multiple instances of generating_request_body and
// sending_request_body for each SPDY chunk in the upload.
enum stateType mUpstreamState;
// The underlying HTTP transaction
nsRefPtr<nsAHttpTransaction> mTransaction;
// The session that this stream is a subset of
SpdySession *mSession;
// The underlying socket transport object is needed to propogate some events
nsISocketTransport *mSocketTransport;
// These are temporary state variables to hold the argument to
// Read/WriteSegments so it can be accessed by On(read/write)segment
// further up the stack.
nsAHttpSegmentReader *mSegmentReader;
nsAHttpSegmentWriter *mSegmentWriter;
// The 24 bit SPDY stream ID
PRUint32 mStreamID;
// The quanta upstream data frames are chopped into
PRUint32 mChunkSize;
// Flag is set when all http request headers have been read
PRUint32 mSynFrameComplete : 1;
// Flag is set when there is more request data to send and the
// stream needs to be rescheduled for writing. Sometimes this
// is done as a matter of fairness, not really due to blocking
PRUint32 mBlockedOnWrite : 1;
// Flag is set when the HTTP processor has more data to send
// but has blocked in doing so.
PRUint32 mRequestBlockedOnRead : 1;
// Flag is set when a FIN has been placed on a data or syn packet
// (i.e after the client has closed)
PRUint32 mSentFinOnData : 1;
// Flag is set after the response frame bearing the fin bit has
// been processed. (i.e. after the server has closed).
PRUint32 mRecvdFin : 1;
// Flag is set after syn reply received
PRUint32 mFullyOpen : 1;
// The InlineFrame and associated data is used for composing control
// frames and data frame headers.
nsAutoArrayPtr<char> mTxInlineFrame;
PRUint32 mTxInlineFrameAllocation;
PRUint32 mTxInlineFrameSize;
PRUint32 mTxInlineFrameSent;
// mTxStreamFrameSize and mTxStreamFrameSent track the progress of
// transmitting a request body data frame. The data frame itself
// is never copied into the spdy layer.
PRUint32 mTxStreamFrameSize;
PRUint32 mTxStreamFrameSent;
// Compression context and buffer for request header compression.
// This is a copy of SpdySession::mUpstreamZlib because it needs
// to remain the same in all streams of a session.
z_stream *mZlib;
nsCString mFlatHttpRequestHeaders;
// Track the content-length of a request body so that we can
// place the fin flag on the last data packet instead of waiting
// for a stream closed indication. Relying on stream close results
// in an extra 0-length runt packet and seems to have some interop
// problems with the google servers.
PRInt64 mRequestBodyLen;
// based on nsISupportsPriority definitions
PRInt32 mPriority;
};
}} // namespace mozilla::net
#endif // mozilla_net_SpdyStream_h

Просмотреть файл

@ -77,8 +77,8 @@ public:
// after a transaction returned NS_BASE_STREAM_WOULD_BLOCK from its
// ReadSegments/WriteSegments methods.
//
virtual nsresult ResumeSend() = 0;
virtual nsresult ResumeRecv() = 0;
virtual nsresult ResumeSend(nsAHttpTransaction *caller) = 0;
virtual nsresult ResumeRecv(nsAHttpTransaction *caller) = 0;
//
// called by the connection manager to close a transaction being processed
@ -132,8 +132,8 @@ public:
#define NS_DECL_NSAHTTPCONNECTION \
nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, bool *reset); \
nsresult ResumeSend(); \
nsresult ResumeRecv(); \
nsresult ResumeSend(nsAHttpTransaction *); \
nsresult ResumeRecv(nsAHttpTransaction *); \
void CloseTransaction(nsAHttpTransaction *, nsresult); \
void GetConnectionInfo(nsHttpConnectionInfo **); \
nsresult TakeTransport(nsISocketTransport **, \

Просмотреть файл

@ -62,6 +62,7 @@ class nsAHttpTransaction : public nsISupports
public:
// called by the connection when it takes ownership of the transaction.
virtual void SetConnection(nsAHttpConnection *) = 0;
virtual nsAHttpConnection *Connection() = 0;
// called by the connection to get security callbacks to set on the
// socket transport.
@ -95,10 +96,16 @@ public:
// called to retrieve the request headers of the transaction
virtual nsHttpRequestHead *RequestHead() = 0;
// determine the number of real http/1.x transactions on this
// abstract object. Pipelines may have multiple, SPDY has 0,
// normal http transactions have 1.
virtual PRUint32 Http1xTransactionCount() = 0;
};
#define NS_DECL_NSAHTTPTRANSACTION \
void SetConnection(nsAHttpConnection *); \
nsAHttpConnection *Connection(); \
void GetSecurityCallbacks(nsIInterfaceRequestor **, \
nsIEventTarget **); \
void OnTransportStatus(nsITransport* transport, \
@ -110,7 +117,8 @@ public:
nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *); \
void Close(nsresult reason); \
void SetSSLConnectFailed(); \
nsHttpRequestHead *RequestHead();
nsHttpRequestHead *RequestHead(); \
PRUint32 Http1xTransactionCount();
//-----------------------------------------------------------------------------
// nsAHttpSegmentReader

Просмотреть файл

@ -131,6 +131,11 @@ typedef PRUint8 nsHttpVersion;
// host. Used by a forced reload to reset the connection states.
#define NS_HTTP_CLEAR_KEEPALIVES (1<<6)
// Disallow the use of the SPDY protocol. This is meant for the contexts
// such as HTTP upgrade which are nonsensical for SPDY, it is not the
// SPDY configuration variable.
#define NS_HTTP_DISALLOW_SPDY (1<<7)
//-----------------------------------------------------------------------------
// some default values
//-----------------------------------------------------------------------------

Просмотреть файл

@ -56,6 +56,7 @@ HTTP_ATOM(Accept_Language, "Accept-Language")
HTTP_ATOM(Accept_Ranges, "Accept-Ranges")
HTTP_ATOM(Age, "Age")
HTTP_ATOM(Allow, "Allow")
HTTP_ATOM(Alternate_Protocol, "Alternate-Protocol")
HTTP_ATOM(Authentication, "Authentication")
HTTP_ATOM(Authorization, "Authorization")
HTTP_ATOM(Cache_Control, "Cache-Control")

Просмотреть файл

@ -209,6 +209,18 @@ nsHttpChannel::Connect(bool firstTime)
LOG(("nsHttpChannel::Connect() STS permissions found\n"));
return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
}
// Check for a previous SPDY Alternate-Protocol directive
if (gHttpHandler->IsSpdyEnabled() && mAllowSpdy) {
nsCAutoString hostPort;
if (NS_SUCCEEDED(mURI->GetHostPort(hostPort)) &&
gHttpHandler->ConnMgr()->GetSpdyAlternateProtocol(hostPort)) {
LOG(("nsHttpChannel::Connect() Alternate-Protocol found\n"));
return AsyncCall(
&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
}
}
}
// ensure that we are using a valid hostname
@ -507,6 +519,9 @@ nsHttpChannel::SetupTransaction()
}
}
if (!mAllowSpdy)
mCaps |= NS_HTTP_DISALLOW_SPDY;
// use the URI path if not proxying (transparent proxying such as SSL proxy
// does not count here). also, figure out what version we should be speaking.
nsCAutoString buf, path;
@ -634,6 +649,7 @@ nsHttpChannel::SetupTransaction()
mCaps |= NS_HTTP_STICKY_CONNECTION;
mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
mCaps |= NS_HTTP_DISALLOW_SPDY;
}
nsCOMPtr<nsIAsyncInputStream> responseStream;
@ -4090,6 +4106,16 @@ nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
mSecurityInfo = mTransaction->SecurityInfo();
}
if (gHttpHandler->IsSpdyEnabled() && !mCachePump && NS_FAILED(mStatus) &&
(mLoadFlags & LOAD_REPLACE) && mOriginalURI && mAllowSpdy) {
// For sanity's sake we may want to cancel an alternate protocol
// redirection involving the original host name
nsCAutoString hostPort;
if (NS_SUCCEEDED(mOriginalURI->GetHostPort(hostPort)))
gHttpHandler->ConnMgr()->RemoveSpdyAlternateProtocol(hostPort);
}
// don't enter this block if we're reading from the cache...
if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
// mTransactionPump doesn't hit OnInputStreamReady and call this until

Просмотреть файл

@ -53,6 +53,9 @@
#include "nsProxyRelease.h"
#include "prmem.h"
#include "nsPreloadedStream.h"
#include "SpdySession.h"
#include "mozilla/Telemetry.h"
#include "nsISupportsPriority.h"
#ifdef DEBUG
// defined by the socket transport service while active
@ -75,6 +78,7 @@ nsHttpConnection::nsHttpConnection()
, mConsiderReusedAfterEpoch(0)
, mCurrentBytesRead(0)
, mMaxBytesRead(0)
, mTotalBytesRead(0)
, mKeepAlive(true) // assume to keep-alive by default
, mKeepAliveMask(true)
, mSupportsPipelining(false) // assume low-grade server
@ -82,6 +86,13 @@ nsHttpConnection::nsHttpConnection()
, mCompletedProxyConnect(false)
, mLastTransactionExpectedNoContent(false)
, mIdleMonitoring(false)
, mHttp1xTransactionCount(0)
, mNPNComplete(false)
, mSetupNPNCalled(false)
, mUsingSpdy(false)
, mPriority(nsISupportsPriority::PRIORITY_NORMAL)
, mReportedSpdy(false)
, mEverUsedSpdy(false)
{
LOG(("Creating nsHttpConnection @%x\n", this));
@ -103,6 +114,24 @@ nsHttpConnection::~nsHttpConnection()
// release our reference to the handler
nsHttpHandler *handler = gHttpHandler;
NS_RELEASE(handler);
if (!mEverUsedSpdy) {
LOG(("nsHttpConnection %p performed %d HTTP/1.x transactions\n",
this, mHttp1xTransactionCount));
mozilla::Telemetry::Accumulate(
mozilla::Telemetry::HTTP_REQUEST_PER_CONN, mHttp1xTransactionCount);
}
if (mTotalBytesRead) {
PRUint32 totalKBRead = static_cast<PRUint32>(mTotalBytesRead >> 10);
LOG(("nsHttpConnection %p read %dkb on connection spdy=%d\n",
this, totalKBRead, mEverUsedSpdy));
mozilla::Telemetry::Accumulate(
mEverUsedSpdy ?
mozilla::Telemetry::SPDY_KBREAD_PER_CONN :
mozilla::Telemetry::HTTP_KBREAD_PER_CONN,
totalKBRead);
}
}
nsresult
@ -141,9 +170,79 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info,
return NS_OK;
}
bool
nsHttpConnection::EnsureNPNComplete()
{
// NPN is only used by SPDY right now.
//
// If for some reason the components to check on NPN aren't available,
// this function will just return true to continue on and disable SPDY
NS_ABORT_IF_FALSE(mSocketTransport, "EnsureNPNComplete "
"socket transport precondition");
if (mNPNComplete)
return true;
nsresult rv;
nsCOMPtr<nsISupports> securityInfo;
nsCOMPtr<nsISSLSocketControl> ssl;
nsCAutoString negotiatedNPN;
rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
if (NS_FAILED(rv))
goto npnComplete;
ssl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv))
goto npnComplete;
rv = ssl->GetNegotiatedNPN(negotiatedNPN);
if (rv == NS_ERROR_NOT_CONNECTED) {
// By writing 0 bytes to the socket the SSL handshake machine is
// pushed forward.
PRUint32 count = 0;
rv = mSocketOut->Write("", 0, &count);
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK)
goto npnComplete;
return false;
}
if (NS_FAILED(rv))
goto npnComplete;
LOG(("nsHttpConnection::EnsureNPNComplete %p negotiated to '%s'",
this, negotiatedNPN.get()));
if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2"))) {
mUsingSpdy = true;
mEverUsedSpdy = true;
mIsReused = true; /* all spdy streams are reused */
// Wrap the old http transaction into the new spdy session
// as the first stream
mSpdySession = new SpdySession(mTransaction,
mSocketTransport,
mPriority);
mTransaction = mSpdySession;
mIdleTimeout = gHttpHandler->SpdyTimeout();
}
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_CONNECT,
mUsingSpdy);
npnComplete:
LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true"));
mNPNComplete = true;
return true;
}
// called on the socket thread
nsresult
nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps, PRInt32 pri)
{
nsresult rv;
@ -151,6 +250,10 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
LOG(("nsHttpConnection::Activate [this=%x trans=%x caps=%x]\n",
this, trans, caps));
mPriority = pri;
if (mTransaction && mUsingSpdy)
return AddTransaction(trans, pri);
NS_ENSURE_ARG_POINTER(trans);
NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
@ -166,6 +269,8 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
mCallbackTarget = callbackTarget;
}
SetupNPN(caps); // only for spdy
// take ownership of the transaction
mTransaction = trans;
@ -198,6 +303,95 @@ failed_activation:
return rv;
}
void
nsHttpConnection::SetupNPN(PRUint8 caps)
{
if (mSetupNPNCalled) /* do only once */
return;
mSetupNPNCalled = true;
// Setup NPN Negotiation if necessary (only for SPDY)
if (!mNPNComplete) {
mNPNComplete = true;
if (mConnInfo->UsingSSL() &&
!(caps & NS_HTTP_DISALLOW_SPDY) &&
!mConnInfo->UsingHttpProxy() &&
gHttpHandler->IsSpdyEnabled()) {
LOG(("nsHttpConnection::Init Setting up SPDY Negotiation"));
nsCOMPtr<nsISupports> securityInfo;
nsresult rv =
mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
if (NS_FAILED(rv))
return;
nsCOMPtr<nsISSLSocketControl> ssl =
do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv))
return;
nsTArray<nsCString> protocolArray;
protocolArray.AppendElement(NS_LITERAL_CSTRING("spdy/2"));
protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
mNPNComplete = false;
}
}
}
}
void
nsHttpConnection::HandleAlternateProtocol(nsHttpResponseHead *responseHead)
{
// Look for the Alternate-Protocol header. Alternate-Protocol is
// essentially a way to rediect future transactions from http to
// spdy.
//
if (!gHttpHandler->IsSpdyEnabled() || mUsingSpdy)
return;
const char *val = responseHead->PeekHeader(nsHttp::Alternate_Protocol);
if (!val)
return;
// The spec allows redirections to any port, but due to concerns over
// silently redirecting to stealth ports we only allow port 443
//
// Alternate-Protocol: 5678:somethingelse, 443:npn-spdy/2
if (nsHttp::FindToken(val, "443:npn-spdy/2", HTTP_HEADER_VALUE_SEPS)) {
LOG(("Connection %p Transaction %p found Alternate-Protocol "
"header %s", this, mTransaction.get(), val));
gHttpHandler->ConnMgr()->ReportSpdyAlternateProtocol(this);
}
}
nsresult
nsHttpConnection::AddTransaction(nsAHttpTransaction *httpTransaction,
PRInt32 priority)
{
LOG(("nsHttpConnection::AddTransaction for SPDY"));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(mSpdySession && mUsingSpdy,
"AddTransaction to live http connection without spdy");
NS_ABORT_IF_FALSE(mTransaction,
"AddTransaction to idle http connection");
if (!mSpdySession->AddStream(httpTransaction, priority)) {
NS_ABORT_IF_FALSE(0, "AddStream should never fail due to"
"RoomForMore() admission check");
return NS_ERROR_FAILURE;
}
ResumeSend(httpTransaction);
return NS_OK;
}
void
nsHttpConnection::Close(nsresult reason)
{
@ -237,13 +431,30 @@ nsHttpConnection::ProxyStartSSL()
return ssl->ProxyStartSSL();
}
void
nsHttpConnection::DontReuse()
{
mKeepAliveMask = false;
mKeepAlive = false;
mIdleTimeout = 0;
if (mUsingSpdy)
mSpdySession->DontReuse();
}
bool
nsHttpConnection::CanReuse()
{
bool canReuse = IsKeepAlive() &&
bool canReuse;
if (mUsingSpdy)
canReuse = mSpdySession->CanReuse();
else
canReuse = IsKeepAlive();
canReuse = canReuse &&
(NowInSeconds() - mLastReadTime < mIdleTimeout) &&
IsAlive();
// An idle persistent connection should not have data waiting to be read
// before a request is sent. Data here is likely a 408 timeout response
// which we would deal with later on through the restart logic, but that
@ -251,7 +462,7 @@ nsHttpConnection::CanReuse()
// be removed with fixing of 631801
PRUint32 dataSize;
if (canReuse && mSocketIn && !mConnInfo->UsingSSL() &&
if (canReuse && mSocketIn && !mConnInfo->UsingSSL() && !mUsingSpdy &&
NS_SUCCEEDED(mSocketIn->Available(&dataSize)) && dataSize) {
LOG(("nsHttpConnection::CanReuse %p %s"
"Socket not reusable because read data pending (%d) on it.\n",
@ -261,6 +472,16 @@ nsHttpConnection::CanReuse()
return canReuse;
}
bool
nsHttpConnection::CanDirectlyActivate()
{
// return true if a new transaction can be addded to ths connection at any
// time through Activate(). In practice this means this is a healthy SPDY
// connection with room for more concurrent streams.
return UsingSpdy() && CanReuse() && mSpdySession->RoomForMoreStreams();
}
PRUint32 nsHttpConnection::TimeToLive()
{
PRInt32 tmp = mIdleTimeout - (NowInSeconds() - mLastReadTime);
@ -276,6 +497,10 @@ nsHttpConnection::IsAlive()
if (!mSocketTransport)
return false;
// SocketTransport::IsAlive can run the SSL state machine, so make sure
// the NPN options are set before that happens.
SetupNPN(0);
bool alive;
nsresult rv = mSocketTransport->IsAlive(&alive);
if (NS_FAILED(rv))
@ -295,6 +520,10 @@ nsHttpConnection::IsAlive()
bool
nsHttpConnection::SupportsPipelining(nsHttpResponseHead *responseHead)
{
// SPDY supports infinite parallelism, so no need to pipeline.
if (mUsingSpdy)
return false;
// XXX there should be a strict mode available that disables this
// blacklisting.
@ -420,20 +649,30 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
if (mKeepAlive) {
val = responseHead->PeekHeader(nsHttp::Keep_Alive);
const char *cp = PL_strcasestr(val, "timeout=");
if (cp)
mIdleTimeout = (PRUint32) atoi(cp + 8);
else
mIdleTimeout = gHttpHandler->IdleTimeout();
if (!mUsingSpdy) {
const char *cp = PL_strcasestr(val, "timeout=");
if (cp)
mIdleTimeout = (PRUint32) atoi(cp + 8);
else
mIdleTimeout = gHttpHandler->IdleTimeout();
}
else {
mIdleTimeout = gHttpHandler->SpdyTimeout();
}
LOG(("Connection can be reused [this=%x idle-timeout=%u]\n", this, mIdleTimeout));
}
if (!mProxyConnectStream)
HandleAlternateProtocol(responseHead);
// if we're doing an SSL proxy connect, then we need to check whether or not
// the connect was successful. if so, then we have to reset the transaction
// and step-up the socket connection to SSL. finally, we have to wake up the
// socket write request.
if (mProxyConnectStream) {
NS_ABORT_IF_FALSE(!mUsingSpdy,
"SPDY NPN Complete while using proxy connect stream");
mProxyConnectStream = 0;
if (responseHead->Status() == 200) {
LOG(("proxy CONNECT succeeded! ssl=%s\n",
@ -506,6 +745,8 @@ nsHttpConnection::TakeTransport(nsISocketTransport **aTransport,
nsIAsyncInputStream **aInputStream,
nsIAsyncOutputStream **aOutputStream)
{
if (mUsingSpdy)
return NS_ERROR_FAILURE;
if (mTransaction && !mTransaction->IsDone())
return NS_ERROR_IN_PROGRESS;
if (!(mSocketTransport && mSocketIn && mSocketOut))
@ -553,7 +794,7 @@ nsHttpConnection::PushBack(const char *data, PRUint32 length)
}
nsresult
nsHttpConnection::ResumeSend()
nsHttpConnection::ResumeSend(nsAHttpTransaction *)
{
LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this));
@ -567,7 +808,7 @@ nsHttpConnection::ResumeSend()
}
nsresult
nsHttpConnection::ResumeRecv()
nsHttpConnection::ResumeRecv(nsAHttpTransaction *)
{
LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this));
@ -586,7 +827,8 @@ nsHttpConnection::BeginIdleMonitoring()
LOG(("nsHttpConnection::BeginIdleMonitoring [this=%p]\n", this));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(!mTransaction, "BeginIdleMonitoring() while active");
NS_ABORT_IF_FALSE(!mUsingSpdy, "Idle monitoring of spdy not allowed");
LOG(("Entering Idle Monitoring Mode [this=%p]", this));
mIdleMonitoring = true;
if (mSocketIn)
@ -628,6 +870,15 @@ nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
if (reason == NS_BASE_STREAM_CLOSED)
reason = NS_OK;
if (mUsingSpdy) {
DontReuse();
// if !mSpdySession then mUsingSpdy must be false for canreuse()
mUsingSpdy = false;
mSpdySession = nsnull;
}
mHttp1xTransactionCount += mTransaction->Http1xTransactionCount();
mTransaction->Close(reason);
mTransaction = nsnull;
@ -692,6 +943,8 @@ nsHttpConnection::OnSocketWritable()
bool again = true;
do {
mSocketOutCondition = NS_OK;
// if we're doing an SSL proxy connect, then we need to bypass calling
// into the transaction.
//
@ -705,7 +958,26 @@ nsHttpConnection::OnSocketWritable()
nsIOService::gDefaultSegmentSize,
&n);
}
else if (!EnsureNPNComplete()) {
// When SPDY is disabled this branch is not executed because Activate()
// sets mNPNComplete to true in that case.
// We are ready to proceed with SSL but the handshake is not done.
// When using NPN to negotiate between HTTPS and SPDY, we need to
// see the results of the handshake to know what bytes to send, so
// we cannot proceed with the request headers.
rv = NS_OK;
mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
n = 0;
}
else {
if (gHttpHandler->IsSpdyEnabled() && !mReportedSpdy) {
mReportedSpdy = true;
gHttpHandler->ConnMgr()->
ReportSpdyConnection(this, mUsingSpdy);
}
LOG((" writing transaction request stream\n"));
rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize, &n);
}
@ -808,6 +1080,7 @@ nsHttpConnection::OnSocketReadable()
}
else {
mCurrentBytesRead += n;
mTotalBytesRead += n;
if (NS_FAILED(mSocketInCondition)) {
// continue waiting for the socket if necessary...
if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK)
@ -831,6 +1104,8 @@ nsHttpConnection::SetupProxyConnect()
LOG(("nsHttpConnection::SetupProxyConnect [this=%x]\n", this));
NS_ENSURE_TRUE(!mProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED);
NS_ABORT_IF_FALSE(!mUsingSpdy,
"SPDY NPN Complete while using proxy connect stream");
nsCAutoString buf;
nsresult rv = nsHttpHandler::GenerateHostPort(

Просмотреть файл

@ -47,6 +47,7 @@
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "prinrval.h"
#include "SpdySession.h"
#include "nsIStreamListener.h"
#include "nsISocketTransport.h"
@ -92,8 +93,9 @@ public:
nsIEventTarget *);
// Activate causes the given transaction to be processed on this
// connection. It fails if there is already an existing transaction.
nsresult Activate(nsAHttpTransaction *, PRUint8 caps);
// connection. It fails if there is already an existing transaction unless
// a multiplexing protocol such as SPDY is being used
nsresult Activate(nsAHttpTransaction *, PRUint8 caps, PRInt32 pri);
// Close the underlying socket transport.
void Close(nsresult reason);
@ -102,15 +104,15 @@ public:
// XXX document when these are ok to call
bool SupportsPipelining() { return mSupportsPipelining; }
bool IsKeepAlive() { return mKeepAliveMask && mKeepAlive; }
bool IsKeepAlive() { return mUsingSpdy ||
(mKeepAliveMask && mKeepAlive); }
bool CanReuse(); // can this connection be reused?
bool CanDirectlyActivate();
// Returns time in seconds for how long connection can be reused.
PRUint32 TimeToLive();
void DontReuse() { mKeepAliveMask = false;
mKeepAlive = false;
mIdleTimeout = 0; }
void DontReuse();
void DropTransport() { DontReuse(); mSocketTransport = 0; }
bool LastTransactionExpectedNoContent()
@ -140,8 +142,8 @@ public:
void SetIsReusedAfter(PRUint32 afterMilliseconds);
void SetIdleTimeout(PRUint16 val) {mIdleTimeout = val;}
nsresult PushBack(const char *data, PRUint32 length);
nsresult ResumeSend();
nsresult ResumeRecv();
nsresult ResumeSend(nsAHttpTransaction *caller);
nsresult ResumeRecv(nsAHttpTransaction *caller);
PRInt64 MaxBytesRead() {return mMaxBytesRead;}
static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *,
@ -154,6 +156,8 @@ public:
void BeginIdleMonitoring();
void EndIdleMonitoring();
bool UsingSpdy() { return mUsingSpdy; }
private:
// called to cause the underlying socket to start speaking SSL
nsresult ProxyStartSSL();
@ -167,6 +171,18 @@ private:
bool IsAlive();
bool SupportsPipelining(nsHttpResponseHead *);
// Makes certain the SSL handshake is complete and NPN negotiation
// has had a chance to happen
bool EnsureNPNComplete();
void SetupNPN(PRUint8 caps);
// Inform the connection manager of any SPDY Alternate-Protocol
// redirections
void HandleAlternateProtocol(nsHttpResponseHead *);
// Directly Add a transaction to an active connection for SPDY
nsresult AddTransaction(nsAHttpTransaction *, PRInt32);
private:
nsCOMPtr<nsISocketTransport> mSocketTransport;
nsCOMPtr<nsIAsyncInputStream> mSocketIn;
@ -194,6 +210,7 @@ private:
PRIntervalTime mConsiderReusedAfterEpoch;
PRInt64 mCurrentBytesRead; // data read per activation
PRInt64 mMaxBytesRead; // max read in 1 activation
PRInt64 mTotalBytesRead; // total data read
nsRefPtr<nsIAsyncInputStream> mInputOverflow;
@ -204,6 +221,21 @@ private:
bool mCompletedProxyConnect;
bool mLastTransactionExpectedNoContent;
bool mIdleMonitoring;
// The number of <= HTTP/1.1 transactions performed on this connection. This
// excludes spdy transactions.
PRUint32 mHttp1xTransactionCount;
// SPDY related
bool mNPNComplete;
bool mSetupNPNCalled;
bool mUsingSpdy;
nsRefPtr<mozilla::net::SpdySession> mSpdySession;
PRInt32 mPriority;
bool mReportedSpdy;
// mUsingSpdy is cleared when mSpdySession is freed, this is permanent
bool mEverUsedSpdy;
};
#endif // nsHttpConnection_h__

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше