This commit is contained in:
Richard Newman 2012-06-26 13:00:09 -07:00
Родитель 15f3252a6c 329df2bdf1
Коммит 82c5d3b41b
398 изменённых файлов: 5344 добавлений и 2754 удалений

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

@ -67,7 +67,7 @@
#include "nsNetUtil.h"
#include "nsEventStates.h"
#ifdef NS_DEBUG
#ifdef DEBUG
#include "nsIDOMCharacterData.h"
#endif
@ -799,38 +799,11 @@ Accessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
if (!accessible)
return fallbackAnswer;
if (accessible == this) {
// Manually walk through accessible children and see if the are within this
// point. Skip offscreen or invisible accessibles. This takes care of cases
// where layout won't walk into things for us, such as image map areas and
// sub documents (XXX: subdocuments should be handled by methods of
// OuterDocAccessibles).
PRUint32 childCount = ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
Accessible* child = GetChildAt(childIdx);
PRInt32 childX, childY, childWidth, childHeight;
child->GetBounds(&childX, &childY, &childWidth, &childHeight);
if (aX >= childX && aX < childX + childWidth &&
aY >= childY && aY < childY + childHeight &&
(child->State() & states::INVISIBLE) == 0) {
if (aWhichChild == eDeepestChild)
return child->ChildAtPoint(aX, aY, eDeepestChild);
return child;
}
}
// The point is in this accessible but not in a child. We are allowed to
// return |this| as the answer.
return accessible;
}
// Hurray! We have an accessible for the frame that layout gave us.
// Since DOM node of obtained accessible may be out of flow then we should
// ensure obtained accessible is a child of this accessible.
Accessible* child = accessible;
while (true) {
while (child != this) {
Accessible* parent = child->Parent();
if (!parent) {
// Reached the top of the hierarchy. These bounds were inside an
@ -838,13 +811,37 @@ Accessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
return fallbackAnswer;
}
if (parent == this)
return aWhichChild == eDeepestChild ? accessible : child;
// If we landed on a legitimate child of |this|, and we want the direct
// child, return it here.
if (parent == this && aWhichChild == eDirectChild)
return child;
child = parent;
}
return nsnull;
// Manually walk through accessible children and see if the are within this
// point. Skip offscreen or invisible accessibles. This takes care of cases
// where layout won't walk into things for us, such as image map areas and
// sub documents (XXX: subdocuments should be handled by methods of
// OuterDocAccessibles).
PRUint32 childCount = accessible->ChildCount();
for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
Accessible* child = accessible->GetChildAt(childIdx);
PRInt32 childX, childY, childWidth, childHeight;
child->GetBounds(&childX, &childY, &childWidth, &childHeight);
if (aX >= childX && aX < childX + childWidth &&
aY >= childY && aY < childY + childHeight &&
(child->State() & states::INVISIBLE) == 0) {
if (aWhichChild == eDeepestChild)
return child->ChildAtPoint(aX, aY, eDeepestChild);
return child;
}
}
return accessible;
}
// nsIAccessible getChildAtPoint(in long x, in long y)

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

@ -232,8 +232,9 @@ var AccessFu = {
let presenterContext =
new PresenterContext(position, event.oldAccessible);
let reason = event.reason;
this.presenters.forEach(
function(p) { p.pivotChanged(presenterContext); });
function(p) { p.pivotChanged(presenterContext, reason); });
break;
}

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

@ -39,8 +39,10 @@ Presenter.prototype = {
* The virtual cursor's position changed.
* @param {PresenterContext} aContext the context object for the new pivot
* position.
* @param {int} aReason the reason for the pivot change.
* See nsIAccessiblePivot.
*/
pivotChanged: function pivotChanged(aContext) {},
pivotChanged: function pivotChanged(aContext, aReason) {},
/**
* An object's action has been invoked.
@ -140,7 +142,7 @@ VisualPresenter.prototype = {
this._highlight(this._currentObject);
},
pivotChanged: function VisualPresenter_pivotChanged(aContext) {
pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
this._currentObject = aContext.accessible;
if (!aContext.accessible) {
@ -159,7 +161,7 @@ VisualPresenter.prototype = {
},
tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) {
this.pivotChanged(aVCContext);
this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
},
tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
@ -229,17 +231,53 @@ AndroidPresenter.prototype = {
ANDROID_VIEW_FOCUSED: 0x08,
ANDROID_VIEW_TEXT_CHANGED: 0x10,
ANDROID_WINDOW_STATE_CHANGED: 0x20,
ANDROID_VIEW_HOVER_ENTER: 0x80,
ANDROID_VIEW_HOVER_EXIT: 0x100,
ANDROID_VIEW_SCROLLED: 0x1000,
pivotChanged: function AndroidPresenter_pivotChanged(aContext) {
attach: function AndroidPresenter_attach(aWindow) {
this.chromeWin = aWindow;
},
pivotChanged: function AndroidPresenter_pivotChanged(aContext, aReason) {
if (!aContext.accessible)
return;
let isExploreByTouch = (aReason == Ci.nsIAccessiblePivot.REASON_POINT &&
Utils.AndroidSdkVersion >= 14);
if (isExploreByTouch) {
// This isn't really used by TalkBack so this is a half-hearted attempt
// for now.
this.sendMessageToJava({
gecko: {
type: 'Accessibility:Event',
eventType: this.ANDROID_VIEW_HOVER_EXIT,
text: []
}
});
}
let output = [];
aContext.newAncestry.forEach(
function(acc) {
output.push.apply(output, UtteranceGenerator.genForObject(acc));
if (isExploreByTouch) {
// Just provide the parent for some context, no need to utter the entire
// ancestry change since it doesn't make sense in spatial navigation.
for (var i = aContext.newAncestry.length - 1; i >= 0; i--) {
let utter = UtteranceGenerator.genForObject(aContext.newAncestry[i]);
if (utter.length) {
output.push.apply(output, utter);
break;
}
}
);
} else {
// Utter the entire context change in linear navigation.
aContext.newAncestry.forEach(
function(acc) {
output.push.apply(output, UtteranceGenerator.genForObject(acc));
}
);
}
output.push.apply(output,
UtteranceGenerator.genForObject(aContext.accessible));
@ -253,7 +291,9 @@ AndroidPresenter.prototype = {
this.sendMessageToJava({
gecko: {
type: 'Accessibility:Event',
eventType: this.ANDROID_VIEW_FOCUSED,
eventType: isExploreByTouch ?
this.ANDROID_VIEW_HOVER_ENTER :
this.ANDROID_VIEW_FOCUSED,
text: output
}
});
@ -271,7 +311,7 @@ AndroidPresenter.prototype = {
tabSelected: function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
// Send a pivot change message with the full context utterance for this doc.
this.pivotChanged(aVCContext);
this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
},
tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
@ -319,6 +359,24 @@ AndroidPresenter.prototype = {
this.sendMessageToJava({gecko: androidEvent});
},
viewportChanged: function AndroidPresenter_viewportChanged() {
if (Utils.AndroidSdkVersion < 14)
return;
let win = Utils.getBrowserApp(this.chromeWin).selectedBrowser.contentWindow;
this.sendMessageToJava({
gecko: {
type: 'Accessibility:Event',
eventType: this.ANDROID_VIEW_SCROLLED,
text: [],
scrollX: win.scrollX,
scrollY: win.scrollY,
maxScrollX: win.scrollMaxX,
maxScrollY: win.scrollMaxY
}
});
},
sendMessageToJava: function AndroidPresenter_sendMessageTojava(aMessage) {
return Cc['@mozilla.org/android/bridge;1'].
getService(Ci.nsIAndroidBridge).

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

@ -48,6 +48,10 @@ var Utils = {
}
},
getCurrentContentDoc: function getCurrentContentDoc(aWindow) {
return this.getBrowserApp(aWindow).selectedBrowser.contentDocument;
},
getViewport: function getViewport(aWindow) {
switch (this.OS) {
case 'Android':

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

@ -408,20 +408,54 @@ var VirtualCursorController = {
SINGLE_LINE_EDITABLE: 1,
MULTI_LINE_EDITABLE: 2,
explorebytouch: false,
exploreByTouch: false,
attach: function attach(aWindow) {
this.chromeWin = aWindow;
this.chromeWin.document.addEventListener('keypress', this, true);
this.chromeWin.document.addEventListener('mousemove', this, true);
},
detach: function detach() {
this.chromeWin.document.removeEventListener('keypress', this, true);
this.chromeWin.document.removeEventListener('mousemove', this, true);
},
handleEvent: function handleEvent(aEvent) {
let document = Utils.getBrowserApp(this.chromeWin).
selectedBrowser.contentDocument;
handleEvent: function VirtualCursorController_handleEvent(aEvent) {
switch (aEvent.type) {
case 'keypress':
this._handleKeypress(aEvent);
break;
case 'mousemove':
this._handleMousemove(aEvent);
break;
}
},
_handleMousemove: function _handleMousemove(aEvent) {
// Explore by touch is disabled.
if (!this.exploreByTouch)
return;
// On non-Android we use the shift key to simulate touch.
if (Utils.OS != 'Android' && !aEvent.shiftKey)
return;
// We should not be calling moveToPoint more than 10 times a second.
// It is granular enough to feel natural, and it does not hammer the CPU.
if (!this._handleMousemove._lastEventTime ||
aEvent.timeStamp - this._handleMousemove._lastEventTime >= 100) {
this.moveToPoint(Utils.getCurrentContentDoc(this.chromeWin),
aEvent.screenX, aEvent.screenY);
this._handleMousemove._lastEventTime = aEvent.timeStamp;
}
aEvent.preventDefault();
aEvent.stopImmediatePropagation();
},
_handleKeypress: function _handleKeypress(aEvent) {
let document = Utils.getCurrentContentDoc(this.chromeWin);
let target = aEvent.target;
switch (aEvent.keyCode) {
@ -499,6 +533,11 @@ var VirtualCursorController = {
aEvent.stopPropagation();
},
moveToPoint: function moveToPoint(aDocument, aX, aY) {
this.getVirtualCursor(aDocument).moveToPoint(TraversalRules.Simple,
aX, aY, true);
},
_isEditableText: function _isEditableText(aElement) {
// XXX: Support contentEditable and design mode
if (aElement instanceof Ci.nsIDOMHTMLInputElement &&
@ -582,8 +621,7 @@ var VirtualCursorController = {
doc = doc.parentDocument;
continue;
}
if (vc)
vc.moveNext(aRule || TraversalRules.Simple, aAccessible, true);
vc.moveNext(aRule || TraversalRules.Simple, aAccessible, true);
break;
}
},

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

@ -103,7 +103,7 @@ __try {
}
if (NULL == *ppv) {
HRESULT hr = CAccessibleHyperlink::QueryInterface(iid, ppv);
HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv);
if (SUCCEEDED(hr))
return hr;
}

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

@ -14,7 +14,7 @@
#include "Accessible.h"
#include "Accessible2.h"
#include "ia2AccessibleComponent.h"
#include "CAccessibleHyperlink.h"
#include "ia2AccessibleHyperlink.h"
#include "CAccessibleValue.h"
#define DECL_IUNKNOWN_INHERITED \
@ -65,7 +65,7 @@ Class::QueryInterface(REFIID iid, void** ppv) \
class AccessibleWrap : public Accessible,
public ia2AccessibleComponent,
public CAccessibleHyperlink,
public ia2AccessibleHyperlink,
public CAccessibleValue,
public IAccessible2
{

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

@ -26,7 +26,6 @@ CPPSRCS = \
nsAccessNodeWrap.cpp \
nsHTMLWin32ObjectAccessible.cpp \
nsWinUtils.cpp \
CAccessibleHyperlink.cpp \
CAccessibleTable.cpp \
CAccessibleTableCell.cpp \
CAccessibleValue.cpp \
@ -36,6 +35,7 @@ CPPSRCS = \
ia2AccessibleComponent.cpp \
ia2AccessibleEditableText.cpp \
ia2AccessibleImage.cpp \
ia2AccessibleHyperlink.cpp \
ia2AccessibleHypertext.cpp \
ia2AccessibleRelation.cpp \
ia2AccessibleText.cpp \

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

@ -5,8 +5,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CAccessibleHyperlink.h"
#include "Accessible2.h"
#include "AccessibleHyperlink.h"
#include "AccessibleHyperlink_i.c"
@ -17,13 +15,12 @@
// IUnknown
STDMETHODIMP
CAccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
ia2AccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
{
*ppv = NULL;
if (IID_IAccessibleHyperlink == iid) {
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
if (!thisObj->IsLink())
if (!static_cast<AccessibleWrap*>(this)->IsLink())
return E_NOINTERFACE;
*ppv = static_cast<IAccessibleHyperlink*>(this);
@ -37,12 +34,12 @@ CAccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
// IAccessibleHyperlink
STDMETHODIMP
CAccessibleHyperlink::get_anchor(long aIndex, VARIANT *aAnchor)
ia2AccessibleHyperlink::get_anchor(long aIndex, VARIANT* aAnchor)
{
__try {
VariantInit(aAnchor);
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
if (thisObj->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@ -72,12 +69,12 @@ __try {
}
STDMETHODIMP
CAccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT *aAnchorTarget)
ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget)
{
__try {
VariantInit(aAnchorTarget);
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
if (thisObj->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@ -115,12 +112,12 @@ __try {
}
STDMETHODIMP
CAccessibleHyperlink::get_startIndex(long *aIndex)
ia2AccessibleHyperlink::get_startIndex(long* aIndex)
{
__try {
*aIndex = 0;
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
if (thisObj->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@ -135,12 +132,12 @@ __try {
}
STDMETHODIMP
CAccessibleHyperlink::get_endIndex(long *aIndex)
ia2AccessibleHyperlink::get_endIndex(long* aIndex)
{
__try {
*aIndex = 0;
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
if (thisObj->IsDefunct())
return CO_E_OBJNOTCONNECTED;
@ -155,12 +152,12 @@ __try {
}
STDMETHODIMP
CAccessibleHyperlink::get_valid(boolean *aValid)
ia2AccessibleHyperlink::get_valid(boolean* aValid)
{
__try {
*aValid = false;
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
if (thisObj->IsDefunct())
return CO_E_OBJNOTCONNECTED;

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

@ -13,8 +13,8 @@
#include "ia2AccessibleAction.h"
#include "AccessibleHyperlink.h"
class CAccessibleHyperlink: public ia2AccessibleAction,
public IAccessibleHyperlink
class ia2AccessibleHyperlink : public ia2AccessibleAction,
public IAccessibleHyperlink
{
public:

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

@ -6,13 +6,23 @@
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../layout.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
function doPreTest()
{
var imgMap = document.getElementById("imgmap");
waitForImageMap(imgMap, doTest);
}
function doTest()
{
// Not specific case, child and deepchild testing.
@ -54,11 +64,16 @@ if (!MAC) {
testChildAtPoint("area", 1, 1, "area", "area");
// Test image maps. Their children are not in the layout tree.
var theLetterA = getAccessible("imgmap").firstChild;
hitTest("imgmap", theLetterA, theLetterA);
hitTest("container", "imgmap", theLetterA);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
addA11yLoadEvent(doPreTest);
</script>
</head>
<body>
@ -82,5 +97,15 @@ if (!MAC) {
<div id="outofflow" style="width: 10px; height: 10px; position: absolute; left: 0px; top: 0px; background-color: yellow;">
</div>
<div id="area" style="width: 100px; height: 100px; background-color: blue;"></div>
<map name="atoz_map">
<area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
coords="0,0,15,15" alt="thelettera" shape="rect"/>
</map>
<div id="container">
<img id="imgmap" width="447" height="15" usemap="#atoz_map" src="../letters.gif"/>
</div>
</body>
</html>

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

@ -71,9 +71,20 @@ let FormAssistant = {
if (evt.target != target || this.isKeyboardOpened)
return;
if (!(evt.target instanceof HTMLInputElement ||
evt.target instanceof HTMLTextAreaElement))
let ignore = {
button: true,
checkbox: true,
file: true,
radio: true,
reset: true,
submit: true
};
if ((target instanceof HTMLInputElement && ignore[target.type]) ||
!(target instanceof HTMLInputElement ||
target instanceof HTMLTextAreaElement)) {
return;
}
this.isKeyboardOpened = this.tryShowIme(evt.target);
break;

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

@ -18,6 +18,8 @@ window.addEventListener('ContentStart', function() {
.getInterface(Components.interfaces.nsIDOMWindowUtils);
let hostDPI = windowUtils.displayDPI;
let DEFAULT_SCREEN = "320x480";
// This is a somewhat random selection of named screens.
// Add more to this list when we support more hardware.
// Data from: http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density
@ -62,9 +64,9 @@ window.addEventListener('ContentStart', function() {
try {
screenarg = args.handleFlagWithParam('screen', false);
// If there isn't one, we don't need to do anything
// If there isn't one, use the default screen
if (screenarg === null)
return;
screenarg = DEFAULT_SCREEN;
// With no value, tell the user how to use it
if (screenarg == '')

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

@ -140,7 +140,6 @@ var shell = {
addPermissions(domains.split(","));
CustomEventManager.init();
WebappsHelper.init();
// XXX could factor out into a settings->pref map. Not worth it yet.
@ -216,7 +215,7 @@ var shell = {
case evt.DOM_VK_PAGE_DOWN:
this.changeVolume(-1);
break;
case evt.DOM_VK_PAGE_UP:
this.changeVolume(1);
break;
@ -249,6 +248,9 @@ var shell = {
}
break;
case 'mozbrowserloadstart':
if (content.document.location == 'about:blank')
return;
this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow);

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

@ -187,6 +187,7 @@
command="Tools:Scratchpad"/>
<menuitem id="appmenu_styleeditor"
hidden="true"
type="checkbox"
label="&styleeditor.label;"
key="key_styleeditor"
command="Tools:StyleEditor"/>

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

@ -563,6 +563,7 @@
key="key_scratchpad"
command="Tools:Scratchpad"/>
<menuitem id="menu_styleeditor"
type="checkbox"
hidden="true"
label="&styleeditor.label;"
accesskey="&styleeditor.accesskey;"

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

@ -95,7 +95,7 @@
<command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true"/>
<command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true"/>
<command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
<command id="Tools:StyleEditor" oncommand="StyleEditor.openChrome();" disabled="true"/>
<command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true"/>
<command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
<command id="Tools:Sanitize"

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

@ -1477,6 +1477,7 @@ var gBrowserInit = {
#ifdef MENUBAR_CAN_AUTOHIDE
document.getElementById("appmenu_styleeditor").hidden = false;
#endif
document.getElementById("developer-toolbar-styleeditor").hidden = false;
}
#ifdef MENUBAR_CAN_AUTOHIDE
@ -1496,7 +1497,6 @@ var gBrowserInit = {
#ifdef MENUBAR_CAN_AUTOHIDE
document.getElementById("appmenu_responsiveUI").hidden = false;
#endif
document.getElementById("developer-toolbar-responsiveui").hidden = false;
}
let appMenuButton = document.getElementById("appmenu-button");
@ -7370,40 +7370,29 @@ var StyleEditor = {
*/
openChrome: function SE_openChrome(aSelectedStyleSheet, aLine, aCol)
{
const CHROME_URL = "chrome://browser/content/styleeditor.xul";
const CHROME_WINDOW_TYPE = "Tools:StyleEditor";
const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
// focus currently open Style Editor window for this document, if any
let contentWindow = gBrowser.selectedBrowser.contentWindow;
let contentWindowID = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
let enumerator = Services.wm.getEnumerator(CHROME_WINDOW_TYPE);
while (enumerator.hasMoreElements()) {
var win = enumerator.getNext();
if (win.styleEditorChrome.contentWindowID == contentWindowID) {
if (aSelectedStyleSheet) {
win.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol);
}
win.focus();
return win;
}
let win = this.StyleEditorManager.getEditorForWindow(contentWindow);
if (win) {
this.StyleEditorManager.selectEditor(win);
return win;
} else {
return this.StyleEditorManager.newEditor(contentWindow,
aSelectedStyleSheet, aLine, aCol);
}
},
let args = {
contentWindow: contentWindow,
selectedStyleSheet: aSelectedStyleSheet,
line: aLine,
col: aCol
};
args.wrappedJSObject = args;
let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
CHROME_WINDOW_FLAGS, args);
chromeWindow.focus();
return chromeWindow;
toggle: function SE_toggle()
{
this.StyleEditorManager.toggleEditor(gBrowser.contentWindow);
}
};
XPCOMUtils.defineLazyGetter(StyleEditor, "StyleEditorManager", function() {
let tmp = {};
Cu.import("resource:///modules/devtools/StyleEditor.jsm", tmp);
return new tmp.StyleEditorManager(window);
});
XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
#ifdef XP_WIN

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

@ -1045,13 +1045,13 @@
class="devtools-toolbarbutton"
hidden="true"
command="Tools:Inspect"/>
<toolbarbutton id="developer-toolbar-responsiveui"
label="&responsiveDesignTool.label;"
<toolbarbutton id="developer-toolbar-styleeditor"
label="&styleeditor.label;"
class="devtools-toolbarbutton"
hidden="true"
command="Tools:ResponsiveUI"/>
command="Tools:StyleEditor"/>
<toolbarbutton id="developer-toolbar-debugger"
label="&scriptsButton.label;"
label="&debuggerMenu.label2;"
class="devtools-toolbarbutton"
hidden="true"
command="Tools:Debugger"/>

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

@ -2,10 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
:root {
-moz-user-focus: normal;
}
input[type=button] {
cursor: pointer;
}
@ -15,6 +11,7 @@ input[type=button] {
display: -moz-box;
position: relative;
-moz-box-flex: 1;
-moz-user-focus: normal;
}
#newtab-scrollbox:not([page-disabled]) {

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

@ -80,18 +80,24 @@ let gPage = {
* @param aValue Whether the New Tab Page is enabled or not.
*/
_updateAttributes: function Page_updateAttributes(aValue) {
let selector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
let nodes = document.querySelectorAll(selector);
// Set the nodes' states.
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
for (let node of document.querySelectorAll(nodeSelector)) {
if (aValue)
node.removeAttribute("page-disabled");
else
node.setAttribute("page-disabled", "true");
}
// Enables/disables the control and link elements.
let inputSelector = ".newtab-control, .newtab-link";
for (let input of document.querySelectorAll(inputSelector)) {
if (aValue)
input.removeAttribute("tabindex");
else
input.setAttribute("tabindex", "-1");
}
// Update the toggle button's title.
let toggle = document.getElementById("newtab-toggle");
toggle.setAttribute("title", newTabString(aValue ? "hide" : "show"));

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

@ -0,0 +1,14 @@
[
{
"size": 47,
"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
"size": 72350187,
"digest": "7d2fbe08aca3ae740e33b8aee872705a3b5229681dd0617ceffd6619fba75cb3cb7e1c3a071218f7cfd464003e5cd773cd8e67d16f78df9c50218fb6671580c6",
"algorithm": "sha512",
"filename": "clang.tar.bz2"
}
]

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

@ -0,0 +1,14 @@
[
{
"size": 47,
"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
"size": 71534797,
"digest": "66bd11bea6e1f07090e9e03c833e107088097605611fd455e80b280ce2b71ca71ff9841a66614f62da162469b222b5eefd5535373b199c60fd485959889b5dcb",
"algorithm": "sha512",
"filename": "clang.tar.bz2"
}
]

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

@ -0,0 +1 @@
../macosx64/clang.manifest

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

@ -0,0 +1,14 @@
[
{
"size": 47,
"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
"size": 63015959,
"digest": "e0ac132a77b052d6494d4bf02f17aba475138ffcff07a5f92f17f45b1d5f27b7b0cea36c29473965271e60910af82ffd4989df94c10d24794d1bf8362bcb785b",
"algorithm": "sha512",
"filename": "clang.tar.bz2"
}
]

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

@ -35,6 +35,7 @@ function checkOpen() {
let close = document.getElementById("developer-toolbar-closebutton");
let webconsole = document.getElementById("developer-toolbar-webconsole");
let inspector = document.getElementById("developer-toolbar-inspector");
let styleeditor = document.getElementById("developer-toolbar-styleeditor");
let debuggr = document.getElementById("developer-toolbar-debugger");
ok(close, "Close button exists");
@ -42,18 +43,21 @@ function checkOpen() {
ok(!isChecked(webconsole), "web console button state 1");
ok(!isChecked(inspector), "inspector button state 1");
ok(!isChecked(debuggr), "debugger button state 1");
ok(!isChecked(styleeditor), "styleeditor button state 1");
document.getElementById("Tools:WebConsole").doCommand();
ok(isChecked(webconsole), "web console button state 2");
ok(!isChecked(inspector), "inspector button state 2");
ok(!isChecked(debuggr), "debugger button state 2");
ok(!isChecked(styleeditor), "styleeditor button state 2");
document.getElementById("Tools:Inspect").doCommand();
ok(isChecked(webconsole), "web console button state 3");
ok(isChecked(inspector), "inspector button state 3");
ok(!isChecked(debuggr), "debugger button state 3");
ok(!isChecked(styleeditor), "styleeditor button state 3");
// Christmas tree!
@ -66,12 +70,14 @@ function checkOpen() {
ok(!isChecked(webconsole), "web console button state 6");
ok(isChecked(inspector), "inspector button state 6");
ok(!isChecked(debuggr), "debugger button state 6");
ok(!isChecked(styleeditor), "styleeditor button state 6");
document.getElementById("Tools:Inspect").doCommand();
ok(!isChecked(webconsole), "web console button state 7");
ok(!isChecked(inspector), "inspector button state 7");
ok(!isChecked(debuggr), "debugger button state 7");
ok(!isChecked(styleeditor), "styleeditor button state 7");
// All closed
@ -81,9 +87,38 @@ function checkOpen() {
ok(!isChecked(webconsole), "web console button state 8");
ok(isChecked(inspector), "inspector button state 8");
ok(!isChecked(debuggr), "debugger button state 8");
ok(!isChecked(styleeditor), "styleeditor button state 8");
oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkClosed));
document.getElementById("Tools:DevToolbar").doCommand();
// Test Style Editor
document.getElementById("Tools:StyleEditor").doCommand();
ok(!isChecked(webconsole), "web console button state 9");
ok(isChecked(inspector), "inspector button state 9");
ok(!isChecked(debuggr), "debugger button state 9");
ok(isChecked(styleeditor), "styleeditor button state 9");
// Test Debugger
document.getElementById("Tools:Debugger").doCommand();
ok(!isChecked(webconsole), "web console button state 9");
ok(isChecked(inspector), "inspector button state 9");
ok(isChecked(debuggr), "debugger button state 9");
ok(isChecked(styleeditor), "styleeditor button state 9");
addTab("about:blank", function(browser, tab) {
info("Opening a new tab");
ok(!isChecked(webconsole), "web console button state 10");
ok(!isChecked(inspector), "inspector button state 10");
ok(!isChecked(debuggr), "debugger button state 10");
ok(!isChecked(styleeditor), "styleeditor button state 10");
gBrowser.removeCurrentTab();
oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkClosed));
document.getElementById("Tools:DevToolbar").doCommand();
});
}
function checkClosed() {
@ -102,10 +137,15 @@ function checkReOpen() {
let webconsole = document.getElementById("developer-toolbar-webconsole");
let inspector = document.getElementById("developer-toolbar-inspector");
let debuggr = document.getElementById("developer-toolbar-debugger");
let styleeditor = document.getElementById("developer-toolbar-styleeditor");
ok(isChecked(webconsole), "web console button state 9");
ok(isChecked(inspector), "inspector button state 9");
ok(!isChecked(debuggr), "debugger button state 9");
ok(isChecked(webconsole), "web console button state 99");
ok(isChecked(inspector), "inspector button state 99");
ok(isChecked(debuggr), "debugger button state 99");
ok(isChecked(styleeditor), "styleeditor button state 99");
// We close the style editor (not automatically closed)
document.getElementById("Tools:StyleEditor").doCommand();
oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkReClosed));
document.getElementById("developer-toolbar-closebutton").doCommand();

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

@ -5,7 +5,7 @@
"use strict";
const EXPORTED_SYMBOLS = ["StyleEditor", "StyleEditorFlags"];
const EXPORTED_SYMBOLS = ["StyleEditor", "StyleEditorFlags", "StyleEditorManager"];
const Cc = Components.classes;
const Ci = Components.interfaces;
@ -1160,3 +1160,133 @@ function setupBracketCompletion(aSourceEditor)
aSourceEditor.setCaretOffset(aSourceEditor.getCaretOffset() - 1);
}, false);
}
/**
* Manage the different editors instances.
*/
function StyleEditorManager(aWindow) {
this.chromeWindow = aWindow;
this.listenToTabs();
this.editors = new WeakMap();
}
StyleEditorManager.prototype = {
/**
* Get the editor for a specific content window.
*/
getEditorForWindow: function SEM_getEditorForWindow(aContentWindow) {
return this.editors.get(aContentWindow);
},
/**
* Focus the editor and select a stylesheet.
*
* @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
* @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
* @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
*/
selectEditor: function SEM_selectEditor(aWindow, aSelectedStyleSheet, aLine, aCol) {
if (aSelectedStyleSheet) {
aWindow.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol);
}
aWindow.focus();
},
/**
* Open a new editor.
*
* @param {Window} content window.
* @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
* @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
* @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
*/
newEditor: function SEM_newEditor(aContentWindow, aSelectedStyleSheet, aLine, aCol) {
const CHROME_URL = "chrome://browser/content/styleeditor.xul";
const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
let args = {
contentWindow: aContentWindow,
selectedStyleSheet: aSelectedStyleSheet,
line: aLine,
col: aCol
};
args.wrappedJSObject = args;
let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
CHROME_WINDOW_FLAGS, args);
chromeWindow.onunload = function() {
if (chromeWindow.location == CHROME_URL) {
// not about:blank being unloaded
this.unregisterEditor(aContentWindow);
}
}.bind(this);
chromeWindow.focus();
this.editors.set(aContentWindow, chromeWindow);
this.refreshCommand();
return chromeWindow;
},
/**
* Toggle an editor.
*
* @param {Window} associated content window.
*/
toggleEditor: function SEM_toggleEditor(aContentWindow) {
let editor = this.getEditorForWindow(aContentWindow);
if (editor) {
editor.close();
} else {
this.newEditor(aContentWindow);
}
},
/**
* Close an editor.
*
* @param {Window} associated content window.
*/
unregisterEditor: function SEM_unregisterEditor(aContentWindow) {
let chromeWindow = this.editors.get(aContentWindow);
if (chromeWindow) {
chromeWindow.close();
}
this.editors.delete(aContentWindow);
this.refreshCommand();
},
/**
* Update the status of tool's menuitems and buttons.
*/
refreshCommand: function SEM_refreshCommand() {
let contentWindow = this.chromeWindow.gBrowser.contentWindow;
let command = this.chromeWindow.document.getElementById("Tools:StyleEditor");
let win = this.getEditorForWindow(contentWindow);
if (win) {
command.setAttribute("checked", "true");
} else {
command.setAttribute("checked", "false");
}
},
/**
* Trigger refreshCommand when needed.
*/
listenToTabs: function SEM_listenToTabs() {
let win = this.chromeWindow;
let tabs = win.gBrowser.tabContainer;
let bound_refreshCommand = this.refreshCommand.bind(this);
tabs.addEventListener("TabSelect", bound_refreshCommand, true);
win.addEventListener("unload", function onClose(aEvent) {
tabs.removeEventListener("TabSelect", bound_refreshCommand, true);
win.removeEventListener("unload", onClose, false);
}, false);
},
}

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

@ -497,7 +497,7 @@ let Manager = {
NetworkResponseListener = ConsoleProgressListener = null;
XPCOMUtils = gConsoleStorage = WebConsoleUtils = l10n = JSPropertyProvider =
NetworkHelper = NetUtil = activityDistributor = null;
null;
},
};

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

@ -242,7 +242,6 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY webConsoleButton.label "Web Console">
<!ENTITY inspectorButton.label "Inspector">
<!ENTITY scriptsButton.label "Scripts">
<!ENTITY inspectorHTMLCopyInner.label "Copy Inner HTML">
<!ENTITY inspectorHTMLCopyInner.accesskey "I">

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

@ -5596,6 +5596,9 @@ if test -n "$MOZ_CUBEB"; then
*-mingw*)
AC_DEFINE(MOZ_CUBEB)
;;
*-darwin*)
AC_DEFINE(MOZ_CUBEB)
;;
*-openbsd*)
AC_DEFINE(MOZ_CUBEB)
;;

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

@ -120,7 +120,7 @@ nsContentSink::nsContentSink()
NS_ASSERTION(mInNotification == 0, "What?");
NS_ASSERTION(!mDeferredLayoutStart, "What?");
#ifdef NS_DEBUG
#ifdef DEBUG
if (!gContentSinkLogModuleInfo) {
gContentSinkLogModuleInfo = PR_NewLogModule("nscontentsink");
}
@ -925,7 +925,7 @@ nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache,
// The http manifest attribute URI is equal to the manifest URI of
// the cache the document was loaded from - associate the document with
// that cache and invoke the cache update process.
#ifdef NS_DEBUG
#ifdef DEBUG
nsCAutoString docURISpec, clientID;
mDocumentURI->GetAsciiSpec(docURISpec);
aLoadApplicationCache->GetClientID(clientID);
@ -979,7 +979,7 @@ nsContentSink::SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplication
NS_ASSERTION(applicationCacheDocument,
"mDocument must implement nsIApplicationCacheContainer.");
#ifdef NS_DEBUG
#ifdef DEBUG
nsCAutoString docURISpec, clientID;
mDocumentURI->GetAsciiSpec(docURISpec);
aLoadApplicationCache->GetClientID(clientID);

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

@ -52,7 +52,7 @@ class Loader;
}
}
#ifdef NS_DEBUG
#ifdef DEBUG
extern PRLogModuleInfo* gContentSinkLogModuleInfo;

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

@ -410,6 +410,15 @@ nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
}
}
// static
size_t
nsIdentifierMapEntry::SizeOfExcludingThis(nsIdentifierMapEntry* aEntry,
nsMallocSizeOfFun aMallocSizeOf,
void*)
{
return aEntry->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
// Helper structs for the content->subdoc map
class SubDocMapEntry : public PLDHashEntryHdr
@ -9682,7 +9691,8 @@ nsDocument::DocSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
mStyledLinks.SizeOfExcludingThis(NULL, aWindowSizes->mMallocSizeOf);
aWindowSizes->mDOMOther +=
mIdentifierMap.SizeOfExcludingThis(NULL, aWindowSizes->mMallocSizeOf);
mIdentifierMap.SizeOfExcludingThis(nsIdentifierMapEntry::SizeOfExcludingThis,
aWindowSizes->mMallocSizeOf);
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:

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

@ -215,6 +215,10 @@ public:
ChangeCallback mKey;
};
static size_t SizeOfExcludingThis(nsIdentifierMapEntry* aEntry,
nsMallocSizeOfFun aMallocSizeOf,
void* aArg);
private:
void FireChangeCallbacks(Element* aOldElement, Element* aNewElement,
bool aImageOnly = false);

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

@ -85,7 +85,7 @@
using namespace mozilla;
using namespace mozilla::dom;
#ifdef NS_DEBUG
#ifdef DEBUG
static PRLogModuleInfo* gSinkLogModuleInfo;
#define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj) \
@ -188,7 +188,7 @@ protected:
already_AddRefed<nsGenericHTMLElement>
CreateContentObject(const nsIParserNode& aNode, nsHTMLTag aNodeType);
#ifdef NS_DEBUG
#ifdef DEBUG
void SinkTraceNode(PRUint32 aBit,
const char* aMsg,
const nsHTMLTag aTag,
@ -243,7 +243,7 @@ protected:
bool IsMonolithicContainer(nsHTMLTag aTag);
#ifdef NS_DEBUG
#ifdef DEBUG
void ForceReflow();
#endif
};
@ -313,7 +313,7 @@ private:
//----------------------------------------------------------------------
#ifdef NS_DEBUG
#ifdef DEBUG
void
HTMLContentSink::SinkTraceNode(PRUint32 aBit,
const char* aMsg,
@ -582,7 +582,7 @@ SinkContext::DidAddContent(nsIContent* aContent)
mStack[mStackPos - 1].mContent->GetChildCount()) {
nsIContent* parent = mStack[mStackPos - 1].mContent;
#ifdef NS_DEBUG
#ifdef DEBUG
// Tracing code
nsIParserService *parserService = nsContentUtils::GetParserService();
if (parserService) {
@ -757,7 +757,7 @@ SinkContext::CloseContainer(const nsHTMLTag aTag)
// notification
if (mStack[mStackPos].mNumFlushed < content->GetChildCount()) {
#ifdef NS_DEBUG
#ifdef DEBUG
{
// Tracing code
SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
@ -1068,7 +1068,7 @@ SinkContext::FlushTags()
childCount = content->GetChildCount();
if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) {
#ifdef NS_DEBUG
#ifdef DEBUG
{
// Tracing code
SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
@ -1242,7 +1242,7 @@ HTMLContentSink::HTMLContentSink()
// Note: operator new zeros our memory
#ifdef NS_DEBUG
#ifdef DEBUG
if (!gSinkLogModuleInfo) {
gSinkLogModuleInfo = PR_NewLogModule("htmlcontentsink");
}
@ -1426,7 +1426,7 @@ HTMLContentSink::Init(nsIDocument* aDoc,
mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
mContextStack.AppendElement(mCurrentContext);
#ifdef NS_DEBUG
#ifdef DEBUG
nsCAutoString spec;
(void)aURI->GetSpec(spec);
SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_CALLS,

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

@ -2873,8 +2873,8 @@ static const struct MidasCommand gMidasCommandTable[] = {
{ "cut", "cmd_cut", "", true, false },
{ "copy", "cmd_copy", "", true, false },
{ "paste", "cmd_paste", "", true, false },
{ "delete", "cmd_delete", "", true, false },
{ "forwarddelete", "cmd_forwardDelete", "", true, false },
{ "delete", "cmd_deleteCharBackward", "", true, false },
{ "forwarddelete", "cmd_deleteCharForward", "", true, false },
{ "selectall", "cmd_selectAll", "", true, false },
{ "undo", "cmd_undo", "", true, false },
{ "redo", "cmd_redo", "", true, false },
@ -3189,6 +3189,13 @@ nsHTMLDocument::ExecCommand(const nsAString& commandID,
return NS_OK;
}
// Return false for disabled commands (bug 760052)
bool enabled = false;
cmdMgr->IsCommandEnabled(cmdToDispatch.get(), window, &enabled);
if (!enabled) {
return NS_OK;
}
if (!isBool && paramStr.IsEmpty()) {
rv = cmdMgr->DoCommand(cmdToDispatch.get(), nsnull, window);
} else {

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

@ -1214,7 +1214,7 @@ nsXMLContentSink::HandleDoctypeDecl(const nsAString & aSubset,
nsRefPtr<nsCSSStyleSheet> sheet;
mCSSLoader->LoadSheetSync(uri, true, true, getter_AddRefs(sheet));
#ifdef NS_DEBUG
#ifdef DEBUG
nsCAutoString uriStr;
uri->GetSpec(uriStr);
printf("Loading catalog stylesheet: %s ... %s\n", uriStr.get(), sheet.get() ? "Done" : "Failed");

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

@ -271,7 +271,7 @@ nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement* aElement,
while (updater) {
if (updater->mElement == aElement) {
#ifdef NS_DEBUG
#ifdef DEBUG
if (PR_LOG_TEST(gLog, PR_LOG_NOTICE)) {
nsCAutoString eventsC, targetsC, aeventsC, atargetsC;
eventsC.AssignWithConversion(updater->mEvents);
@ -299,7 +299,7 @@ nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement* aElement,
link = &(updater->mNext);
updater = updater->mNext;
}
#ifdef NS_DEBUG
#ifdef DEBUG
if (PR_LOG_TEST(gLog, PR_LOG_NOTICE)) {
nsCAutoString aeventsC, atargetsC;
CopyUTF16toUTF8(aEvents, aeventsC);
@ -334,7 +334,7 @@ nsXULCommandDispatcher::RemoveCommandUpdater(nsIDOMElement* aElement)
while (updater) {
if (updater->mElement == aElement) {
#ifdef NS_DEBUG
#ifdef DEBUG
if (PR_LOG_TEST(gLog, PR_LOG_NOTICE)) {
nsCAutoString eventsC, targetsC;
eventsC.AssignWithConversion(updater->mEvents);
@ -400,7 +400,7 @@ nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
if (! document)
continue;
#ifdef NS_DEBUG
#ifdef DEBUG
if (PR_LOG_TEST(gLog, PR_LOG_NOTICE)) {
nsCAutoString aeventnameC;
CopyUTF16toUTF8(aEventName, aeventnameC);

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

@ -10575,7 +10575,7 @@ nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
nsDocShell *rootDocShell = static_cast<nsDocShell*>
(rootIDocShell);
#ifdef NS_DEBUG
#ifdef DEBUG
nsresult rv =
#endif
SetChildHistoryEntry(oldRootEntry, rootDocShell,

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

@ -613,7 +613,7 @@ static const char kDOMStringBundleURL[] =
(nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::DOM_OBJECT)
#ifdef NS_DEBUG
#ifdef DEBUG
#define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \
eDOMClassInfo_##_class##_id,
#else
@ -4535,7 +4535,7 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile)
DOM_CLASSINFO_MAP_END
#ifdef NS_DEBUG
#ifdef DEBUG
{
PRUint32 i = ArrayLength(sClassInfoData);
@ -5876,7 +5876,7 @@ DefineInterfaceConstants(JSContext *cx, JSObject *obj, const nsIID *aIID)
}
default:
{
#ifdef NS_DEBUG
#ifdef DEBUG
NS_ERROR("Non-numeric constant found in interface.");
#endif
continue;

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

@ -71,7 +71,7 @@ struct nsDOMClassInfoData
PRUint32 mInterfacesBitmap;
bool mChromeOnly;
bool mDisabled;
#ifdef NS_DEBUG
#ifdef DEBUG
PRUint32 mDebugID;
#endif
};

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

@ -257,7 +257,7 @@ static TimeStamp gLastRecordedRecentTimeouts;
PRInt32 gTimeoutCnt = 0;
#endif
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
static bool gDOMWindowDumpEnabled = false;
#endif
@ -713,7 +713,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
gRefCnt++;
if (gRefCnt == 1) {
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
Preferences::AddBoolVarCache(&gDOMWindowDumpEnabled,
"browser.dom.window.dump.enabled");
#endif
@ -4464,7 +4464,7 @@ nsGlobalWindow::GetFullScreen(bool* aFullScreen)
bool
nsGlobalWindow::DOMWindowDumpEnabled()
{
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
// In optimized builds we check a pref that controls if we should
// enable output from dump() or not, in debug builds it's always
// enabled.
@ -8631,7 +8631,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
aJSCallerContext, aReturn),
NS_ERROR_NOT_INITIALIZED);
#ifdef NS_DEBUG
#ifdef DEBUG
PRUint32 argc = 0;
if (argv)
argv->GetLength(&argc);

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

@ -1676,7 +1676,7 @@ nsJSContext::JSObjectFromInterface(nsISupports* aTarget, JSObject* aScope, JSObj
nsresult rv = nsContentUtils::WrapNative(mContext, aScope, aTarget, &v);
NS_ENSURE_SUCCESS(rv, rv);
#ifdef NS_DEBUG
#ifdef DEBUG
nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
nsCOMPtr<nsISupports> native =
nsContentUtils::XPConnect()->GetNativeOfWrapper(mContext,
@ -2216,7 +2216,7 @@ nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
if (rv == NS_ERROR_NO_INTERFACE) {
// something else - probably an event object or similar -
// just wrap it.
#ifdef NS_DEBUG
#ifdef DEBUG
// but first, check its not another nsISupportsPrimitive, as
// these are now deprecated for use with script contexts.
nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));

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

@ -236,9 +236,9 @@ CollectWindowReports(nsGlobalWindow *aWindow,
REPORT("/layout/frames/" # classname, frameSize, \
"Memory used by frames of " \
"type " #classname " within a window."); \
aWindowTotalSizes->mArenaStats.FRAME_ID_STAT_FIELD(classname) \
+= frameSize; \
} \
aWindowTotalSizes->mArenaStats.FRAME_ID_STAT_FIELD(classname) \
+= frameSize; \
}
#include "nsFrameIdList.h"
#undef FRAME_ID

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

@ -2370,11 +2370,14 @@ class Tokenizer(object):
lexpos=self.lexer.lexpos,
filename = self.filename))
def __init__(self, outputdir):
self.lexer = lex.lex(object=self,
outputdir=outputdir,
lextab='webidllex',
reflags=re.DOTALL)
def __init__(self, outputdir, lexer=None):
if lexer:
self.lexer = lexer
else:
self.lexer = lex.lex(object=self,
outputdir=outputdir,
lextab='webidllex',
reflags=re.DOTALL)
class Parser(Tokenizer):
def getLocation(self, p, i):
@ -3459,11 +3462,13 @@ class Parser(Tokenizer):
else:
raise WebIDLError("invalid syntax", Location(self.lexer, p.lineno, p.lexpos, self._filename))
def __init__(self, outputdir=''):
Tokenizer.__init__(self, outputdir)
def __init__(self, outputdir='', lexer=None):
Tokenizer.__init__(self, outputdir, lexer)
self.parser = yacc.yacc(module=self,
outputdir=outputdir,
tabmodule='webidlyacc')
tabmodule='webidlyacc',
errorlog=yacc.NullLogger(),
picklefile='WebIDLGrammar.pkl')
self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
self._installBuiltins(self._globalScope)
self._productions = []
@ -3535,7 +3540,7 @@ class Parser(Tokenizer):
return result
def reset(self):
return Parser()
return Parser(lexer=self.lexer)
# Builtin IDL defined by WebIDL
_builtins = """

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

@ -58,6 +58,7 @@ $(CPPSRCS): ../%Binding.cpp: $(bindinggen_dependencies) \
_TEST_FILES = \
test_enums.html \
test_integers.html \
test_interfaceToString.html \
test_lookupGetter.html \
test_InstanceOf.html \

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

@ -0,0 +1,45 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<canvas id="c" width="1" height="1"></canvas>
</div>
<pre id="test">
<script type="application/javascript">
function testInt64NonFinite(arg) {
// We can use a WebGLRenderingContext to test conversion to 64-bit signed
// ints edge cases.
try {
var gl = $("c").getContext("experimental-webgl");
} catch (ex) {
// No WebGL support on MacOS 10.5. Just skip this test
todo(false, "WebGL not supported");
return;
}
is(gl.getError(), 0, "Should not start in an error state");
var b = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, b);
var a = new Float32Array(1);
gl.bufferData(gl.ARRAY_BUFFER, a, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, arg, a);
is(gl.getError(), 0, "Should have treated non-finite double as 0");
}
testInt64NonFinite(NaN);
testInt64NonFinite(Infinity);
testInt64NonFinite(-Infinity);
</script>
</pre>
</body>
</html>

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

@ -94,6 +94,12 @@ BrowserElementChild.prototype = {
windowUtils.setIsApp(false);
}
// A cache of the menuitem dom objects keyed by the id we generate
// and pass to the embedder
this._ctxHandlers = {};
// Counter of contextmenu events fired
this._ctxCounter = 0;
addEventListener('DOMTitleChanged',
this._titleChangedHandler.bind(this),
/* useCapture = */ true,
@ -116,6 +122,7 @@ BrowserElementChild.prototype = {
addMsgListener("go-back", this._recvGoBack);
addMsgListener("go-forward", this._recvGoForward);
addMsgListener("unblock-modal-prompt", this._recvStopWaiting);
addMsgListener("fire-ctx-callback", this._recvFireCtxCallback);
let els = Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService);
@ -134,6 +141,9 @@ BrowserElementChild.prototype = {
els.addSystemEventListener(global, 'DOMWindowClose',
this._closeHandler.bind(this),
/* useCapture = */ false);
els.addSystemEventListener(global, 'contextmenu',
this._contextmenuHandler.bind(this),
/* useCapture = */ false);
},
_tryGetInnerWindowID: function(win) {
@ -312,6 +322,61 @@ BrowserElementChild.prototype = {
e.preventDefault();
},
_contextmenuHandler: function(e) {
debug("Got contextmenu");
if (e.defaultPrevented) {
return;
}
e.preventDefault();
this._ctxCounter++;
this._ctxHandlers = {};
var elem = e.target;
var menuData = {systemTargets: [], contextmenu: null};
var ctxMenuId = null;
while (elem && elem.hasAttribute) {
var ctxData = this._getSystemCtxMenuData(elem);
if (ctxData) {
menuData.systemTargets.push({
nodeName: elem.nodeName,
data: ctxData
});
}
if (!ctxMenuId && elem.hasAttribute('contextmenu')) {
ctxMenuId = elem.getAttribute('contextmenu');
}
elem = elem.parentNode;
}
if (ctxMenuId) {
var menu = e.target.ownerDocument.getElementById(ctxMenuId);
if (menu) {
menuData.contextmenu = this._buildMenuObj(menu, '');
}
}
sendAsyncMsg('contextmenu', menuData);
},
_getSystemCtxMenuData: function(elem) {
if ((elem instanceof Ci.nsIDOMHTMLAnchorElement && elem.href) ||
(elem instanceof Ci.nsIDOMHTMLAreaElement && elem.href)) {
return elem.href;
}
if (elem instanceof Ci.nsIImageLoadingContent && elem.currentURI) {
return elem.currentURI.spec;
}
if ((elem instanceof Ci.nsIDOMHTMLMediaElement) ||
(elem instanceof Ci.nsIDOMHTMLImageElement)) {
return elem.currentSrc || elem.src;
}
return false;
},
_recvGetScreenshot: function(data) {
debug("Received getScreenshot message: (" + data.json.id + ")");
var canvas = content.document
@ -328,6 +393,42 @@ BrowserElementChild.prototype = {
});
},
_recvFireCtxCallback: function(data) {
debug("Received fireCtxCallback message: (" + data.json.menuitem + ")");
// We silently ignore if the embedder uses an incorrect id in the callback
if (data.json.menuitem in this._ctxHandlers) {
this._ctxHandlers[data.json.menuitem].click();
this._ctxHandlers = {};
} else {
debug("Ignored invalid contextmenu invokation");
}
},
_buildMenuObj: function(menu, idPrefix) {
function maybeCopyAttribute(src, target, attribute) {
if (src.getAttribute(attribute)) {
target[attribute] = src.getAttribute(attribute);
}
}
var menuObj = {type: 'menu', items: []};
maybeCopyAttribute(menu, menuObj, 'label');
for (var i = 0, child; child = menu.children[i++];) {
if (child.nodeName === 'MENU') {
menuObj.items.push(this._buildMenuObj(child, idPrefix + i + '_'));
} else if (child.nodeName === 'MENUITEM') {
var id = this._ctxCounter + '_' + idPrefix + i;
var menuitem = {id: id, type: 'menuitem'};
maybeCopyAttribute(child, menuitem, 'label');
maybeCopyAttribute(child, menuitem, 'icon');
this._ctxHandlers[id] = child;
menuObj.items.push(menuitem);
}
}
return menuObj;
},
_recvSetVisible: function(data) {
debug("Received setVisible message: (" + data.json.visible + ")");
if (docShell.isActive !== data.json.visible) {

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

@ -0,0 +1,413 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
let Cu = Components.utils;
let Ci = Components.interfaces;
let Cc = Components.classes;
let Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
// Event whitelisted for bubbling.
let whitelistedEvents = [
Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE, // Back button.
Ci.nsIDOMKeyEvent.DOM_VK_SLEEP, // Power button.
Ci.nsIDOMKeyEvent.DOM_VK_CONTEXT_MENU,
Ci.nsIDOMKeyEvent.DOM_VK_F5, // Search button.
Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP, // Volume up.
Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN // Volume down.
];
function debug(msg) {
//dump("BrowserElementChild - " + msg + "\n");
}
function sendAsyncMsg(msg, data) {
sendAsyncMessage('browser-element-api:' + msg, data);
}
function sendSyncMsg(msg, data) {
return sendSyncMessage('browser-element-api:' + msg, data);
}
/**
* The BrowserElementChild implements one half of <iframe mozbrowser>.
* (The other half is, unsurprisingly, BrowserElementParent.)
*
* This script is injected into an <iframe mozbrowser> via
* nsIMessageManager::LoadFrameScript().
*
* Our job here is to listen for events within this frame and bubble them up to
* the parent process.
*/
var global = this;
function BrowserElementChild() {
// Maps outer window id --> weak ref to window. Used by modal dialog code.
this._windowIDDict = {};
this._init();
};
BrowserElementChild.prototype = {
_init: function() {
debug("Starting up.");
sendAsyncMsg("hello");
BrowserElementPromptService.mapWindowToBrowserElementChild(content, this);
docShell.isBrowserFrame = true;
docShell.QueryInterface(Ci.nsIWebProgress)
.addProgressListener(this._progressListener,
Ci.nsIWebProgress.NOTIFY_LOCATION |
Ci.nsIWebProgress.NOTIFY_SECURITY |
Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
// This is necessary to get security web progress notifications.
var securityUI = Cc['@mozilla.org/secure_browser_ui;1']
.createInstance(Ci.nsISecureBrowserUI);
securityUI.init(content);
// A mozbrowser iframe contained inside a mozapp iframe should return false
// for nsWindowUtils::IsPartOfApp (unless the mozbrowser iframe is itself
// also mozapp). That is, mozapp is transitive down to its children, but
// mozbrowser serves as a barrier.
//
// This is because mozapp iframes have some privileges which we don't want
// to extend to untrusted mozbrowser content.
//
// Get the app manifest from the parent, if our frame has one.
let appManifestURL = sendSyncMsg('get-mozapp-manifest-url')[0];
let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
if (!!appManifestURL) {
windowUtils.setIsApp(true);
windowUtils.setApp(appManifestURL);
} else {
windowUtils.setIsApp(false);
}
addEventListener('DOMTitleChanged',
this._titleChangedHandler.bind(this),
/* useCapture = */ true,
/* wantsUntrusted = */ false);
addEventListener('DOMLinkAdded',
this._iconChangedHandler.bind(this),
/* useCapture = */ true,
/* wantsUntrusted = */ false);
var self = this;
function addMsgListener(msg, handler) {
addMessageListener('browser-element-api:' + msg, handler.bind(self));
}
addMsgListener("get-screenshot", this._recvGetScreenshot);
addMsgListener("set-visible", this._recvSetVisible);
addMsgListener("unblock-modal-prompt", this._recvStopWaiting);
let els = Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService);
// We are using the system group for those events so if something in the
// content called .stopPropagation() this will still be called.
els.addSystemEventListener(global, 'keydown',
this._keyEventHandler.bind(this),
/* useCapture = */ true);
els.addSystemEventListener(global, 'keypress',
this._keyEventHandler.bind(this),
/* useCapture = */ true);
els.addSystemEventListener(global, 'keyup',
this._keyEventHandler.bind(this),
/* useCapture = */ true);
els.addSystemEventListener(global, 'DOMWindowClose',
this._closeHandler.bind(this),
/* useCapture = */ false);
},
_tryGetInnerWindowID: function(win) {
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
try {
return utils.currentInnerWindowID;
}
catch(e) {
return null;
}
},
/**
* Show a modal prompt. Called by BrowserElementPromptService.
*/
showModalPrompt: function(win, args) {
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
args.windowID = { outer: utils.outerWindowID,
inner: this._tryGetInnerWindowID(win) };
sendAsyncMsg('showmodalprompt', args);
let returnValue = this._waitForResult(win);
if (args.promptType == 'prompt' ||
args.promptType == 'confirm') {
return returnValue;
}
},
/**
* Spin in a nested event loop until we receive a unblock-modal-prompt message for
* this window.
*/
_waitForResult: function(win) {
debug("_waitForResult(" + win + ")");
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let outerWindowID = utils.outerWindowID;
let innerWindowID = this._tryGetInnerWindowID(win);
if (innerWindowID === null) {
// I have no idea what waiting for a result means when there's no inner
// window, so let's just bail.
debug("_waitForResult: No inner window. Bailing.");
return;
}
this._windowIDDict[outerWindowID] = Cu.getWeakReference(win);
debug("Entering modal state (outerWindowID=" + outerWindowID + ", " +
"innerWindowID=" + innerWindowID + ")");
// In theory, we're supposed to pass |modalStateWin| back to
// leaveModalStateWithWindow. But in practice, the window is always null,
// because it's the window associated with this script context, which
// doesn't have a window. But we'll play along anyway in case this
// changes.
var modalStateWin = utils.enterModalStateWithWindow();
// We'll decrement win.modalDepth when we receive a unblock-modal-prompt message
// for the window.
if (!win.modalDepth) {
win.modalDepth = 0;
}
win.modalDepth++;
let origModalDepth = win.modalDepth;
let thread = Services.tm.currentThread;
debug("Nested event loop - begin");
while (win.modalDepth == origModalDepth) {
// Bail out of the loop if the inner window changed; that means the
// window navigated.
if (this._tryGetInnerWindowID(win) !== innerWindowID) {
debug("_waitForResult: Inner window ID changed " +
"while in nested event loop.");
break;
}
thread.processNextEvent(/* mayWait = */ true);
}
debug("Nested event loop - finish");
// If we exited the loop because the inner window changed, then bail on the
// modal prompt.
if (innerWindowID !== this._tryGetInnerWindowID(win)) {
throw Components.Exception("Modal state aborted by navigation",
Cr.NS_ERROR_NOT_AVAILABLE);
}
let returnValue = win.modalReturnValue;
delete win.modalReturnValue;
utils.leaveModalStateWithWindow(modalStateWin);
debug("Leaving modal state (outerID=" + outerWindowID + ", " +
"innerID=" + innerWindowID + ")");
return returnValue;
},
_recvStopWaiting: function(msg) {
let outerID = msg.json.windowID.outer;
let innerID = msg.json.windowID.inner;
let returnValue = msg.json.returnValue;
debug("recvStopWaiting(outer=" + outerID + ", inner=" + innerID +
", returnValue=" + returnValue + ")");
if (!this._windowIDDict[outerID]) {
debug("recvStopWaiting: No record of outer window ID " + outerID);
return;
}
let win = this._windowIDDict[outerID].get();
delete this._windowIDDict[outerID];
if (!win) {
debug("recvStopWaiting, but window is gone\n");
return;
}
if (innerID !== this._tryGetInnerWindowID(win)) {
debug("recvStopWaiting, but inner ID has changed\n");
return;
}
debug("recvStopWaiting " + win);
win.modalReturnValue = returnValue;
win.modalDepth--;
},
_titleChangedHandler: function(e) {
debug("Got titlechanged: (" + e.target.title + ")");
var win = e.target.defaultView;
// Ignore titlechanges which don't come from the top-level
// <iframe mozbrowser> window.
if (win == content) {
sendAsyncMsg('titlechange', e.target.title);
}
else {
debug("Not top level!");
}
},
_iconChangedHandler: function(e) {
debug("Got iconchanged: (" + e.target.href + ")");
var hasIcon = e.target.rel.split(' ').some(function(x) {
return x.toLowerCase() === 'icon';
});
if (hasIcon) {
var win = e.target.ownerDocument.defaultView;
// Ignore iconchanges which don't come from the top-level
// <iframe mozbrowser> window.
if (win == content) {
sendAsyncMsg('iconchange', e.target.href);
}
else {
debug("Not top level!");
}
}
},
_closeHandler: function(e) {
let win = e.target;
if (win != content || e.defaultPrevented) {
return;
}
debug("Closing window " + win);
sendAsyncMsg('close');
// Inform the window implementation that we handled this close ourselves.
e.preventDefault();
},
_recvGetScreenshot: function(data) {
debug("Received getScreenshot message: (" + data.json.id + ")");
var canvas = content.document
.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
var ctx = canvas.getContext("2d");
canvas.mozOpaque = true;
canvas.height = content.innerHeight;
canvas.width = content.innerWidth;
ctx.drawWindow(content, 0, 0, content.innerWidth,
content.innerHeight, "rgb(255,255,255)");
sendAsyncMsg('got-screenshot', {
id: data.json.id,
screenshot: canvas.toDataURL("image/png")
});
},
_recvSetVisible: function(data) {
debug("Received setVisible message: (" + data.json.visible + ")");
if (docShell.isActive !== data.json.visible) {
docShell.isActive = data.json.visible;
}
},
_keyEventHandler: function(e) {
if (whitelistedEvents.indexOf(e.keyCode) != -1 && !e.defaultPrevented) {
sendAsyncMsg('keyevent', {
type: e.type,
keyCode: e.keyCode,
charCode: e.charCode,
});
}
},
// The docShell keeps a weak reference to the progress listener, so we need
// to keep a strong ref to it ourselves.
_progressListener: {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference]),
_seenLoadStart: false,
onLocationChange: function(webProgress, request, location, flags) {
// We get progress events from subshells here, which is kind of weird.
if (webProgress != docShell) {
return;
}
// Ignore locationchange events which occur before the first loadstart.
// These are usually about:blank loads we don't care about.
if (!this._seenLoadStart) {
return;
}
sendAsyncMsg('locationchange', location.spec);
},
onStateChange: function(webProgress, request, stateFlags, status) {
if (webProgress != docShell) {
return;
}
if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
this._seenLoadStart = true;
sendAsyncMsg('loadstart');
}
if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
sendAsyncMsg('loadend');
}
},
onSecurityChange: function(webProgress, request, state) {
if (webProgress != docShell) {
return;
}
var stateDesc;
if (state & Ci.nsIWebProgressListener.STATE_IS_SECURE) {
stateDesc = 'secure';
}
else if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
stateDesc = 'broken';
}
else if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE) {
stateDesc = 'insecure';
}
else {
debug("Unexpected securitychange state!");
stateDesc = '???';
}
// XXX Until bug 764496 is fixed, this will always return false.
var isEV = !!(state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
sendAsyncMsg('securitychange', {state: stateDesc, extendedValidation: isEV});
},
onStatusChange: function(webProgress, request, status, message) {},
onProgressChange: function(webProgress, request, curSelfProgress,
maxSelfProgress, curTotalProgress, maxTotalProgress) {},
},
};
var api = new BrowserElementChild();

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

@ -140,6 +140,7 @@ function BrowserElementParent(frameLoader) {
}
addMessageListener("hello", this._recvHello);
addMessageListener("contextmenu", this._fireCtxMenuEvent);
addMessageListener("locationchange", this._fireEventFromMsg);
addMessageListener("loadstart", this._fireEventFromMsg);
addMessageListener("loadend", this._fireEventFromMsg);
@ -187,6 +188,25 @@ BrowserElementParent.prototype = {
debug("recvHello");
},
_fireCtxMenuEvent: function(data) {
let evtName = data.name.substring('browser-element-api:'.length);
let detail = data.json;
debug('fireCtxMenuEventFromMsg: ' + evtName + ' ' + detail);
let evt = this._createEvent(evtName, detail);
if (detail.contextmenu) {
var self = this;
XPCNativeWrapper.unwrap(evt.detail).contextMenuItemSelected = function(id) {
self._sendAsyncMsg('fire-ctx-callback', {menuitem: id});
};
}
// The embedder may have default actions on context menu events, so
// we fire a context menu event even if the child didn't define a
// custom context menu
this._frameElement.dispatchEvent(evt);
},
/**
* Fire either a vanilla or a custom event, depending on the contents of
* |data|.

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

@ -0,0 +1,298 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
let Cu = Components.utils;
let Ci = Components.interfaces;
let Cc = Components.classes;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
const BROWSER_FRAMES_ENABLED_PREF = "dom.mozBrowserFramesEnabled";
function debug(msg) {
//dump("BrowserElementParent - " + msg + "\n");
}
/**
* BrowserElementParent implements one half of <iframe mozbrowser>. (The other
* half is, unsurprisingly, BrowserElementChild.)
*
* BrowserElementParentFactory detects when we create a windows or docshell
* contained inside a <iframe mozbrowser> and creates a BrowserElementParent
* object for that window.
*
* BrowserElementParent injects script to listen for certain events in the
* child. We then listen to messages from the child script and take
* appropriate action here in the parent.
*/
function BrowserElementParentFactory() {
this._initialized = false;
}
BrowserElementParentFactory.prototype = {
classID: Components.ID("{ddeafdac-cb39-47c4-9cb8-c9027ee36d26}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
/**
* Called on app startup, and also when the browser frames enabled pref is
* changed.
*/
_init: function() {
if (this._initialized) {
return;
}
// If the pref is disabled, do nothing except wait for the pref to change.
// (This is important for tests, if nothing else.)
if (!this._browserFramesPrefEnabled()) {
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
prefs.addObserver(BROWSER_FRAMES_ENABLED_PREF, this, /* ownsWeak = */ true);
return;
}
debug("_init");
this._initialized = true;
// Maps frame elements to BrowserElementParent objects. We never look up
// anything in this map; the purpose is to keep the BrowserElementParent
// alive for as long as its frame element lives.
this._bepMap = new WeakMap();
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
os.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true);
os.addObserver(this, 'in-process-browser-frame-shown', /* ownsWeak = */ true);
},
_browserFramesPrefEnabled: function() {
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
try {
return prefs.getBoolPref(BROWSER_FRAMES_ENABLED_PREF);
}
catch(e) {
return false;
}
},
_observeInProcessBrowserFrameShown: function(frameLoader) {
debug("In-process browser frame shown " + frameLoader);
this._createBrowserElementParent(frameLoader);
},
_observeRemoteBrowserFrameShown: function(frameLoader) {
debug("Remote browser frame shown " + frameLoader);
this._createBrowserElementParent(frameLoader);
},
_createBrowserElementParent: function(frameLoader) {
let frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
this._bepMap.set(frameElement, new BrowserElementParent(frameLoader));
},
observe: function(subject, topic, data) {
switch(topic) {
case 'app-startup':
this._init();
break;
case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
if (data == BROWSER_FRAMES_ENABLED_PREF) {
this._init();
}
break;
case 'remote-browser-frame-shown':
this._observeRemoteBrowserFrameShown(subject);
break;
case 'in-process-browser-frame-shown':
this._observeInProcessBrowserFrameShown(subject);
break;
case 'content-document-global-created':
this._observeContentGlobalCreated(subject);
break;
}
},
};
function BrowserElementParent(frameLoader) {
debug("Creating new BrowserElementParent object for " + frameLoader);
this._screenshotListeners = {};
this._screenshotReqCounter = 0;
this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
if (!this._frameElement) {
debug("No frame element?");
return;
}
this._mm = frameLoader.messageManager;
// Messages we receive are handed to functions which take a (data) argument,
// where |data| is the message manager's data object.
let self = this;
function addMessageListener(msg, handler) {
self._mm.addMessageListener('browser-element-api:' + msg, handler.bind(self));
}
addMessageListener("hello", this._recvHello);
addMessageListener("locationchange", this._fireEventFromMsg);
addMessageListener("loadstart", this._fireEventFromMsg);
addMessageListener("loadend", this._fireEventFromMsg);
addMessageListener("titlechange", this._fireEventFromMsg);
addMessageListener("iconchange", this._fireEventFromMsg);
addMessageListener("close", this._fireEventFromMsg);
addMessageListener("securitychange", this._fireEventFromMsg);
addMessageListener("get-mozapp-manifest-url", this._sendMozAppManifestURL);
addMessageListener("keyevent", this._fireKeyEvent);
addMessageListener("showmodalprompt", this._handleShowModalPrompt);
addMessageListener('got-screenshot', this._recvGotScreenshot);
function defineMethod(name, fn) {
XPCNativeWrapper.unwrap(self._frameElement)[name] = fn.bind(self);
}
// Define methods on the frame element.
defineMethod('getScreenshot', this._getScreenshot);
defineMethod('setVisible', this._setVisible);
this._mm.loadFrameScript("chrome://global/content/BrowserElementChild.js",
/* allowDelayedLoad = */ true);
}
BrowserElementParent.prototype = {
get _window() {
return this._frameElement.ownerDocument.defaultView;
},
_sendAsyncMsg: function(msg, data) {
this._frameElement.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager
.sendAsyncMessage('browser-element-api:' + msg, data);
},
_recvHello: function(data) {
debug("recvHello");
},
/**
* Fire either a vanilla or a custom event, depending on the contents of
* |data|.
*/
_fireEventFromMsg: function(data) {
let name = data.name.substring('browser-element-api:'.length);
let detail = data.json;
debug('fireEventFromMsg: ' + name + ', ' + detail);
let evt = this._createEvent(name, detail,
/* cancelable = */ false);
this._frameElement.dispatchEvent(evt);
},
_handleShowModalPrompt: function(data) {
// Fire a showmodalprmopt event on the iframe. When this method is called,
// the child is spinning in a nested event loop waiting for an
// unblock-modal-prompt message.
//
// If the embedder calls preventDefault() on the showmodalprompt event,
// we'll block the child until event.detail.unblock() is called.
//
// Otherwise, if preventDefault() is not called, we'll send the
// unblock-modal-prompt message to the child as soon as the event is done
// dispatching.
let detail = data.json;
debug('handleShowPrompt ' + JSON.stringify(detail));
// Strip off the windowID property from the object we send along in the
// event.
let windowID = detail.windowID;
delete detail.windowID;
debug("Event will have detail: " + JSON.stringify(detail));
let evt = this._createEvent('showmodalprompt', detail,
/* cancelable = */ true);
let self = this;
let unblockMsgSent = false;
function sendUnblockMsg() {
if (unblockMsgSent) {
return;
}
unblockMsgSent = true;
// We don't need to sanitize evt.detail.returnValue (e.g. converting the
// return value of confirm() to a boolean); Gecko does that for us.
let data = { windowID: windowID,
returnValue: evt.detail.returnValue };
self._sendAsyncMsg('unblock-modal-prompt', data);
}
XPCNativeWrapper.unwrap(evt.detail).unblock = function() {
sendUnblockMsg();
};
this._frameElement.dispatchEvent(evt);
if (!evt.defaultPrevented) {
// Unblock the inner frame immediately. Otherwise we'll unblock upon
// evt.detail.unblock().
sendUnblockMsg();
}
},
_createEvent: function(evtName, detail, cancelable) {
// This will have to change if we ever want to send a CustomEvent with null
// detail. For now, it's OK.
if (detail !== undefined && detail !== null) {
return new this._window.CustomEvent('mozbrowser' + evtName,
{ bubbles: true,
cancelable: cancelable,
detail: detail });
}
return new this._window.Event('mozbrowser' + evtName,
{ bubbles: true,
cancelable: cancelable });
},
_sendMozAppManifestURL: function(data) {
return this._frameElement.getAttribute('mozapp');
},
_getScreenshot: function() {
let id = 'req_' + this._screenshotReqCounter++;
let req = Services.DOMRequest.createRequest(this._window);
this._screenshotListeners[id] = req;
this._sendAsyncMsg('get-screenshot', {id: id});
return req;
},
_recvGotScreenshot: function(data) {
var req = this._screenshotListeners[data.json.id];
delete this._screenshotListeners[data.json.id];
Services.DOMRequest.fireSuccess(req, data.json.screenshot);
},
_setVisible: function(visible) {
this._sendAsyncMsg('set-visible', {visible: visible});
},
_fireKeyEvent: function(data) {
let evt = this._window.document.createEvent("KeyboardEvent");
evt.initKeyEvent(data.json.type, true, true, this._window,
false, false, false, false, // modifiers
data.json.keyCode,
data.json.charCode);
this._frameElement.dispatchEvent(evt);
},
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParentFactory]);

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

@ -66,6 +66,8 @@ _TEST_FILES = \
test_browserElement_inproc_SecurityChange.html \
file_browserElement_SecurityChange.html \
browserElement_BackForward.js \
browserElement_ContextmenuEvents.js \
test_browserElement_inproc_ContextmenuEvents.html \
$(NULL)
# OOP tests don't work on Windows (bug 763081).
@ -91,6 +93,7 @@ _TEST_FILES += \
test_browserElement_oop_OpenWindowRejected.html \
test_browserElement_oop_SecurityChange.html \
test_browserElement_oop_BackForward.html \
test_browserElement_oop_ContextmenuEvents.html \
$(NULL)
endif

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

@ -0,0 +1,137 @@
"use strict";
SimpleTest.waitForExplicitFinish();
var iframeScript = function() {
content.fireContextMenu = function(element) {
var ev = content.document.createEvent('HTMLEvents');
ev.initEvent('contextmenu', true, false);
element.dispatchEvent(ev);
}
XPCNativeWrapper.unwrap(content).ctxCallbackFired = function(data) {
sendAsyncMessage('test:callbackfired', {data: data});
}
XPCNativeWrapper.unwrap(content).onerror = function(e) {
sendAsyncMessage('test:errorTriggered', {data: e});
}
content.fireContextMenu(content.document.body);
content.fireContextMenu(content.document.getElementById('menu1-trigger'));
content.fireContextMenu(content.document.getElementById('inner-link'));
content.fireContextMenu(content.document.getElementById('menu2-trigger'));
}
var trigger1 = function() {
content.fireContextMenu(content.document.getElementById('menu1-trigger'));
};
function runTest() {
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addToWhitelist();
var iframe1 = document.createElement('iframe');
iframe1.mozbrowser = true;
document.body.appendChild(iframe1);
iframe1.src = 'data:text/html,<html>' +
'<body>' +
'<menu type="context" id="menu1" label="firstmenu">' +
'<menuitem label="foo" onclick="window.ctxCallbackFired(\'foo\')"></menuitem>' +
'<menuitem label="bar" onclick="throw(\'anerror\')"></menuitem>' +
'</menu>' +
'<menu type="context" id="menu2" label="secondmenu">' +
'<menuitem label="outer" onclick="window.ctxCallbackFired(\'err\')"></menuitem>' +
'<menu>' +
'<menuitem label="inner 1"></menuitem>' +
'<menuitem label="inner 2" onclick="window.ctxCallbackFired(\'inner2\')"></menuitem>' +
'</menu>' +
'</menu>' +
'<div id="menu1-trigger" contextmenu="menu1"><a id="inner-link" href="foo.html">Menu 1</a></div>' +
'<a href="bar.html" contextmenu="menu2"><img id="menu2-trigger" src="example.png" /></a>' +
'</body></html>';
var mm;
var numIframeLoaded = 0;
var ctxMenuEvents = 0;
var ctxCallbackEvents = 0;
var cachedCtxDetail = null;
// We fire off various contextmenu events to check the data that gets
// passed to the handler
function iframeContextmenuHandler(e) {
var detail = e.detail;
ctxMenuEvents++;
if (ctxMenuEvents === 1) {
ok(detail.contextmenu === null, 'body context clicks have no context menu');
} else if (ctxMenuEvents === 2) {
cachedCtxDetail = detail;
ok(detail.contextmenu.items.length === 2, 'trigger custom contextmenu');
} else if (ctxMenuEvents === 3) {
ok(detail.systemTargets.length === 1, 'Includes anchor data');
ok(detail.contextmenu.items.length === 2, 'Inner clicks trigger correct menu');
} else if (ctxMenuEvents === 4) {
var innerMenu = detail.contextmenu.items.filter(function(x) {
return x.type === 'menu';
});
ok(detail.systemTargets.length === 2, 'Includes anchor and img data');
ok(innerMenu.length > 0, 'Menu contains a nested menu');
ok(true, 'Got correct number of contextmenu events');
// Finished testing the data passed to the contextmenu handler,
// now we start selecting contextmenu items
// This is previously triggered contextmenu data, since we have
// fired subsequent contextmenus this should not be mistaken
// for a current menuitem
var prevId = cachedCtxDetail.contextmenu.items[0].id;
cachedCtxDetail.contextMenuItemSelected(prevId);
// This triggers a current menuitem
detail.contextMenuItemSelected(innerMenu[0].items[1].id);
// Once an item it selected, subsequent selections are ignored
detail.contextMenuItemSelected(innerMenu[0].items[0].id);
} else if (ctxMenuEvents === 5) {
ok(detail.contextmenu.label === 'firstmenu', 'Correct menu enabled');
detail.contextMenuItemSelected(detail.contextmenu.items[0].id);
} else if (ctxMenuEvents === 6) {
detail.contextMenuItemSelected(detail.contextmenu.items[1].id);
} else if (ctxMenuEvents > 6) {
ok(false, 'Too many events');
}
}
function ctxCallbackRecieved(msg) {
ctxCallbackEvents++;
if (ctxCallbackEvents === 1) {
ok(msg.json.data === 'inner2', 'Callback function got fired correctly');
mm.loadFrameScript('data:,(' + trigger1.toString() + ')();', false);
} else if (ctxCallbackEvents === 2) {
ok(msg.json.data === 'foo', 'Callback function got fired correctly');
mm.loadFrameScript('data:,(' + trigger1.toString() + ')();', false);
} else if (ctxCallbackEvents > 2) {
ok(false, 'Too many callback events');
}
}
function errorTriggered(msg) {
ok(true, 'An error in the callback triggers window.onerror');
SimpleTest.finish();
}
function iframeLoadedHandler() {
numIframeLoaded++;
if (numIframeLoaded === 2) {
mm = SpecialPowers.getBrowserFrameMessageManager(iframe1);
mm.addMessageListener('test:callbackfired', ctxCallbackRecieved);
mm.addMessageListener('test:errorTriggered', errorTriggered);
mm.loadFrameScript('data:,(' + iframeScript.toString() + ')();', false);
}
}
iframe1.addEventListener('mozbrowsercontextmenu', iframeContextmenuHandler);
iframe1.addEventListener('mozbrowserloadend', iframeLoadedHandler);
}
addEventListener('load', function() { SimpleTest.executeSoon(runTest); });

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

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Contextmenu Events</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_ContextmenuEvents.js">
</script>
</body>
</html>

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

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Contextmenu Events</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_ContextmenuEvents.js">
</script>
</body>
</html>

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

@ -291,21 +291,5 @@
"Command outdent, value \"\": input event, uncanceled":true,
"Command outdent, value \"quasit\": beforeinput event, canceled":true,
"Command outdent, value \"quasit\": beforeinput event, uncanceled":true,
"Command outdent, value \"quasit\": input event, uncanceled":true,
"Command redo, value \"\": beforeinput event, canceled":true,
"Command redo, value \"\": input event, canceled":true,
"Command redo, value \"\": beforeinput event, uncanceled":true,
"Command redo, value \"\": input event, uncanceled":true,
"Command redo, value \"quasit\": beforeinput event, canceled":true,
"Command redo, value \"quasit\": input event, canceled":true,
"Command redo, value \"quasit\": beforeinput event, uncanceled":true,
"Command redo, value \"quasit\": input event, uncanceled":true,
"Command undo, value \"\": beforeinput event, canceled":true,
"Command undo, value \"\": input event, canceled":true,
"Command undo, value \"\": beforeinput event, uncanceled":true,
"Command undo, value \"\": input event, uncanceled":true,
"Command undo, value \"quasit\": beforeinput event, canceled":true,
"Command undo, value \"quasit\": input event, canceled":true,
"Command undo, value \"quasit\": beforeinput event, uncanceled":true,
"Command undo, value \"quasit\": input event, uncanceled":true
"Command outdent, value \"quasit\": input event, uncanceled":true
}

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

@ -109,21 +109,14 @@
"[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<p>[foo</p><p> <span>bar</span> </p><p>baz]</p>\" queryCommandIndeterm(\"bold\") before":true,
"[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<p>[foo</p><p> <span>bar</span> </p><p>baz]</p>\" queryCommandIndeterm(\"bold\") before":true,
"[[\"bold\",\"\"]] \"<span>foo[</span><span>]bar</span>\" queryCommandState(\"bold\") after":true,
"[[\"bold\",\"\"]] \"foo<span contenteditable=false>[bar]</span>baz\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"fo[o<span contenteditable=false>bar</span>b]az\" compare innerHTML":true,
"[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"fo[o<span contenteditable=false>bar</span>b]az\" compare innerHTML":true,
"[[\"bold\",\"\"]] \"foo<span contenteditable=false>ba[r</span>b]az\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"bold\",\"\"]] \"fo[o<span contenteditable=false>b]ar</span>baz\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\" compare innerHTML":true,
"[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\" queryCommandState(\"bold\") after":true,
"[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\" compare innerHTML":true,
"[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\" queryCommandState(\"bold\") after":true,
"[[\"bold\",\"\"]] \"<span contenteditable=false>fo[o<span contenteditable=true>bar</span>b]az</span>\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>ba[r</span>b]az</span>\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"bold\",\"\"]] \"<span contenteditable=false>fo[o<span contenteditable=true>b]ar</span>baz</span>\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"bold\",\"\"]] \"<span contenteditable=false>fo[<b>o<span contenteditable=true>bar</span>b</b>]az</span>\": execCommand(\"bold\", false, \"\") return value":true,
"[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<table><tbody data-start=0 data-end=1><tr><td>foo<td>bar<td>baz</table>\" queryCommandIndeterm(\"bold\") before":true,
"[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<table><tbody data-start=0 data-end=1><tr><td>foo<td>bar<td>baz</table>\" queryCommandIndeterm(\"bold\") before":true,
"[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<table data-start=0 data-end=1><tbody><tr><td>foo<td>bar<td>baz</table>\" queryCommandIndeterm(\"bold\") before":true,
@ -1339,10 +1332,8 @@
"[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<span style=\\\"font-family: monospace\\\">b[a]r</span>baz\" compare innerHTML":true,
"[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<span style=\\\"font-family: monospace\\\">b[a]r</span>baz\" queryCommandValue(\"fontname\") before":true,
"[[\"stylewithcss\",\"false\"],[\"fontname\",\"sans-serif\"]] \"foo<span style=\\\"font-family: monospace\\\">b[a]r</span>baz\" queryCommandValue(\"fontname\") before":true,
"[[\"fontname\",\"sans-serif\"]] \"foo<tt contenteditable=false>ba[r</tt>b]az\": execCommand(\"fontname\", false, \"sans-serif\") return value":true,
"[[\"fontname\",\"sans-serif\"]] \"foo<tt contenteditable=false>ba[r</tt>b]az\" queryCommandValue(\"fontname\") before":true,
"[[\"fontname\",\"sans-serif\"]] \"foo<tt contenteditable=false>ba[r</tt>b]az\" queryCommandValue(\"fontname\") after":true,
"[[\"fontname\",\"sans-serif\"]] \"fo[o<tt contenteditable=false>b]ar</tt>baz\": execCommand(\"fontname\", false, \"sans-serif\") return value":true,
"[[\"fontname\",\"sans-serif\"]] \"fo[o<tt contenteditable=false>b]ar</tt>baz\" queryCommandValue(\"fontname\") before":true,
"[[\"fontname\",\"sans-serif\"]] \"fo[o<tt contenteditable=false>b]ar</tt>baz\" queryCommandValue(\"fontname\") after":true,
"[[\"fontname\",\"sans-serif\"]] \"foo<tt>{}<br></tt>bar\" queryCommandValue(\"fontname\") before":true,

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

@ -1714,7 +1714,7 @@ IDBObjectStore::IndexInternal(const nsAString& aName,
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!mTransaction->IsOpen()) {
if (mTransaction->IsFinished()) {
return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
}

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

@ -499,10 +499,7 @@ IDBTransaction::AbortWithCode(nsresult aAbortCode)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// We can't use IsOpen here since we need it to be possible to call Abort()
// even from outside of transaction callbacks.
if (mReadyState != IDBTransaction::INITIAL &&
mReadyState != IDBTransaction::LOADING) {
if (IsFinished()) {
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
@ -683,7 +680,7 @@ IDBTransaction::ObjectStoreInternal(const nsAString& aName,
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!IsOpen()) {
if (IsFinished()) {
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
@ -819,6 +816,10 @@ CommitHelper::Run()
}
NS_ENSURE_TRUE(event, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (mListener) {
mListener->NotifyTransactionPreComplete(mTransaction);
}
bool dummy;
if (NS_FAILED(mTransaction->DispatchEvent(event, &dummy))) {
NS_WARNING("Dispatch failed!");
@ -828,9 +829,8 @@ CommitHelper::Run()
mTransaction->mFiredCompleteOrAbort = true;
#endif
// Tell the listener (if we have one) that we're done
if (mListener) {
mListener->NotifyTransactionComplete(mTransaction);
mListener->NotifyTransactionPostComplete(mTransaction);
}
mTransaction = nsnull;

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

@ -45,7 +45,10 @@ public:
NS_IMETHOD_(nsrefcnt) AddRef() = 0;
NS_IMETHOD_(nsrefcnt) Release() = 0;
virtual nsresult NotifyTransactionComplete(IDBTransaction* aTransaction) = 0;
// Called just before dispatching the final events on the transaction.
virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction) = 0;
// Called just after dispatching the final events on the transaction.
virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction) = 0;
};
class IDBTransaction : public IDBWrapperCache,
@ -122,6 +125,11 @@ public:
bool IsOpen() const;
bool IsFinished() const
{
return mReadyState > LOADING;
}
bool IsWriteAllowed() const
{
return mMode == READ_WRITE || mMode == VERSION_CHANGE;

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

@ -1352,8 +1352,10 @@ protected:
// Need an upgradeneeded event here.
virtual already_AddRefed<nsDOMEvent> CreateSuccessEvent() MOZ_OVERRIDE;
virtual nsresult NotifyTransactionComplete(IDBTransaction* aTransaction)
MOZ_OVERRIDE;
virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction)
MOZ_OVERRIDE;
virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction)
MOZ_OVERRIDE;
virtual ChildProcessSendResult
MaybeSendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE
@ -1956,12 +1958,6 @@ OpenDatabaseHelper::Run()
switch (mState) {
case eSetVersionCompleted: {
// Allow transaction creation/other version change transactions to proceed
// before we fire events. Other version changes will be postd to the end
// of the event loop, and will be behind whatever the page does in
// its error/success event handlers.
mDatabase->ExitSetVersionTransaction();
mState = eFiringEvents;
break;
}
@ -2134,6 +2130,9 @@ OpenDatabaseHelper::NotifySetVersionFinished()
NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
NS_ASSERTION(mState = eSetVersionPending, "How did we get here?");
// Allow transaction creation to proceed.
mDatabase->ExitSetVersionTransaction();
mState = eSetVersionCompleted;
// Dispatch ourself back to the main thread
@ -2292,7 +2291,17 @@ SetVersionHelper::CreateSuccessEvent()
}
nsresult
SetVersionHelper::NotifyTransactionComplete(IDBTransaction* aTransaction)
SetVersionHelper::NotifyTransactionPreComplete(IDBTransaction* aTransaction)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aTransaction, "This is unexpected.");
NS_ASSERTION(mOpenRequest, "Why don't we have a request?");
return mOpenHelper->NotifySetVersionFinished();
}
nsresult
SetVersionHelper::NotifyTransactionPostComplete(IDBTransaction* aTransaction)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aTransaction, "This is unexpected.");
@ -2312,7 +2321,6 @@ SetVersionHelper::NotifyTransactionComplete(IDBTransaction* aTransaction)
mOpenRequest->SetTransaction(nsnull);
mOpenRequest = nsnull;
rv = mOpenHelper->NotifySetVersionFinished();
mOpenHelper = nsnull;
return rv;

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

@ -337,7 +337,6 @@ IndexedDBDatabaseChild::RecvSuccess(
if (openHelper) {
request->Reset();
database->ExitSetVersionTransaction();
}
else {
openHelper = new IPCOpenDatabaseHelper(mDatabase, request);
@ -370,7 +369,6 @@ IndexedDBDatabaseChild::RecvError(const nsresult& aRv)
if (openHelper) {
request->Reset();
database->ExitSetVersionTransaction();
}
else {
openHelper = new IPCOpenDatabaseHelper(NULL, request);
@ -540,12 +538,9 @@ IndexedDBTransactionChild::FireCompleteEvent(nsresult aRv)
nsRefPtr<IDBTransaction> transaction;
mStrongTransaction.swap(transaction);
// This is where we should allow the database to start issuing new
// transactions once we fix the main thread. E.g.:
//
// if (transaction->GetMode() == IDBTransaction::VERSION_CHANGE) {
// transaction->Database()->ExitSetVersionTransaction();
// }
if (transaction->GetMode() == IDBTransaction::VERSION_CHANGE) {
transaction->Database()->ExitSetVersionTransaction();
}
nsRefPtr<CommitHelper> helper = new CommitHelper(transaction, aRv);

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

@ -71,6 +71,7 @@
is(db.objectStoreNames.length, 1, "Correct objectStoreNames length");
ok(db.objectStoreNames.contains("foo"), "Has correct objectStore");
let originalRequest = request;
request = objectStore.add({}, 1);
request.onsuccess = grabEventAndContinueHandler;
request.onerror = errorHandler;
@ -88,10 +89,13 @@
is(db.objectStoreNames.length, 1, "Correct objectStoreNames length");
is(trans.error.name, "ConstraintError", "Right error");
ok(trans.error === request.error, "Object identity holds");
is(originalRequest.transaction, trans, "request.transaction should still be set");
event = yield;
is(event.type, "error", "Got request error event");
is(event.target, originalRequest, "error event has right target");
is(event.target.error.name, "ConstraintError", "Right error");
is(originalRequest.transaction, null, "request.transaction should now be null");
let request = mozIndexedDB.open(window.location.pathname, 1);
request.onerror = errorHandler;

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

@ -7,7 +7,17 @@ var testGenerator = testSteps();
function executeSoon(aFun)
{
SimpleTest.executeSoon(aFun);
let comp = SpecialPowers.wrap(Components);
let thread = comp.classes["@mozilla.org/thread-manager;1"]
.getService(comp.interfaces.nsIThreadManager)
.mainThread;
thread.dispatch({
run: function() {
aFun();
}
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}
function clearAllDatabases(callback) {

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

@ -48,14 +48,11 @@ function testSteps()
event = yield;
is(event.type, "complete", "Got complete event");
// The database is still not fully open here.
try {
db.transaction("foo");
ok(false, "Transactions should be disallowed now!");
ok(true, "Transactions should be allowed now!");
} catch (e) {
ok(e instanceof DOMException, "Expect a DOMException");
is(e.name, "InvalidStateError", "Expect an InvalidStateError");
is(e.code, DOMException.INVALID_STATE_ERR, "Expect an INVALID_STATE_ERR");
ok(false, "Transactions should be allowed now!");
}
request.onsuccess = grabEventAndContinueHandler;

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

@ -10,17 +10,69 @@ function testSteps()
let request = mozIndexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
let event = yield;
let db = event.target.result;
db.onerror = errorHandler;
event.target.onsuccess = continueToNextStep;
event.target.transaction.onerror = errorHandler;
event.target.transaction.oncomplete = grabEventAndContinueHandler;
db.createObjectStore("foo", { autoIncrement: true });
let os = db.createObjectStore("foo", { autoIncrement: true });
let index = os.createIndex("bar", "foo.bar");
event = yield;
is(request.transaction, event.target,
"request.transaction should still be set");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(request.transaction, null, "request.transaction should be cleared");
let transaction = db.transaction("foo", "readwrite");
os = transaction.objectStore("foo");
// Place a request to keep the transaction alive long enough for our
// executeSoon.
let requestComplete = false;
let wasAbleToGrabObjectStoreOutsideOfCallback = false;
let wasAbleToGrabIndexOutsideOfCallback = false;
executeSoon(function() {
ok(!requestComplete, "Ordering is correct.");
wasAbleToGrabObjectStoreOutsideOfCallback = !!transaction.objectStore("foo");
wasAbleToGrabIndexOutsideOfCallback =
!!transaction.objectStore("foo").index("bar");
});
request = os.add({});
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
requestComplete = true;
ok(wasAbleToGrabObjectStoreOutsideOfCallback,
"Should be able to get objectStore");
ok(wasAbleToGrabIndexOutsideOfCallback,
"Should be able to get index");
transaction.oncomplete = grabEventAndContinueHandler;
yield;
let transaction = db.transaction("foo");
try {
transaction.objectStore("foo");
ok(false, "Should have thrown!");
}
catch (e) {
ok(e instanceof DOMException, "Got database exception.");
is(e.name, "InvalidStateError", "Good error.");
is(e.code, DOMException.INVALID_STATE_ERR, "Good error code.");
}
continueToNextStep();
yield;

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

@ -66,7 +66,7 @@ mOwner(owner)
// construct the URL we'll use later in calls to GetURL()
NS_GetURLSpecFromFile(mTempFile, mFileURL);
#ifdef NS_DEBUG
#ifdef DEBUG
printf("File URL = %s\n", mFileURL.get());
#endif
}

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

@ -1636,7 +1636,7 @@ nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
aMimeType, pluginTag->mFileName.get()));
#ifdef NS_DEBUG
#ifdef DEBUG
if (aMimeType && !pluginTag->mFileName.IsEmpty())
printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get());
#endif

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

@ -219,7 +219,7 @@ void nsPluginNativeWindowGtk2::SetAllocation() {
nsresult nsPluginNativeWindowGtk2::CreateXtWindow() {
NS_ASSERTION(!mSocketWidget,"Already created a socket widget!");
#ifdef NS_DEBUG
#ifdef DEBUG
printf("About to create new xtbin of %i X %i from %p...\n",
width, height, (void*)window);
#endif
@ -235,11 +235,11 @@ nsresult nsPluginNativeWindowGtk2::CreateXtWindow() {
gtk_widget_set_size_request(mSocketWidget, width, height);
#ifdef NS_DEBUG
#ifdef DEBUG
printf("About to show xtbin(%p)...\n", (void*)mSocketWidget); fflush(NULL);
#endif
gtk_widget_show(mSocketWidget);
#ifdef NS_DEBUG
#ifdef DEBUG
printf("completed gtk_widget_show(%p)\n", (void*)mSocketWidget); fflush(NULL);
#endif

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

@ -303,7 +303,7 @@ nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
pLibrary = *outLibrary;
#endif // MOZ_WIDGET_GTK2
#ifdef NS_DEBUG
#ifdef DEBUG
printf("LoadPlugin() %s returned %lx\n",
libSpec.value.pathname, (unsigned long)pLibrary);
#endif

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

@ -24,7 +24,7 @@
#include "xpcpublic.h"
#include "nsJSEnvironment.h"
#include "nsDOMJSUtils.h"
#ifdef NS_DEBUG
#ifdef DEBUG
#include "nspr.h" // PR_fprintf
@ -174,7 +174,7 @@ nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent)
// mContext is the same context which event listener manager pushes
// to JS context stack.
#ifdef NS_DEBUG
#ifdef DEBUG
JSContext* cx = nsnull;
nsCOMPtr<nsIJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1");

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

@ -65,6 +65,10 @@ interface nsIRILTelephonyCallback : nsISupports
[scriptable, uuid(8a711703-1ee5-4675-9d9a-0b188e944cfe)]
interface nsIRILDataCallInfo : nsISupports
{
/**
* Current data call state, one of the
* nsINetworkInterface::NETWORK_STATE_* constants.
*/
readonly attribute unsigned long state;
readonly attribute AString cid;
readonly attribute AString apn;
@ -217,7 +221,7 @@ interface nsIRilContext : nsISupports
readonly attribute nsIDOMMozMobileConnectionInfo data;
};
[scriptable, uuid(92bea0af-8d75-4592-87d0-1cab88e36904)]
[scriptable, uuid(8b649965-6687-46a8-88fa-a5495ce90735)]
interface nsIRadioInterfaceLayer : nsISupports
{
const unsigned short CALL_STATE_UNKNOWN = 0;
@ -233,13 +237,6 @@ interface nsIRadioInterfaceLayer : nsISupports
const unsigned short CALL_STATE_DISCONNECTED = 10;
const unsigned short CALL_STATE_INCOMING = 11;
// Keep consistent with GECKO_DATACALL_STATE_* values in ril_consts.js
const unsigned short DATACALL_STATE_UNKNOWN = 0;
const unsigned short DATACALL_STATE_CONNECTING = 1;
const unsigned short DATACALL_STATE_CONNECTED = 2;
const unsigned short DATACALL_STATE_DISCONNECTING = 3;
const unsigned short DATACALL_STATE_DISCONNECTED = 4;
/**
* Activates or deactivates radio power.
*/

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

@ -1756,7 +1756,7 @@ let RIL = {
* String containing PDP type to request. ("IP", "IPV6", ...)
*/
setupDataCall: function setupDataCall(options) {
let token = Buf.newParcel(REQUEST_SETUP_DATA_CALL);
let token = Buf.newParcel(REQUEST_SETUP_DATA_CALL, options);
Buf.writeUint32(7);
Buf.writeString(options.radioTech.toString());
Buf.writeString(DATACALL_PROFILE_DEFAULT.toString());
@ -3297,41 +3297,43 @@ RIL[REQUEST_QUERY_CLIP] = null;
RIL[REQUEST_LAST_DATA_CALL_FAIL_CAUSE] = null;
RIL.readDataCall_v5 = function readDataCall_v5() {
return {
cid: Buf.readUint32().toString(),
active: Buf.readUint32(), // DATACALL_ACTIVE_*
type: Buf.readString(),
apn: Buf.readString(),
address: Buf.readString()
};
if (!options) {
options = {};
}
cid = Buf.readUint32().toString();
active = Buf.readUint32(); // DATACALL_ACTIVE_*
type = Buf.readString();
apn = Buf.readString();
address = Buf.readString();
return options;
};
RIL.readDataCall_v6 = function readDataCall_v6(obj) {
if (!obj) {
obj = {};
RIL.readDataCall_v6 = function readDataCall_v6(options) {
if (!options) {
options = {};
}
obj.status = Buf.readUint32(); // DATACALL_FAIL_*
options.status = Buf.readUint32(); // DATACALL_FAIL_*
if (!RILQUIRKS_DATACALLSTATE_NO_SUGGESTEDRETRYTIME) {
obj.suggestedRetryTime = Buf.readUint32();
options.suggestedRetryTime = Buf.readUint32();
}
obj.cid = Buf.readUint32().toString();
obj.active = Buf.readUint32(); // DATACALL_ACTIVE_*
obj.type = Buf.readString();
obj.ifname = Buf.readString();
obj.ipaddr = Buf.readString();
obj.dns = Buf.readString();
obj.gw = Buf.readString();
if (obj.dns) {
obj.dns = obj.dns.split(" ");
options.cid = Buf.readUint32().toString();
options.active = Buf.readUint32(); // DATACALL_ACTIVE_*
options.type = Buf.readString();
options.ifname = Buf.readString();
options.ipaddr = Buf.readString();
options.dns = Buf.readString();
options.gw = Buf.readString();
if (options.dns) {
options.dns = options.dns.split(" ");
}
//TODO for now we only support one address and gateway
if (obj.ipaddr) {
obj.ipaddr = obj.ipaddr.split(" ")[0];
if (options.ipaddr) {
options.ipaddr = options.ipaddr.split(" ")[0];
}
if (obj.gw) {
obj.gw = obj.gw.split(" ")[0];
if (options.gw) {
options.gw = options.gw.split(" ")[0];
}
return obj;
return options;
};
RIL[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(length, options) {
@ -3354,9 +3356,9 @@ RIL[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(length, options) {
for (let i = 0; i < num; i++) {
let datacall;
if (version < 6) {
datacall = this.readDataCall_v5();
datacall = this.readDataCall_v5(options);
} else {
datacall = this.readDataCall_v6();
datacall = this.readDataCall_v6(options);
}
datacalls[datacall.cid] = datacall;
}

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

@ -493,18 +493,14 @@ nsOutdentCommand::IsCommandEnabled(const char * aCommandName,
nsISupports *refCon,
bool *outCmdEnabled)
{
*outCmdEnabled = false;
nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(refCon);
if (editor && htmlEditor)
{
bool canIndent, isEditable = false;
nsresult rv = editor->GetIsSelectionEditable(&isEditable);
if (editor) {
nsresult rv = editor->GetIsSelectionEditable(outCmdEnabled);
NS_ENSURE_SUCCESS(rv, rv);
if (isEditable)
return htmlEditor->GetIndentState(&canIndent, outCmdEnabled);
}
*outCmdEnabled = false;
return NS_OK;
}

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

@ -184,12 +184,9 @@ nsSetDocumentStateCommand::IsCommandEnabled(const char * aCommandName,
nsISupports *refCon,
bool *outCmdEnabled)
{
// These commands are always enabled
NS_ENSURE_ARG_POINTER(outCmdEnabled);
nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
if (editor)
return editor->GetIsSelectionEditable(outCmdEnabled);
*outCmdEnabled = false;
*outCmdEnabled = true;
return NS_OK;
}

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

@ -13,7 +13,7 @@
#include "mozilla/dom/Element.h"
#ifdef NS_DEBUG
#ifdef DEBUG
static bool gNoisy = false;
#endif
@ -60,7 +60,7 @@ NS_IMETHODIMP CreateElementTxn::Init(nsEditor *aEditor,
NS_IMETHODIMP CreateElementTxn::DoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
char* nodename = ToNewCString(mTag);
@ -84,7 +84,7 @@ NS_IMETHODIMP CreateElementTxn::DoTransaction(void)
// Try to insert formatting whitespace for the new node:
mEditor->MarkNodeDirty(mNewNode);
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf(" newNode = %p\n", static_cast<void*>(mNewNode.get()));
@ -134,7 +134,7 @@ NS_IMETHODIMP CreateElementTxn::DoTransaction(void)
NS_IMETHODIMP CreateElementTxn::UndoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("Undo Create Element, mParent = %p, node = %p\n",
@ -152,7 +152,7 @@ NS_IMETHODIMP CreateElementTxn::UndoTransaction(void)
NS_IMETHODIMP CreateElementTxn::RedoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy) { printf("Redo Create Element\n"); }
#endif

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

@ -8,11 +8,11 @@
#include "DeleteElementTxn.h"
#include "nsSelectionState.h"
#ifdef NS_DEBUG
#ifdef DEBUG
#include "nsIDOMElement.h"
#endif
#ifdef NS_DEBUG
#ifdef DEBUG
static bool gNoisy = false;
#endif
@ -67,7 +67,7 @@ NS_IMETHODIMP DeleteElementTxn::Init(nsIEditor *aEditor,
NS_IMETHODIMP DeleteElementTxn::DoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("%p Do Delete Element element = %p\n",
@ -80,7 +80,7 @@ NS_IMETHODIMP DeleteElementTxn::DoTransaction(void)
if (!mParent) { return NS_OK; } // this is a no-op, there's no parent to delete mElement from
#ifdef NS_DEBUG
#ifdef DEBUG
// begin debug output
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mElement);
nsAutoString elementTag(NS_LITERAL_STRING("text node"));
@ -118,7 +118,7 @@ NS_IMETHODIMP DeleteElementTxn::DoTransaction(void)
NS_IMETHODIMP DeleteElementTxn::UndoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("%p Undo Delete Element element = %p, parent = %p\n",
@ -131,7 +131,7 @@ NS_IMETHODIMP DeleteElementTxn::UndoTransaction(void)
if (!mParent) { return NS_OK; } // this is a legal state, the txn is a no-op
if (!mElement) { return NS_ERROR_NULL_POINTER; }
#ifdef NS_DEBUG
#ifdef DEBUG
// begin debug output
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mElement);
nsAutoString elementTag(NS_LITERAL_STRING("text node"));
@ -161,7 +161,7 @@ NS_IMETHODIMP DeleteElementTxn::UndoTransaction(void)
NS_IMETHODIMP DeleteElementTxn::RedoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("%p Redo Delete Element element = %p, parent = %p\n",

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

@ -16,7 +16,7 @@
using namespace mozilla;
#ifdef NS_DEBUG
#ifdef DEBUG
static bool gNoisy = false;
#endif
@ -110,7 +110,7 @@ NS_IMETHODIMP DeleteRangeTxn::Init(nsEditor *aEditor,
NS_IMETHODIMP DeleteRangeTxn::DoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy) { printf("Do Delete Range\n"); }
#endif
@ -171,7 +171,7 @@ NS_IMETHODIMP DeleteRangeTxn::DoTransaction(void)
NS_IMETHODIMP DeleteRangeTxn::UndoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy) { printf("Undo Delete Range\n"); }
#endif
@ -182,7 +182,7 @@ NS_IMETHODIMP DeleteRangeTxn::UndoTransaction(void)
NS_IMETHODIMP DeleteRangeTxn::RedoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy) { printf("Redo Delete Range\n"); }
#endif

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

@ -9,7 +9,7 @@
#include "nsIDOMNodeList.h"
#include "nsReadableUtils.h"
#ifdef NS_DEBUG
#ifdef DEBUG
static bool gNoisy = false;
#endif
@ -55,7 +55,7 @@ NS_IMETHODIMP InsertElementTxn::Init(nsIDOMNode *aNode,
NS_IMETHODIMP InsertElementTxn::DoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
nsCOMPtr<nsIContent>nodeAsContent = do_QueryInterface(mNode);
@ -116,7 +116,7 @@ NS_IMETHODIMP InsertElementTxn::DoTransaction(void)
NS_IMETHODIMP InsertElementTxn::UndoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("%p Undo Insert Element of %p into parent %p at offset %d\n",

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

@ -8,7 +8,7 @@
#include "nsISelection.h"
#include "EditAggregateTxn.h"
#ifdef NS_DEBUG
#ifdef DEBUG
static bool gNoisy = false;
#endif
@ -62,7 +62,7 @@ NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement,
NS_IMETHODIMP InsertTextTxn::DoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("Do Insert Text element = %p\n",
@ -98,7 +98,7 @@ NS_IMETHODIMP InsertTextTxn::DoTransaction(void)
NS_IMETHODIMP InsertTextTxn::UndoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("Undo Insert Text element = %p\n",
@ -133,7 +133,7 @@ NS_IMETHODIMP InsertTextTxn::Merge(nsITransaction *aTransaction, bool *aDidMerge
otherInsTxn->GetData(otherData);
mStringToInsert += otherData;
*aDidMerge = true;
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("InsertTextTxn assimilated %p\n",

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

@ -8,7 +8,7 @@
#include "nsIDOMNodeList.h"
#include "nsIDOMCharacterData.h"
#ifdef NS_DEBUG
#ifdef DEBUG
static bool gNoisy = false;
#endif
@ -56,7 +56,7 @@ NS_IMETHODIMP JoinElementTxn::Init(nsEditor *aEditor,
// After DoTransaction() and RedoTransaction(), the left node is removed from the content tree and right node remains.
NS_IMETHODIMP JoinElementTxn::DoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("%p Do Join of %p and %p\n",
@ -108,7 +108,7 @@ NS_IMETHODIMP JoinElementTxn::DoTransaction(void)
// and re-inserted mLeft?
NS_IMETHODIMP JoinElementTxn::UndoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("%p Undo Join, right node = %p\n",

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

@ -9,7 +9,7 @@
#include "nsISelection.h"
#include "nsIDOMCharacterData.h"
#ifdef NS_DEBUG
#ifdef DEBUG
static bool gNoisy = false;
#endif
@ -52,7 +52,7 @@ NS_IMETHODIMP SplitElementTxn::Init(nsEditor *aEditor,
NS_IMETHODIMP SplitElementTxn::DoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("%p Do Split of node %p offset %d\n",
@ -72,7 +72,7 @@ NS_IMETHODIMP SplitElementTxn::DoTransaction(void)
NS_ENSURE_TRUE(mNewLeftNode, NS_ERROR_NULL_POINTER);
mEditor->MarkNodeDirty(mExistingRightNode);
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf(" created left node = %p\n",
@ -108,7 +108,7 @@ NS_IMETHODIMP SplitElementTxn::DoTransaction(void)
NS_IMETHODIMP SplitElementTxn::UndoTransaction(void)
{
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy) {
printf("%p Undo Split of existing node %p and new node %p offset %d\n",
static_cast<void*>(this),
@ -125,7 +125,7 @@ NS_IMETHODIMP SplitElementTxn::UndoTransaction(void)
// this assumes Do inserted the new node in front of the prior existing node
nsresult result = mEditor->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent, false);
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("** after join left child node %p into right node %p\n",
@ -156,7 +156,7 @@ NS_IMETHODIMP SplitElementTxn::RedoTransaction(void)
return NS_ERROR_NOT_INITIALIZED;
}
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy) {
printf("%p Redo Split of existing node %p and new node %p offset %d\n",
static_cast<void*>(this),
@ -174,7 +174,7 @@ NS_IMETHODIMP SplitElementTxn::RedoTransaction(void)
if (rightNodeAsText)
{
result = rightNodeAsText->DeleteData(0, mOffset);
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("** after delete of text in right text node %p offset %d\n",
@ -199,7 +199,7 @@ NS_IMETHODIMP SplitElementTxn::RedoTransaction(void)
if (NS_SUCCEEDED(result))
{
result = mNewLeftNode->AppendChild(child, getter_AddRefs(resultNode));
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("** move child node %p from right node %p to left node %p\n",
@ -215,7 +215,7 @@ NS_IMETHODIMP SplitElementTxn::RedoTransaction(void)
}
// second, re-insert the left node into the tree
result = mParent->InsertBefore(mNewLeftNode, mExistingRightNode, getter_AddRefs(resultNode));
#ifdef NS_DEBUG
#ifdef DEBUG
if (gNoisy)
{
printf("** reinsert left child node %p before right node %p\n",

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

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<script>
function boom()
{
var r = document.createRange();
r.setEnd(document.createTextNode("x"), 0);
window.getSelection().addRange(r);
document.execCommand("inserthtml", false, "y");
}
</script>
</head>
<body contenteditable="true" onload="boom();"></body>
</html>

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

@ -11,4 +11,5 @@ load 636074-1.html
load 713427-1.html
load 713427-2.xhtml
load 762183.html
load 766360.html
load 766413.html

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

@ -4377,25 +4377,25 @@ NS_IMETHODIMP
nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
nsIDOMNode ** aNewNode)
{
nsCOMPtr<nsIDOMNode> parentSelectedNode;
PRInt32 offsetOfNewNode;
nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode,
offsetOfNewNode);
nsresult result = DeleteSelectionAndPrepareToCreateNode();
NS_ENSURE_SUCCESS(result, result);
nsRefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsINode> node = selection->GetAnchorNode();
PRInt32 offset = selection->GetAnchorOffset();
nsCOMPtr<nsIDOMNode> newNode;
result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode,
result = CreateNode(aTag, node->AsDOMNode(), offset,
getter_AddRefs(newNode));
// XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly in success/failure cases
// XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly
// in success/failure cases
*aNewNode = newNode;
NS_IF_ADDREF(*aNewNode);
// we want the selection to be just after the new node
nsCOMPtr<nsISelection> selection;
result = GetSelection(getter_AddRefs(selection));
NS_ENSURE_SUCCESS(result, result);
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
return selection->Collapse(parentSelectedNode, offsetOfNewNode+1);
return selection->Collapse(node, offset + 1);
}
@ -4442,86 +4442,55 @@ nsEditor::IsIMEComposing() {
return mIsIMEComposing;
}
NS_IMETHODIMP
nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode)
nsresult
nsEditor::DeleteSelectionAndPrepareToCreateNode()
{
nsresult result=NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsISelection> selection;
result = GetSelection(getter_AddRefs(selection));
NS_ENSURE_SUCCESS(result, result);
nsresult res;
nsRefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
if (!selection->Collapsed()) {
result = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
if (NS_FAILED(result)) {
return result;
}
// get the new selection
result = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(result)) {
return result;
}
res = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsIDOMNode> selectedNode;
selection->GetAnchorNode(getter_AddRefs(selectedNode));
// no selection is ok.
// if there is a selection, it must be collapsed
if (selectedNode && !selection->Collapsed()) {
result = selection->CollapseToEnd();
NS_ENSURE_SUCCESS(result, result);
MOZ_ASSERT(selection->Collapsed(),
"Selection not collapsed after delete");
}
// If the selection is a chardata node, split it if necessary and compute
// where to put the new node
nsCOMPtr<nsINode> node = selection->GetAnchorNode();
MOZ_ASSERT(node, "Selection has no ranges in it");
if (node && node->IsNodeOfType(nsINode::eDATA_NODE)) {
NS_ASSERTION(node->GetNodeParent(),
"It's impossible to insert into chardata with no parent -- "
"fix the caller");
NS_ENSURE_STATE(node->GetNodeParent());
PRInt32 offset = selection->GetAnchorOffset();
if (offset == 0) {
res = selection->Collapse(node->GetNodeParent(),
node->GetNodeParent()->IndexOf(node));
MOZ_ASSERT(NS_SUCCEEDED(res));
NS_ENSURE_SUCCESS(res, res);
} else if (offset == (PRInt32)node->Length()) {
res = selection->Collapse(node->GetNodeParent(),
node->GetNodeParent()->IndexOf(node) + 1);
MOZ_ASSERT(NS_SUCCEEDED(res));
NS_ENSURE_SUCCESS(res, res);
} else {
nsCOMPtr<nsIDOMNode> tmp;
res = SplitNode(node->AsDOMNode(), offset, getter_AddRefs(tmp));
NS_ENSURE_SUCCESS(res, res);
res = selection->Collapse(node->GetNodeParent(),
node->GetNodeParent()->IndexOf(node));
MOZ_ASSERT(NS_SUCCEEDED(res));
NS_ENSURE_SUCCESS(res, res);
}
}
// split the selected node
PRInt32 offsetOfSelectedNode;
result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
{
nsCOMPtr<nsIDOMNode> selectedNode;
PRUint32 selectedNodeContentCount=0;
nsCOMPtr<nsIDOMCharacterData>selectedParentNodeAsText;
selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
offsetOfNewNode = offsetOfSelectedNode;
/* if the selection is a text node, split the text node if necessary
and compute where to put the new node
*/
if (selectedParentNodeAsText)
{
PRInt32 indexOfTextNodeInParent;
selectedNode = do_QueryInterface(parentSelectedNode);
selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
{
nsCOMPtr<nsIDOMNode> newSiblingNode;
result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
// now get the node's offset in its parent, and insert the new tag there
if (NS_SUCCEEDED(result)) {
result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
}
}
else
{ // determine where to insert the new node
if (0==offsetOfSelectedNode) {
offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
}
else { // insert new node as last child
GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node
}
}
}
// Here's where the new node was inserted
}
#ifdef DEBUG
else {
printf("InsertLineBreak into an empty document is not yet supported\n");
}
#endif
return result;
return NS_OK;
}

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

@ -314,8 +314,15 @@ protected:
nsIDOMNode *aRightNode,
JoinElementTxn **aTxn);
NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode,
PRInt32& offsetOfNewNode);
/**
* This method first deletes the selection, if it's not collapsed. Then if
* the selection lies in a CharacterData node, it splits it. If the
* selection is at this point collapsed in a CharacterData node, it's
* adjusted to be collapsed right before or after the node instead (which is
* always possible, since the node was split).
*/
nsresult DeleteSelectionAndPrepareToCreateNode();
// called after a transaction is done successfully
NS_IMETHOD DoAfterDoTransaction(nsITransaction *aTxn);

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

@ -527,68 +527,60 @@ nsSwitchTextDirectionCommand::GetCommandStateParams(const char *aCommandName,
}
NS_IMETHODIMP
nsDeleteCommand::IsCommandEnabled(const char * aCommandName,
nsISupports *aCommandRefCon,
bool *outCmdEnabled)
nsDeleteCommand::IsCommandEnabled(const char* aCommandName,
nsISupports* aCommandRefCon,
bool* outCmdEnabled)
{
NS_ENSURE_ARG_POINTER(outCmdEnabled);
nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
*outCmdEnabled = false;
// we can delete when we can cut
NS_ENSURE_TRUE(editor, NS_OK);
bool isEditable = false;
nsresult rv = editor->GetIsSelectionEditable(&isEditable);
// We can generally delete whenever the selection is editable. However,
// cmd_delete doesn't make sense if the selection is collapsed because it's
// directionless, which is the same condition under which we can't cut.
nsresult rv = editor->GetIsSelectionEditable(outCmdEnabled);
NS_ENSURE_SUCCESS(rv, rv);
if (!isEditable)
return NS_OK;
else if (!nsCRT::strcmp(aCommandName,"cmd_delete"))
return editor->CanCut(outCmdEnabled);
else if (!nsCRT::strcmp(aCommandName,"cmd_forwardDelete"))
return editor->CanCut(outCmdEnabled);
else if (!nsCRT::strcmp(aCommandName,"cmd_deleteCharBackward"))
*outCmdEnabled = true;
else if (!nsCRT::strcmp(aCommandName,"cmd_deleteCharForward"))
*outCmdEnabled = true;
else if (!nsCRT::strcmp(aCommandName,"cmd_deleteWordBackward"))
*outCmdEnabled = true;
else if (!nsCRT::strcmp(aCommandName,"cmd_deleteWordForward"))
*outCmdEnabled = true;
else if (!nsCRT::strcmp(aCommandName,"cmd_deleteToBeginningOfLine"))
*outCmdEnabled = true;
else if (!nsCRT::strcmp(aCommandName,"cmd_deleteToEndOfLine"))
*outCmdEnabled = true;
if (!nsCRT::strcmp("cmd_delete", aCommandName) && *outCmdEnabled) {
rv = editor->CanCut(outCmdEnabled);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMETHODIMP
nsDeleteCommand::DoCommand(const char *aCommandName, nsISupports *aCommandRefCon)
nsDeleteCommand::DoCommand(const char* aCommandName,
nsISupports* aCommandRefCon)
{
nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
NS_ENSURE_TRUE(editor, NS_ERROR_FAILURE);
nsIEditor::EDirection deleteDir = nsIEditor::eNone;
if (!nsCRT::strcmp("cmd_delete",aCommandName))
if (!nsCRT::strcmp("cmd_delete", aCommandName)) {
// Really this should probably be eNone, but it only makes a difference if
// the selection is collapsed, and then this command is disabled. So let's
// keep it as it always was to avoid breaking things.
deleteDir = nsIEditor::ePrevious;
else if (!nsCRT::strcmp("cmd_forwardDelete", aCommandName))
} else if (!nsCRT::strcmp("cmd_deleteCharForward", aCommandName)) {
deleteDir = nsIEditor::eNext;
else if (!nsCRT::strcmp("cmd_deleteCharBackward",aCommandName))
} else if (!nsCRT::strcmp("cmd_deleteCharBackward", aCommandName)) {
deleteDir = nsIEditor::ePrevious;
else if (!nsCRT::strcmp("cmd_deleteCharForward",aCommandName))
deleteDir = nsIEditor::eNext;
else if (!nsCRT::strcmp("cmd_deleteWordBackward",aCommandName))
} else if (!nsCRT::strcmp("cmd_deleteWordBackward", aCommandName)) {
deleteDir = nsIEditor::ePreviousWord;
else if (!nsCRT::strcmp("cmd_deleteWordForward",aCommandName))
} else if (!nsCRT::strcmp("cmd_deleteWordForward", aCommandName)) {
deleteDir = nsIEditor::eNextWord;
else if (!nsCRT::strcmp("cmd_deleteToBeginningOfLine",aCommandName))
} else if (!nsCRT::strcmp("cmd_deleteToBeginningOfLine", aCommandName)) {
deleteDir = nsIEditor::eToBeginningOfLine;
else if (!nsCRT::strcmp("cmd_deleteToEndOfLine",aCommandName))
} else if (!nsCRT::strcmp("cmd_deleteToEndOfLine", aCommandName)) {
deleteDir = nsIEditor::eToEndOfLine;
} else {
MOZ_NOT_REACHED("Unrecognized nsDeleteCommand");
}
return editor->DeleteSelection(deleteDir, nsIEditor::eStrip);
}

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

@ -58,7 +58,6 @@ nsresult nsEditorController::RegisterEditingCommands(nsIControllerCommandTable *
NS_REGISTER_ONE_COMMAND(nsSwitchTextDirectionCommand, "cmd_switchTextDirection");
NS_REGISTER_FIRST_COMMAND(nsDeleteCommand, "cmd_delete");
NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_forwardDelete");
NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_deleteCharBackward");
NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_deleteCharForward");
NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_deleteWordBackward");

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

@ -25,7 +25,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=408231
["createlink", "true"],
["cut", "false"],
["decreasefontsize", "true"],
["delete", "false"],
["delete", "true"],
["fontname", "true"],
["fontsize", "true"],
["formatblock", "true"],
@ -44,7 +44,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=408231
["justifyfull", "true"],
["justifyleft", "true"],
["justifyright", "true"],
["outdent", "false"],
["outdent", "true"],
//["paste", "true"],
["redo", "false"],
["removeformat", "true"],

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

@ -6,7 +6,7 @@
#ifndef __TextEditorTest_h__
#define __TextEditorTest_h__
#ifdef NS_DEBUG
#ifdef DEBUG
#include "nsCOMPtr.h"
#include "nsIEditor.h"
@ -35,6 +35,6 @@ protected:
nsCOMPtr<nsIEditor> mEditor;
};
#endif /* NS_DEBUG */
#endif /* DEBUG */
#endif

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

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<script>
function boom()
{
window.getSelection().removeAllRanges();
var r = document.createRange();
r.setStart(document.getElementById("x"), 1);
r.setEnd(document.getElementById("y"), 0);
window.getSelection().addRange(r);
document.execCommand("insertorderedlist", false, null);
}
</script>
</head>
<body onload="boom();"><div id="x" contenteditable="true">a</div><div id="y" contenteditable="true"></div></body>
</html>

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

@ -28,5 +28,6 @@ load 682650-1.html
load 716456-1.html
load 759748.html
load 761861.html
load 766795.html
load 766305.html
load 766387.html
load 766795.html

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

@ -361,7 +361,7 @@ nsHTMLEditor::DoInsertHTMLWithContext(const nsAString & aInputString,
if (!cellSelectionMode)
{
rv = DeleteSelectionAndPrepareToCreateNode(parentNode, offsetOfNewNode);
rv = DeleteSelectionAndPrepareToCreateNode();
NS_ENSURE_SUCCESS(rv, rv);
// pasting does not inherit local inline styles

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

@ -1720,9 +1720,7 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, bool aDeleteSele
NS_ENSURE_SUCCESS(res, res);
}
nsCOMPtr<nsIDOMNode> tempNode;
PRInt32 tempOffset;
nsresult result = DeleteSelectionAndPrepareToCreateNode(tempNode,tempOffset);
nsresult result = DeleteSelectionAndPrepareToCreateNode();
NS_ENSURE_SUCCESS(result, result);
}
@ -3598,6 +3596,36 @@ nsHTMLEditor::IsModifiableNode(nsINode *aNode)
return !aNode || aNode->IsEditable();
}
NS_IMETHODIMP
nsHTMLEditor::GetIsSelectionEditable(bool* aIsSelectionEditable)
{
MOZ_ASSERT(aIsSelectionEditable);
nsRefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
// Per the editing spec as of June 2012: we have to have a selection whose
// start and end nodes are editable, and which share an ancestor editing
// host. (Bug 766387.)
*aIsSelectionEditable = selection->GetRangeCount() &&
selection->GetAnchorNode()->IsEditable() &&
selection->GetFocusNode()->IsEditable();
if (*aIsSelectionEditable) {
nsINode* commonAncestor =
selection->GetAnchorFocusRange()->GetCommonAncestor();
while (commonAncestor && !commonAncestor->IsEditable()) {
commonAncestor = commonAncestor->GetNodeParent();
}
if (!commonAncestor) {
// No editable common ancestor
*aIsSelectionEditable = false;
}
}
return NS_OK;
}
static nsresult
SetSelectionAroundHeadChildren(nsISelection* aSelection,
nsIWeakReference* aDocWeak)

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

@ -310,6 +310,8 @@ public:
NS_IMETHOD_(bool) IsModifiableNode(nsIDOMNode *aNode);
virtual bool IsModifiableNode(nsINode *aNode);
NS_IMETHOD GetIsSelectionEditable(bool* aIsSelectionEditable);
NS_IMETHOD SelectAll();
NS_IMETHOD GetRootElement(nsIDOMElement **aRootElement);

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

@ -283,9 +283,6 @@ const knownFailures = {
"QE-Proposed-INSERTLINEBREAK_TEXT-1-dM": true,
"QE-Proposed-INSERTLINEBREAK_TEXT-1-body": true,
"QE-Proposed-INSERTLINEBREAK_TEXT-1-div": true,
"QE-Proposed-OUTDENT_TEXT-1-dM": true,
"QE-Proposed-OUTDENT_TEXT-1-body": true,
"QE-Proposed-OUTDENT_TEXT-1-div": true,
"QE-Proposed-CREATEBOOKMARK_TEXT-1-dM": true,
"QE-Proposed-CREATEBOOKMARK_TEXT-1-body": true,
"QE-Proposed-CREATEBOOKMARK_TEXT-1-div": true,

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

@ -26,11 +26,11 @@ addLoadEvent(function() {
document.querySelector("input").focus();
var threw = false;
try {
document.execCommand("inserthtml", null, "<span>f" + "oo</span>");
is(document.execCommand("inserthtml", null, "<span>f" + "oo</span>"),
false, "The insertHTML command should return false");
} catch (e) {
threw = true;
ok(false, "insertHTML should not throw here");
}
ok(threw, "The inserthtml command should fail");
is(document.querySelectorAll("span").length, 0, "No span element should be injected inside the page");
is(document.body.innerHTML.indexOf("f" + "oo"), -1, "No text should be injected inside the page");
SimpleTest.finish();

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

@ -32,13 +32,24 @@ SimpleTest.waitForFocus(runTests);
var gBlock1, gBlock2;
function IsCommandEnabled(command, alwaysEnabled) {
var alwaysEnabledCommands = [
"contentReadOnly",
"copy",
"enableInlineTableEditing",
"enableObjectResizing",
"insertBrOnReturn",
"selectAll",
"styleWithCSS",
];
function IsCommandEnabled(command) {
var enabled;
// non-editable div: should return false unless alwaysEnabled
window.getSelection().selectAllChildren(gBlock1);
enabled = document.queryCommandEnabled(command);
is(enabled, alwaysEnabled, "'" + command + "' should not be enabled on a non-editable block.");
is(enabled, alwaysEnabledCommands.indexOf(command) != -1,
"'" + command + "' should not be enabled on a non-editable block.");
// editable div: should return true
window.getSelection().selectAllChildren(gBlock2);
@ -67,15 +78,15 @@ function runTests() {
];
document.execCommand("styleWithCSS", false, false);
for (i = 0; i < commands.length; i++)
IsCommandEnabled(commands[i], commands[i] == "selectAll");
IsCommandEnabled(commands[i]);
document.execCommand("styleWithCSS", false, true);
for (i = 0; i < commands.length; i++)
IsCommandEnabled(commands[i], commands[i] == "selectAll");
IsCommandEnabled(commands[i]);
// Mozilla-specific stuff
commands = ["enableInlineTableEditing", "enableObjectResizing", "insertBrOnReturn"];
for (i = 0; i < commands.length; i++)
IsCommandEnabled(commands[i], false);
IsCommandEnabled(commands[i]);
// cut/copy/paste -- SpecialPowers required
SpecialPowers.setCharPref("capability.policy.policynames", "allowclipboard");
@ -84,7 +95,7 @@ function runTests() {
SpecialPowers.setCharPref("capability.policy.allowclipboard.Clipboard.paste", "allAccess");
commands = ["cut", "paste", "copy"];
for (i = 0; i < commands.length; i++) {
IsCommandEnabled(commands[i], commands[i] == "copy");
IsCommandEnabled(commands[i]);
document.execCommand(commands[i], false, false);
}
SpecialPowers.clearUserPref("capability.policy.policynames");
@ -97,7 +108,7 @@ function runTests() {
// * there's nothing to redo if we haven't undone something first
commands = ["delete", "undo", "redo"];
for (i = 0; i < commands.length; i++) {
IsCommandEnabled(commands[i], false);
IsCommandEnabled(commands[i]);
document.execCommand(commands[i], false, false);
}

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

@ -80,7 +80,7 @@ NS_IMETHODIMP nsAppStartupNotifier::Observe(nsISupports *aSubject, const char *a
}
}
else {
#ifdef NS_DEBUG
#ifdef DEBUG
nsCAutoString warnStr("Cannot create startup observer : ");
warnStr += contractId.get();
NS_WARNING(warnStr.get());

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

@ -874,9 +874,9 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont,
IDWriteRenderingParams *params = NULL;
if (aRenderOptions) {
if (aRenderOptions->GetType() != FONT_DWRITE) {
gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
// This should never happen.
MOZ_ASSERT(false);
gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
// This should never happen.
MOZ_ASSERT(false);
} else {
params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderOptions)->mParams;
}
@ -895,7 +895,12 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aFont,
PrepareForDrawing(rt);
rt->SetTextRenderingParams(params);
if (rt != mRT || params != mTextRenderingParams) {
rt->SetTextRenderingParams(params);
if (rt == mRT) {
mTextRenderingParams = params;
}
}
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);

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