зеркало из https://github.com/mozilla/pjs.git
Merge m-c to s-c.
This commit is contained in:
Коммит
7b7f96fa9b
|
@ -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 = ../../../..
|
||||
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,7 +221,8 @@ var gEditItemOverlay = {
|
|||
this._allTags = this._getCommonTags();
|
||||
this._initTextField("tagsField", this._allTags.join(", "), false);
|
||||
this._element("itemsCountText").value =
|
||||
PlacesUIUtils.getFormattedString("detailsPane.multipleItems",
|
||||
PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
|
||||
this._itemIds.length,
|
||||
[this._itemIds.length]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
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;
|
||||
|
||||
if (win) {
|
||||
var homePage = document.getElementById("browser.startup.homepage");
|
||||
var tabs = win.gBrowser.visibleTabs;
|
||||
let homePage = document.getElementById("browser.startup.homepage");
|
||||
let tabs = this._getTabsForHomePage();
|
||||
function getTabURI(t) t.linkedBrowser.currentURI.spec;
|
||||
|
||||
// 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) {
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function() {
|
||||
win.Scratchpad.removeObserver(this);
|
||||
|
||||
let state = win.Scratchpad.getState();
|
||||
win.close();
|
||||
addState(state);
|
||||
},
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -785,6 +792,8 @@ var Scratchpad = {
|
|||
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();
|
||||
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
openScratchpad(function(aWin, aScratchpad) {
|
||||
aScratchpad.setText("new text");
|
||||
ok(!isStar(win), "no star if scratchpad isn't from a file");
|
||||
ok(!isStar(aWin), "no star if scratchpad isn't from a file");
|
||||
|
||||
aScratchpad.onTextSaved();
|
||||
ok(!isStar(win), "no star before changing text");
|
||||
ok(!isStar(aWin), "no star before changing text");
|
||||
|
||||
aScratchpad.setText("new text2");
|
||||
ok(isStar(win), "shows star if scratchpad text changes");
|
||||
ok(isStar(aWin), "shows star if scratchpad text changes");
|
||||
|
||||
aScratchpad.onTextSaved();
|
||||
ok(!isStar(win), "no star if scratchpad was just saved");
|
||||
ok(!isStar(aWin), "no star if scratchpad was just saved");
|
||||
|
||||
aScratchpad.undo();
|
||||
ok(isStar(win), "star if scratchpad undo");
|
||||
ok(isStar(aWin), "star if scratchpad undo");
|
||||
|
||||
win.close();
|
||||
aWin.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
||||
function testErrorStatus()
|
||||
{
|
||||
let win = ScratchpadManager.openScratchpad();
|
||||
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
openScratchpad(function(aWin, aScratchpad) {
|
||||
aScratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
|
||||
aScratchpad.setText("new text");
|
||||
ok(!isStar(win), "no star if file save failed");
|
||||
ok(!isStar(aWin), "no star if file save failed");
|
||||
|
||||
win.close();
|
||||
aWin.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
||||
|
||||
|
@ -92,21 +72,13 @@ function testRestoreNotFromFile()
|
|||
}];
|
||||
|
||||
let [win] = ScratchpadManager.restoreSession(session);
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
openScratchpad(function(aWin, aScratchpad) {
|
||||
aScratchpad.setText("new text");
|
||||
ok(!isStar(win), "no star if restored scratchpad isn't from a file");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}, {window: win, noFocus: true});
|
||||
}
|
||||
|
||||
function testRestoreFromFileSaved()
|
||||
|
@ -119,13 +91,7 @@ function testRestoreFromFileSaved()
|
|||
}];
|
||||
|
||||
let [win] = ScratchpadManager.restoreSession(session);
|
||||
win.addEventListener("load", function onScratchpadLoad() {
|
||||
win.removeEventListener("load", onScratchpadLoad, false);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
openScratchpad(function(aWin, aScratchpad) {
|
||||
ok(!isStar(win), "no star before changing text in scratchpad restored from file");
|
||||
|
||||
aScratchpad.setText("new text");
|
||||
|
@ -133,9 +99,7 @@ function testRestoreFromFileSaved()
|
|||
|
||||
win.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}, {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);
|
||||
|
||||
win.Scratchpad.addObserver({
|
||||
onReady: function (aScratchpad) {
|
||||
aScratchpad.removeObserver(this);
|
||||
|
||||
openScratchpad(function() {
|
||||
ok(isStar(win), "star with scratchpad restored with unsaved text");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}, {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,9 +2,29 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
.viewContainer {
|
||||
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;
|
||||
|
@ -14,3 +34,92 @@
|
|||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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,14 +5439,11 @@ 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.
|
||||
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");
|
||||
|
@ -168,6 +174,9 @@ function testReturnKey()
|
|||
|
||||
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");
|
||||
|
|
|
@ -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@
|
||||
|
|
23
configure.in
23
configure.in
|
@ -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 \
|
||||
ScaledFontSkia.cpp \
|
||||
$(NULL)
|
||||
|
||||
DEFINES += -DUSE_SKIA
|
||||
|
||||
endif
|
||||
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
ifdef MOZ_ENABLE_SKIA
|
||||
CPPSRCS += \
|
||||
ScaledFontMac.cpp \
|
||||
$(NULL)
|
||||
DEFINES += -DUSE_SKIA
|
||||
|
||||
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() {
|
||||
|
@ -1436,6 +1431,31 @@ abstract public class GeckoApp
|
|||
registerReceiver(mSmsReceiver, smsFilter);
|
||||
|
||||
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,10 +431,27 @@ 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();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
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,6 +827,7 @@ 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;
|
||||
|
@ -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__
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче