Merge mozilla-central into mozilla-inbound.

This commit is contained in:
Mounir Lamouri 2012-02-09 11:31:59 +01:00
Родитель 8f5539d3cd 64d4bdb205
Коммит be36ebc4ba
16 изменённых файлов: 165 добавлений и 911 удалений

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

@ -1100,12 +1100,9 @@ pref("devtools.editor.expandtab", true);
// Tells which component you want to use for source editing in developer tools.
//
// Available components:
// "textarea" - this is a basic text editor, like an HTML <textarea>.
//
// "orion" - this is the Orion source code editor from the Eclipse project. It
// provides programmer-specific editor features such as syntax highlighting,
// indenting and bracket recognition. It may not be appropriate for all
// locales (esp. RTL) or a11y situations.
// indenting and bracket recognition.
pref("devtools.editor.component", "orion");
// Whether the character encoding menu is under the main Firefox button. This

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

@ -86,7 +86,8 @@ var gLastValidURLStr = "";
var gInPrintPreviewMode = false;
var gDownloadMgr = null;
var gContextMenu = null; // nsContextMenu instance
var gDelayedStartupTimeoutId;
var gDelayedStartupTimeoutId; // used for non-browser-windows
var gFirstPaintListener = null;
var gStartupRan = false;
#ifndef XP_MACOSX
@ -1383,7 +1384,18 @@ function BrowserStartup() {
retrieveToolbarIconsizesFromTheme();
gDelayedStartupTimeoutId = setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
// Listen for the first paint event for this window, and only do non-critical
// work then, so that things like session restore don't block the window from
// being visible.
gFirstPaintListener = function(e) {
if (e.target == window) {
window.removeEventListener("MozAfterPaint", gFirstPaintListener, false);
gFirstPaintListener = null;
delayedStartup(isLoadingBlank, mustLoadSidebar);
}
};
window.addEventListener("MozAfterPaint", gFirstPaintListener, false);
gStartupRan = true;
}
@ -1514,7 +1526,6 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
Cu.import("resource:///modules/TelemetryTimestamps.jsm", tmp);
let TelemetryTimestamps = tmp.TelemetryTimestamps;
TelemetryTimestamps.add("delayedStartupStarted");
gDelayedStartupTimeoutId = null;
Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
@ -1867,8 +1878,9 @@ function BrowserShutdown() {
// Now either cancel delayedStartup, or clean up the services initialized from
// it.
if (gDelayedStartupTimeoutId) {
clearTimeout(gDelayedStartupTimeoutId);
if (gFirstPaintListener) {
window.removeEventListener("MozAfterPaint", gFirstPaintListener, false);
gFirstPaintListener = null;
} else {
if (Win7Features)
Win7Features.onCloseWindow();

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

@ -87,10 +87,11 @@ let gPage = {
// Initialize the drop target shim.
gDropTargetShim.init();
#ifdef XP_MACOSX
// Workaround to prevent a delay on MacOSX due to a slow drop animation.
let doc = document.documentElement;
doc.addEventListener("dragover", this.onDragOver, false);
doc.addEventListener("drop", this.onDrop, false);
document.addEventListener("dragover", this.onDragOver, false);
document.addEventListener("drop", this.onDrop, false);
#endif
}.bind(this));
},
@ -155,7 +156,7 @@ let gPage = {
* @param aEvent The 'dragover' event.
*/
onDragOver: function Page_onDragOver(aEvent) {
if (gDrag.isValid(aEvent))
if (gDrag.isValid(aEvent) && gDrag.draggedSite)
aEvent.preventDefault();
},
@ -165,7 +166,7 @@ let gPage = {
* @param aEvent The 'drop' event.
*/
onDrop: function Page_onDrop(aEvent) {
if (gDrag.isValid(aEvent)) {
if (gDrag.isValid(aEvent) && gDrag.draggedSite) {
aEvent.preventDefault();
aEvent.stopPropagation();
}

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

@ -322,15 +322,19 @@ function newWindowWithState(state, callback) {
callback(win);
};
whenDelayedStartupFinished(win, function () {
ss.setWindowState(win, JSON.stringify(state), true);
win.close();
win = ss.undoCloseWindow(0);
whenWindowLoaded(win, function () {
whenWindowStateReady(win, function () {
afterAllTabsLoaded(check, win);
});
ss.setWindowState(win, JSON.stringify(state), true);
});
whenDelayedStartupFinished(win, check);
});
}
// ----------

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

@ -49,7 +49,6 @@ TEST_DIRS += test
EXTRA_JS_MODULES = \
source-editor.jsm \
source-editor-orion.jsm \
source-editor-textarea.jsm \
source-editor-ui.jsm \
$(NULL)

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

@ -1,879 +0,0 @@
/* vim:set ft=javascript ts=2 sw=2 sts=2 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 the Source Editor component (textarea fallback).
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mihai Sucan <mihai.sucan@gmail.com> (original author)
* Kenny Heaton <kennyheaton@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 *****/
"use strict";
const Cu = Components.utils;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/source-editor-ui.jsm");
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
/**
* Default key bindings in the textarea editor.
*/
const DEFAULT_KEYBINDINGS = [
{
_action: "_doTab",
keyCode: Ci.nsIDOMKeyEvent.DOM_VK_TAB,
shiftKey: false,
accelKey: false,
altKey: false,
},
];
var EXPORTED_SYMBOLS = ["SourceEditor"];
/**
* The SourceEditor object constructor. The SourceEditor component allows you to
* provide users with an editor tailored to the specific needs of editing source
* code, aimed primarily at web developers.
*
* The editor used here is a simple textarea. This is used as a fallback
* mechanism for when the user disables the code editor feature.
*
* @constructor
*/
function SourceEditor() {
// Update the SourceEditor defaults from user preferences.
SourceEditor.DEFAULTS.TAB_SIZE =
Services.prefs.getIntPref(SourceEditor.PREFS.TAB_SIZE);
SourceEditor.DEFAULTS.EXPAND_TAB =
Services.prefs.getBoolPref(SourceEditor.PREFS.EXPAND_TAB);
this._listeners = {};
this._lastSelection = {};
this.ui = new SourceEditorUI(this);
}
SourceEditor.prototype = {
_textbox: null,
_editor: null,
_listeners: null,
_lineDelimiter: null,
_editActionListener: null,
_expandTab: null,
_tabSize: null,
/**
* The Source Editor user interface manager.
* @type object
* An instance of the SourceEditorUI.
*/
ui: null,
/**
* The editor container element.
* @type nsIDOMElement
*/
parentElement: null,
/**
* Initialize the editor.
*
* @param nsIDOMElement aElement
* The DOM element where you want the editor to show.
* @param object aConfig
* Editor configuration object. Properties:
* - placeholderText - the text you want to be shown by default.
* - mode - the editor mode, based on the file type you want to edit.
* You can use one of the predefined modes.
* - tabSize - define how many spaces to use for a tab character.
* - expandTab - tells if you want tab characters to be expanded to
* spaces.
* - readOnly - make the editor read only.
* - undoLimit - how many steps should the undo stack hold.
* @param function [aCallback]
* Function you want to execute once the editor is loaded and
* initialized.
*/
init: function SE_init(aElement, aConfig, aCallback)
{
if (this._textbox) {
throw new Error("SourceEditor is already initialized!");
}
let doc = aElement.ownerDocument;
let win = doc.defaultView;
this._textbox = doc.createElementNS(XUL_NS, "textbox");
this._textbox.flex = 1;
this._textbox.setAttribute("multiline", true);
this._textbox.setAttribute("dir", "ltr");
aElement.appendChild(this._textbox);
this.parentElement = aElement;
this._editor = this._textbox.editor;
this._expandTab = aConfig.expandTab !== undefined ?
aConfig.expandTab : SourceEditor.DEFAULTS.EXPAND_TAB;
this._tabSize = aConfig.tabSize || SourceEditor.DEFAULTS.TAB_SIZE;
this._textbox.style.MozTabSize = this._tabSize;
this._textbox.setAttribute("value", aConfig.placeholderText || "");
this._textbox.setAttribute("class", "monospace");
this._textbox.style.direction = "ltr";
this._textbox.readOnly = aConfig.readOnly;
// Make sure that the SourceEditor Selection events are fired properly.
// Also make sure that the configured keyboard bindings work.
this._textbox.addEventListener("select", this._onSelect.bind(this), false);
this._textbox.addEventListener("keypress", this._onKeyPress.bind(this), false);
this._textbox.addEventListener("keyup", this._onSelect.bind(this), false);
this._textbox.addEventListener("click", this._onSelect.bind(this), false);
// Mimic the mode change.
this.setMode(aConfig.mode || SourceEditor.DEFAULTS.MODE);
this._editor.transactionManager.maxTransactionCount =
aConfig.undoLimit || SourceEditor.DEFAULTS.UNDO_LIMIT;
// Make sure that the transactions stack is clean.
this.resetUndo();
// Add the edit action listener so we can fire the SourceEditor TextChanged
// events.
this._editActionListener = new EditActionListener(this);
this._editor.addEditActionListener(this._editActionListener);
this._lineDelimiter = win.navigator.platform.indexOf("Win") > -1 ?
"\r\n" : "\n";
this._config = aConfig;
for each (let key in DEFAULT_KEYBINDINGS) {
for (let prop in key) {
if (prop == "accelKey") {
let newProp = Services.appinfo.OS == "Darwin" ? "metaKey" : "ctrlKey";
key[newProp] = key[prop];
delete key[prop];
break;
}
}
}
this.ui.init();
this.ui.onReady();
if (aCallback) {
aCallback(this);
}
},
/**
* The textbox keypress event handler calls the configured action for keyboard
* event.
*
* @private
* @param nsIDOMEvent aEvent
* The DOM object for the event.
* @see DEFAULT_KEYBINDINGS
*/
_onKeyPress: function SE__onKeyPress(aEvent)
{
for each (let key in DEFAULT_KEYBINDINGS) {
let matched = true;
for (let prop in key) {
if (prop.charAt(0) != "_" && aEvent[prop] !== key[prop]) {
matched = false;
break;
}
}
if (matched) {
let context = key._context ? this[key._context] : this;
context[key._action].call(context);
aEvent.preventDefault();
break;
}
}
},
/**
* The Tab keypress event handler. This allows the user to indent the code
* with spaces, when expandTab is true.
*/
_doTab: function SE__doTab()
{
let selection = this.getSelection();
let caret = selection.start;
let indent = "\t";
if (this._expandTab) {
let text = this._textbox.value;
let lineStart = caret;
while (lineStart > 0) {
let c = text.charAt(lineStart - 1);
if (c == "\r" || c == "\n") {
break;
}
lineStart--;
}
let offset = caret - lineStart;
let spaces = this._tabSize - (offset % this._tabSize);
indent = (new Array(spaces + 1)).join(" ");
}
this.setText(indent, selection.start, selection.end);
this.setCaretOffset(selection.start + indent.length);
},
/**
* The textbox keyup, click and select event handler tracks selection
* changes. This method invokes the SourceEditor Selection event handlers.
*
* @see SourceEditor.EVENTS.SELECTION
* @private
*/
_onSelect: function SE__onSelect()
{
let selection = this.getSelection();
selection.collapsed = selection.start == selection.end;
if (selection.collapsed && this._lastSelection.collapsed) {
this._lastSelection = selection;
return; // just a cursor move.
}
if (this._lastSelection.start != selection.start ||
this._lastSelection.end != selection.end) {
let sendEvent = {
oldValue: {start: this._lastSelection.start,
end: this._lastSelection.end},
newValue: {start: selection.start, end: selection.end},
};
let listeners = this._listeners[SourceEditor.EVENTS.SELECTION] || [];
listeners.forEach(function(aListener) {
aListener.callback.call(null, sendEvent);
}, this);
this._lastSelection = selection;
}
},
/**
* The TextChanged event dispatcher. This method is called when a change in
* the text occurs. All of the SourceEditor TextChanged event handlers are
* notified about the lower level change.
*
* @see SourceEditor.EVENTS.TEXT_CHANGED
* @see EditActionListener
*
* @private
*
* @param object aEvent
* The TextChanged event object that is going to be sent to the
* SourceEditor event handlers.
*/
_onTextChanged: function SE__onTextChanged(aEvent)
{
let listeners = this._listeners[SourceEditor.EVENTS.TEXT_CHANGED] || [];
listeners.forEach(function(aListener) {
aListener.callback.call(null, aEvent);
}, this);
},
/**
* Get the editor element.
*
* @return nsIDOMElement
* In this implementation a xul:textbox is returned.
*/
get editorElement() {
return this._textbox;
},
/**
* Add an event listener to the editor. You can use one of the known events.
*
* @see SourceEditor.EVENTS
*
* @param string aEventType
* The event type you want to listen for.
* @param function aCallback
* The function you want executed when the event is triggered.
*/
addEventListener:
function SE_addEventListener(aEventType, aCallback)
{
const EVENTS = SourceEditor.EVENTS;
let listener = {
type: aEventType,
callback: aCallback,
};
if (aEventType == EVENTS.CONTEXT_MENU) {
listener.domType = "contextmenu";
listener.target = this._textbox;
listener.handler = this._onContextMenu.bind(this, listener);
listener.target.addEventListener(listener.domType, listener.handler, false);
}
if (!(aEventType in this._listeners)) {
this._listeners[aEventType] = [];
}
this._listeners[aEventType].push(listener);
},
/**
* Remove an event listener from the editor. You can use one of the known
* events.
*
* @see SourceEditor.EVENTS
*
* @param string aEventType
* The event type you have a listener for.
* @param function aCallback
* The function you have as the event handler.
*/
removeEventListener:
function SE_removeEventListener(aEventType, aCallback)
{
let listeners = this._listeners[aEventType];
if (!listeners) {
return;
}
const EVENTS = SourceEditor.EVENTS;
this._listeners[aEventType] = listeners.filter(function(aListener) {
let isSameListener = aListener.type == aEventType &&
aListener.callback === aCallback;
if (isSameListener && aListener.domType) {
aListener.target.removeEventListener(aListener.domType,
aListener.handler, false);
}
return !isSameListener;
}, this);
},
/**
* The xul:textbox contextmenu event handler. This is used a wrapper for each
* contextmenu event listener added by the SourceEditor client.
*
* @param object aListener
* The object that holds listener information, see this._listener and
* this.addEventListener().
* @param nsIDOMEvent aDOMEvent
* The nsIDOMEvent object that triggered the context menu.
*/
_onContextMenu: function SE__onContextMenu(aListener, aDOMEvent)
{
let input = this._textbox.inputField;
let rect = this._textbox.getBoundingClientRect();
// Prepare the event object we send to the event handler.
let sendEvent = {
x: aDOMEvent.clientX - rect.left + input.scrollLeft,
y: aDOMEvent.clientY - rect.top + input.scrollTop,
screenX: aDOMEvent.screenX,
screenY: aDOMEvent.screenY,
};
aDOMEvent.preventDefault();
aListener.callback.call(null, sendEvent);
},
/**
* Undo a change in the editor.
*/
undo: function SE_undo()
{
this._editor.undo(1);
},
/**
* Redo a change in the editor.
*/
redo: function SE_redo()
{
this._editor.redo(1);
},
/**
* Check if there are changes that can be undone.
*
* @return boolean
* True if there are changes that can be undone, false otherwise.
*/
canUndo: function SE_canUndo()
{
let isEnabled = {};
let canUndo = {};
this._editor.canUndo(isEnabled, canUndo);
return canUndo.value;
},
/**
* Check if there are changes that can be repeated.
*
* @return boolean
* True if there are changes that can be repeated, false otherwise.
*/
canRedo: function SE_canRedo()
{
let isEnabled = {};
let canRedo = {};
this._editor.canRedo(isEnabled, canRedo);
return canRedo.value;
},
/**
* Reset the Undo stack
*/
resetUndo: function SE_resetUndo()
{
this._editor.transactionManager.clear();
this._editor.resetModificationCount();
},
/**
* Start a compound change in the editor. Compound changes are grouped into
* only one change that you can undo later, after you invoke
* endCompoundChange().
*/
startCompoundChange: function SE_startCompoundChange()
{
this._editor.beginTransaction();
},
/**
* End a compound change in the editor.
*/
endCompoundChange: function SE_endCompoundChange()
{
this._editor.endTransaction();
},
/**
* Focus the editor.
*/
focus: function SE_focus()
{
this._textbox.focus();
},
/**
* Check if the editor has focus.
*
* @return boolean
* True if the editor is focused, false otherwise.
*/
hasFocus: function SE_hasFocus()
{
return this._textbox.ownerDocument.activeElement ===
this._textbox.inputField;
},
/**
* Get the editor content, in the given range. If no range is given you get
* the entire editor content.
*
* @param number [aStart=0]
* Optional, start from the given offset.
* @param number [aEnd=content char count]
* Optional, end offset for the text you want. If this parameter is not
* given, then the text returned goes until the end of the editor
* content.
* @return string
* The text in the given range.
*/
getText: function SE_getText(aStart, aEnd)
{
let value = this._textbox.value || "";
if (aStart === undefined || aStart === null) {
aStart = 0;
}
if (aEnd === undefined || aEnd === null) {
aEnd = value.length;
}
return value.substring(aStart, aEnd);
},
/**
* Get the number of characters in the editor content.
*
* @return number
* The number of editor content characters.
*/
getCharCount: function SE_getCharCount()
{
return this._textbox.textLength;
},
/**
* Get the selected text.
*
* @return string
* The currently selected text.
*/
getSelectedText: function SE_getSelectedText()
{
let selection = this.getSelection();
return selection.start != selection.end ?
this.getText(selection.start, selection.end) : "";
},
/**
* Replace text in the source editor with the given text, in the given range.
*
* @param string aText
* The text you want to put into the editor.
* @param number [aStart=0]
* Optional, the start offset, zero based, from where you want to start
* replacing text in the editor.
* @param number [aEnd=char count]
* Optional, the end offset, zero based, where you want to stop
* replacing text in the editor.
*/
setText: function SE_setText(aText, aStart, aEnd)
{
if (aStart === undefined) {
this._textbox.value = aText;
} else {
if (aEnd === undefined) {
aEnd = this._textbox.textLength;
}
let value = this._textbox.value || "";
let removedText = value.substring(aStart, aEnd);
let prefix = value.substr(0, aStart);
let suffix = value.substr(aEnd);
if (suffix) {
this._editActionListener._setTextRangeEvent = {
start: aStart,
removedCharCount: removedText.length,
addedCharCount: aText.length,
};
}
this._textbox.value = prefix + aText + suffix;
}
},
/**
* Drop the current selection / deselect.
*/
dropSelection: function SE_dropSelection()
{
let selection = this._editor.selection;
selection.collapse(selection.focusNode, selection.focusOffset);
this._onSelect();
},
/**
* Select a specific range in the editor.
*
* @param number aStart
* Selection range start.
* @param number aEnd
* Selection range end.
*/
setSelection: function SE_setSelection(aStart, aEnd)
{
this._textbox.setSelectionRange(aStart, aEnd);
this._onSelect();
},
/**
* Get the current selection range.
*
* @return object
* An object with two properties, start and end, that give the
* selection range (zero based offsets).
*/
getSelection: function SE_getSelection()
{
return {
start: this._textbox.selectionStart,
end: this._textbox.selectionEnd
};
},
/**
* Get the current caret offset.
*
* @return number
* The current caret offset.
*/
getCaretOffset: function SE_getCaretOffset()
{
let selection = this.getSelection();
return selection.start < selection.end ?
selection.end : selection.start;
},
/**
* Set the caret offset.
*
* @param number aOffset
* The new caret offset you want to set.
*/
setCaretOffset: function SE_setCaretOffset(aOffset)
{
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 = -1, n = text.length, c0, c1;
let line = 0, col = -1;
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.
*
* @return string
* The line delimiter.
*/
getLineDelimiter: function SE_getLineDelimiter()
{
return this._lineDelimiter;
},
/**
* Set the source editor mode to the file type you are editing.
*
* Note: this implementation makes no difference between any of the available
* modes.
*
* @param string aMode
* One of the predefined SourceEditor.MODES.
*/
setMode: function SE_setMode(aMode)
{
// nothing to do here
},
/**
* Get the current source editor mode.
*
* @return string
* Returns one of the predefined SourceEditor.MODES. In this
* implementation SourceEditor.MODES.TEXT is always returned.
*/
getMode: function SE_getMode()
{
return SourceEditor.MODES.TEXT;
},
/**
* Setter for the read-only state of the editor.
* @param boolean aValue
* Tells if you want the editor to read-only or not.
*/
set readOnly(aValue)
{
this._textbox.readOnly = aValue;
},
/**
* Getter for the read-only state of the editor.
* @type boolean
*/
get readOnly()
{
return this._textbox.readOnly;
},
/**
* Destroy/uninitialize the editor.
*/
destroy: function SE_destroy()
{
for (let eventType in this._listeners) {
this._listeners[eventType].forEach(function(aListener) {
if (aListener.domType) {
aListener.target.removeEventListener(aListener.domType,
aListener.handler, false);
}
}, this);
}
this._editor.removeEditActionListener(this._editActionListener);
this.ui.destroy();
this.ui = null;
this.parentElement.removeChild(this._textbox);
this.parentElement = null;
this._editor = null;
this._textbox = null;
this._config = null;
this._listeners = null;
this._lastSelection = null;
this._editActionListener = null;
this._lastFind = null;
},
};
/**
* The nsIEditActionListener for the nsIEditor of the xul:textbox used by the
* SourceEditor. This listener traces text changes such that SourceEditor
* TextChanged event handlers get their events.
*
* @see
* http://mxr.mozilla.org/mozilla-central/source/editor/idl/nsIEditActionListener.idl
*
* @constructor
* @param object aSourceEditor
* An instance of the SourceEditor to notify when text changes happen.
*/
function EditActionListener(aSourceEditor) {
this._sourceEditor = aSourceEditor;
}
EditActionListener.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIEditActionListener]),
WillCreateNode: function() { },
DidCreateNode: function() { },
WillInsertNode: function() { },
DidInsertNode: function EAL_DidInsertNode(aNode)
{
if (aNode.nodeType != aNode.TEXT_NODE) {
return;
}
let event;
if (this._setTextRangeEvent) {
event = this._setTextRangeEvent;
delete this._setTextRangeEvent;
} else {
event = {
start: 0,
removedCharCount: 0,
addedCharCount: aNode.textContent.length,
};
}
this._sourceEditor._onTextChanged(event);
},
WillDeleteNode: function() { },
DidDeleteNode: function() { },
WillSplitNode: function() { },
DidSplitNode: function() { },
WillJoinNodes: function() { },
DidJoinNodes: function() { },
WillInsertText: function() { },
DidInsertText: function EAL_DidInsertText(aTextNode, aOffset, aString)
{
let event = {
start: aOffset,
removedCharCount: 0,
addedCharCount: aString.length,
};
this._sourceEditor._onTextChanged(event);
},
WillDeleteText: function() { },
DidDeleteText: function EAL_DidDeleteText(aTextNode, aOffset, aLength)
{
let event = {
start: aOffset,
removedCharCount: aLength,
addedCharCount: 0,
};
this._sourceEditor._onTextChanged(event);
},
WillDeleteSelection: function EAL_WillDeleteSelection()
{
if (this._setTextRangeEvent) {
return;
}
let selection = this._sourceEditor.getSelection();
let str = this._sourceEditor.getSelectedText();
let event = {
start: selection.start,
removedCharCount: str.length,
addedCharCount: 0,
};
this._sourceEditor._onTextChanged(event);
},
DidDeleteSelection: function() { },
};

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

@ -51,7 +51,7 @@ var component = Services.prefs.getCharPref(PREF_EDITOR_COMPONENT);
var obj = {};
try {
if (component == "ui") {
throw new Error("The UI editor component is not available.");
throw new Error("The ui editor component is not available.");
}
Cu.import("resource:///modules/source-editor-" + component + ".jsm", obj);
} catch (ex) {

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

@ -111,6 +111,7 @@ function StyleEditor(aDocument, aStyleSheet)
this._styleSheet = aStyleSheet;
this._styleSheetIndex = -1; // unknown for now, will be set after load
this._styleSheetFilePath = null; // original file path for the style sheet
this._loaded = false;
@ -571,6 +572,7 @@ StyleEditor.prototype = {
* @param mixed aFile
* Optional nsIFile or string representing the filename to save in the
* background, no UI will be displayed.
* If not specified, the original style sheet URI is used.
* To implement 'Save' instead of 'Save as', you can pass savedFile here.
* @param function(nsIFile aFile) aCallback
* Optional callback called when the operation has finished.
@ -580,7 +582,8 @@ StyleEditor.prototype = {
*/
saveToFile: function SE_saveToFile(aFile, aCallback)
{
aFile = this._showFilePicker(aFile, true);
aFile = this._showFilePicker(aFile || this._styleSheetFilePath, true);
if (!aFile) {
if (aCallback) {
aCallback(null);
@ -729,6 +732,14 @@ StyleEditor.prototype = {
_showFilePicker: function SE__showFilePicker(aFile, aSave, aParentWindow)
{
if (typeof(aFile) == "string") {
try {
if (Services.io.extractScheme(aFile) == "file") {
let uri = Services.io.newURI(aFile, null, null);
let file = uri.QueryInterface(Ci.nsIFileURL).file;
return file;
}
} catch (ex) {
}
try {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(aFile);
@ -772,6 +783,7 @@ StyleEditor.prototype = {
let scheme = Services.io.extractScheme(this.styleSheet.href);
switch (scheme) {
case "file":
this._styleSheetFilePath = this.styleSheet.href;
case "chrome":
case "resource":
this._loadSourceFromFile(this.styleSheet.href);

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

@ -46,6 +46,7 @@ include $(topsrcdir)/config/rules.mk
_BROWSER_TEST_FILES = \
browser_styleeditor_enabled.js \
browser_styleeditor_filesave.js \
browser_styleeditor_import.js \
browser_styleeditor_init.js \
browser_styleeditor_loading.js \

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

@ -0,0 +1,101 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI_HTML = TEST_BASE + "simple.html";
const TESTCASE_URI_CSS = TEST_BASE + "simple.css";
const Cc = Components.classes;
const Ci = Components.interfaces;
let tempScope = {};
Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
Components.utils.import("resource://gre/modules/NetUtil.jsm", tempScope);
let FileUtils = tempScope.FileUtils;
let NetUtil = tempScope.NetUtil;
function test()
{
waitForExplicitFinish();
copy(TESTCASE_URI_HTML, "simple.html", function(htmlFile) {
copy(TESTCASE_URI_CSS, "simple.css", function(cssFile) {
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onEditorAdded: function (aChrome, aEditor) {
if (aEditor.styleSheetIndex != 0) {
return; // we want to test against the first stylesheet
}
if (aEditor.sourceEditor) {
run(aEditor); // already attached to input element
} else {
aEditor.addActionListener({
onAttach: run
});
}
}
});
});
let uri = Services.io.newFileURI(htmlFile);
let filePath = uri.resolve("");
content.location = filePath;
});
});
}
function run(aEditor)
{
aEditor.saveToFile(null, function (aFile) {
ok(aFile, "file should get saved directly when using a file:// URI");
gChromeWindow.close();
finish();
});
}
function copy(aSrcChromeURL, aDestFileName, aCallback)
{
let destFile = FileUtils.getFile("ProfD", [aDestFileName]);
write(read(aSrcChromeURL), destFile, aCallback);
}
function read(aSrcChromeURL)
{
let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
.getService(Ci.nsIScriptableInputStream);
let channel = Services.io.newChannel(aSrcChromeURL, null, null);
let input = channel.open();
scriptableStream.init(input);
let data = scriptableStream.read(input.available());
scriptableStream.close();
input.close();
return data;
}
function write(aData, aFile, aCallback)
{
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let istream = converter.convertToInputStream(aData);
let ostream = FileUtils.openSafeFileOutputStream(aFile);
NetUtil.asyncCopy(istream, ostream, function(status) {
if (!Components.isSuccessCode(status)) {
info("Coudln't write to " + aFile.path);
return;
}
aCallback(aFile);
});
}

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

@ -1959,6 +1959,7 @@ nsObjectLoadingContent::SyncStartPluginInstance()
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> kungFuURIGrip(mURI);
return InstantiatePluginInstance(mContentType.get(), mURI.get());
}

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

@ -39,7 +39,7 @@ function init()
}
description(
"Tests that glActiveTexture and glBindTexture work as expected" +
"Tests that glActiveTexture and glBindTexture work as expected." +
" Specifically texture targets are per active texture unit.");
var canvas2d = document.getElementById("canvas2d");

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

@ -464,7 +464,12 @@ PluginInstanceParent::AnswerPStreamNotifyConstructor(PStreamNotifyParent* actor,
file, actor);
}
if (!streamDestroyed) {
if (streamDestroyed) {
// If the stream was destroyed, we must return an error code in the
// constructor.
*result = NPERR_GENERIC_ERROR;
}
else {
static_cast<StreamNotifyParent*>(actor)->ClearDestructionFlag();
if (*result != NPERR_NO_ERROR)
return PStreamNotifyParent::Send__delete__(actor,