Merge m-c to b2g-inbound. a=merge
@ -296,6 +296,7 @@
|
|||||||
@BINPATH@/components/toolkit_finalizationwitness.xpt
|
@BINPATH@/components/toolkit_finalizationwitness.xpt
|
||||||
@BINPATH@/components/toolkit_formautofill.xpt
|
@BINPATH@/components/toolkit_formautofill.xpt
|
||||||
@BINPATH@/components/toolkit_osfile.xpt
|
@BINPATH@/components/toolkit_osfile.xpt
|
||||||
|
@BINPATH@/components/toolkit_xulstore.xpt
|
||||||
@BINPATH@/components/toolkitprofile.xpt
|
@BINPATH@/components/toolkitprofile.xpt
|
||||||
#ifdef MOZ_ENABLE_XREMOTE
|
#ifdef MOZ_ENABLE_XREMOTE
|
||||||
@BINPATH@/components/toolkitremote.xpt
|
@BINPATH@/components/toolkitremote.xpt
|
||||||
@ -540,6 +541,8 @@
|
|||||||
#endif
|
#endif
|
||||||
@BINPATH@/components/TelemetryStartup.js
|
@BINPATH@/components/TelemetryStartup.js
|
||||||
@BINPATH@/components/TelemetryStartup.manifest
|
@BINPATH@/components/TelemetryStartup.manifest
|
||||||
|
@BINPATH@/components/XULStore.js
|
||||||
|
@BINPATH@/components/XULStore.manifest
|
||||||
@BINPATH@/components/Webapps.js
|
@BINPATH@/components/Webapps.js
|
||||||
@BINPATH@/components/Webapps.manifest
|
@BINPATH@/components/Webapps.manifest
|
||||||
@BINPATH@/components/AppsService.js
|
@BINPATH@/components/AppsService.js
|
||||||
@ -692,9 +695,6 @@
|
|||||||
@BINPATH@/res/text_caret_tilt_right@1.5x.png
|
@BINPATH@/res/text_caret_tilt_right@1.5x.png
|
||||||
@BINPATH@/res/text_caret_tilt_right@2.25x.png
|
@BINPATH@/res/text_caret_tilt_right@2.25x.png
|
||||||
@BINPATH@/res/text_caret_tilt_right@2x.png
|
@BINPATH@/res/text_caret_tilt_right@2x.png
|
||||||
@BINPATH@/res/text_selection_handle.png
|
|
||||||
@BINPATH@/res/text_selection_handle@1.5.png
|
|
||||||
@BINPATH@/res/text_selection_handle@2.png
|
|
||||||
@BINPATH@/res/grabber.gif
|
@BINPATH@/res/grabber.gif
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
@BINPATH@/res/cursors/*
|
@BINPATH@/res/cursors/*
|
||||||
|
@ -9,6 +9,21 @@ var FullScreen = {
|
|||||||
delete this._fullScrToggler;
|
delete this._fullScrToggler;
|
||||||
return this._fullScrToggler = document.getElementById("fullscr-toggler");
|
return this._fullScrToggler = document.getElementById("fullscr-toggler");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
// called when we go into full screen, even if initiated by a web page script
|
||||||
|
window.addEventListener("fullscreen", this, true);
|
||||||
|
window.messageManager.addMessageListener("MozEnteredDomFullscreen", this);
|
||||||
|
|
||||||
|
if (window.fullScreen)
|
||||||
|
this.toggle();
|
||||||
|
},
|
||||||
|
|
||||||
|
uninit: function() {
|
||||||
|
window.messageManager.removeMessageListener("MozEnteredDomFullscreen", this);
|
||||||
|
this.cleanup();
|
||||||
|
},
|
||||||
|
|
||||||
toggle: function (event) {
|
toggle: function (event) {
|
||||||
var enterFS = window.fullScreen;
|
var enterFS = window.fullScreen;
|
||||||
|
|
||||||
@ -95,9 +110,12 @@ var FullScreen = {
|
|||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "activate":
|
case "activate":
|
||||||
if (document.mozFullScreen) {
|
if (document.mozFullScreen) {
|
||||||
this.showWarning(this.fullscreenDoc);
|
this.showWarning(this.fullscreenOrigin);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "fullscreen":
|
||||||
|
this.toggle(event);
|
||||||
|
break;
|
||||||
case "transitionend":
|
case "transitionend":
|
||||||
if (event.propertyName == "opacity")
|
if (event.propertyName == "opacity")
|
||||||
this.cancelWarning();
|
this.cancelWarning();
|
||||||
@ -105,18 +123,33 @@ var FullScreen = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
enterDomFullscreen : function(event) {
|
receiveMessage: function(aMessage) {
|
||||||
|
if (aMessage.name == "MozEnteredDomFullscreen") {
|
||||||
|
// If we're a multiprocess browser, then the request to enter fullscreen
|
||||||
|
// did not bubble up to the root browser document - it stopped at the root
|
||||||
|
// of the content document. That means we have to kick off the switch to
|
||||||
|
// fullscreen here at the operating system level in the parent process
|
||||||
|
// ourselves.
|
||||||
|
let data = aMessage.data;
|
||||||
|
let browser = aMessage.target;
|
||||||
|
if (gMultiProcessBrowser && browser.getAttribute("remote") == "true") {
|
||||||
|
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils);
|
||||||
|
windowUtils.remoteFrameFullscreenChanged(browser, data.origin);
|
||||||
|
}
|
||||||
|
this.enterDomFullscreen(browser, data.origin);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
enterDomFullscreen : function(aBrowser, aOrigin) {
|
||||||
if (!document.mozFullScreen)
|
if (!document.mozFullScreen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// However, if we receive a "MozEnteredDomFullScreen" event for a document
|
// If we've received a fullscreen notification, we have to ensure that the
|
||||||
// which is not a subdocument of a currently active (ie. visible) browser
|
// element that's requesting fullscreen belongs to the browser that's currently
|
||||||
// or iframe, we know that we've switched to a different frame since the
|
// active. If not, we exit fullscreen since the "full-screen document" isn't
|
||||||
// request to enter full-screen was made, so we should exit full-screen
|
// actually visible now.
|
||||||
// since the "full-screen document" isn't acutally visible.
|
if (gBrowser.selectedBrowser != aBrowser) {
|
||||||
if (!event.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Ci.nsIWebNavigation)
|
|
||||||
.QueryInterface(Ci.nsIDocShell).isActive) {
|
|
||||||
document.mozCancelFullScreen();
|
document.mozCancelFullScreen();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -136,7 +169,7 @@ var FullScreen = {
|
|||||||
if (gFindBarInitialized)
|
if (gFindBarInitialized)
|
||||||
gFindBar.close();
|
gFindBar.close();
|
||||||
|
|
||||||
this.showWarning(event.target);
|
this.showWarning(aOrigin);
|
||||||
|
|
||||||
// Exit DOM full-screen mode upon open, close, or change tab.
|
// Exit DOM full-screen mode upon open, close, or change tab.
|
||||||
gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
|
gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
|
||||||
@ -178,7 +211,9 @@ var FullScreen = {
|
|||||||
gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
|
gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
|
||||||
if (!this.useLionFullScreen)
|
if (!this.useLionFullScreen)
|
||||||
window.removeEventListener("activate", this);
|
window.removeEventListener("activate", this);
|
||||||
this.fullscreenDoc = null;
|
|
||||||
|
window.messageManager
|
||||||
|
.broadcastAsyncMessage("DOMFullscreen:Cleanup");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -337,7 +372,7 @@ var FullScreen = {
|
|||||||
// the permission manager can't handle (documents with URIs without a host).
|
// the permission manager can't handle (documents with URIs without a host).
|
||||||
// We simply require those to be approved every time instead.
|
// We simply require those to be approved every time instead.
|
||||||
let rememberCheckbox = document.getElementById("full-screen-remember-decision");
|
let rememberCheckbox = document.getElementById("full-screen-remember-decision");
|
||||||
let uri = this.fullscreenDoc.nodePrincipal.URI;
|
let uri = BrowserUtils.makeURI(this.fullscreenOrigin);
|
||||||
if (!rememberCheckbox.hidden) {
|
if (!rememberCheckbox.hidden) {
|
||||||
if (rememberCheckbox.checked)
|
if (rememberCheckbox.checked)
|
||||||
Services.perms.add(uri,
|
Services.perms.add(uri,
|
||||||
@ -370,27 +405,29 @@ var FullScreen = {
|
|||||||
// If the document has been granted fullscreen, notify Gecko so it can resume
|
// If the document has been granted fullscreen, notify Gecko so it can resume
|
||||||
// any pending pointer lock requests, otherwise exit fullscreen; the user denied
|
// any pending pointer lock requests, otherwise exit fullscreen; the user denied
|
||||||
// the fullscreen request.
|
// the fullscreen request.
|
||||||
if (isApproved)
|
if (isApproved) {
|
||||||
Services.obs.notifyObservers(this.fullscreenDoc, "fullscreen-approved", "");
|
gBrowser.selectedBrowser
|
||||||
else
|
.messageManager
|
||||||
|
.sendAsyncMessage("DOMFullscreen:Approved");
|
||||||
|
} else {
|
||||||
document.mozCancelFullScreen();
|
document.mozCancelFullScreen();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
warningBox: null,
|
warningBox: null,
|
||||||
warningFadeOutTimeout: null,
|
warningFadeOutTimeout: null,
|
||||||
fullscreenDoc: null,
|
|
||||||
|
|
||||||
// Shows the fullscreen approval UI, or if the domain has already been approved
|
// Shows the fullscreen approval UI, or if the domain has already been approved
|
||||||
// for fullscreen, shows a warning that the site has entered fullscreen for a short
|
// for fullscreen, shows a warning that the site has entered fullscreen for a short
|
||||||
// duration.
|
// duration.
|
||||||
showWarning: function(targetDoc) {
|
showWarning: function(aOrigin) {
|
||||||
if (!document.mozFullScreen ||
|
if (!document.mozFullScreen ||
|
||||||
!gPrefService.getBoolPref("full-screen-api.approval-required"))
|
!gPrefService.getBoolPref("full-screen-api.approval-required"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Set the strings on the fullscreen approval UI.
|
// Set the strings on the fullscreen approval UI.
|
||||||
this.fullscreenDoc = targetDoc;
|
this.fullscreenOrigin = aOrigin;
|
||||||
let uri = this.fullscreenDoc.nodePrincipal.URI;
|
let uri = BrowserUtils.makeURI(aOrigin);
|
||||||
let host = null;
|
let host = null;
|
||||||
try {
|
try {
|
||||||
host = uri.host;
|
host = uri.host;
|
||||||
|
@ -19,10 +19,6 @@ var FullZoom = {
|
|||||||
// browser.zoom.updateBackgroundTabs preference cache
|
// browser.zoom.updateBackgroundTabs preference cache
|
||||||
updateBackgroundTabs: undefined,
|
updateBackgroundTabs: undefined,
|
||||||
|
|
||||||
// One of the possible values for the mousewheel.* preferences.
|
|
||||||
// From EventStateManager.h.
|
|
||||||
ACTION_ZOOM: 3,
|
|
||||||
|
|
||||||
// This maps the browser to monotonically increasing integer
|
// This maps the browser to monotonically increasing integer
|
||||||
// tokens. _browserTokenMap[browser] is increased each time the zoom is
|
// tokens. _browserTokenMap[browser] is increased each time the zoom is
|
||||||
// changed in the browser. See _getBrowserToken and _ignorePendingZoomAccesses.
|
// changed in the browser. See _getBrowserToken and _ignorePendingZoomAccesses.
|
||||||
@ -49,8 +45,7 @@ var FullZoom = {
|
|||||||
// Initialization & Destruction
|
// Initialization & Destruction
|
||||||
|
|
||||||
init: function FullZoom_init() {
|
init: function FullZoom_init() {
|
||||||
// Listen for scrollwheel events so we can save scrollwheel-based changes.
|
gBrowser.addEventListener("ZoomChangeUsingMouseWheel", this);
|
||||||
window.addEventListener("DOMMouseScroll", this, false);
|
|
||||||
|
|
||||||
// Register ourselves with the service so we know when our pref changes.
|
// Register ourselves with the service so we know when our pref changes.
|
||||||
this._cps2 = Cc["@mozilla.org/content-pref/service;1"].
|
this._cps2 = Cc["@mozilla.org/content-pref/service;1"].
|
||||||
@ -81,7 +76,7 @@ var FullZoom = {
|
|||||||
destroy: function FullZoom_destroy() {
|
destroy: function FullZoom_destroy() {
|
||||||
gPrefService.removeObserver("browser.zoom.", this);
|
gPrefService.removeObserver("browser.zoom.", this);
|
||||||
this._cps2.removeObserverForName(this.name, this);
|
this._cps2.removeObserverForName(this.name, this);
|
||||||
window.removeEventListener("DOMMouseScroll", this, false);
|
gBrowser.removeEventListener("ZoomChangeUsingMouseWheel", this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@ -92,58 +87,14 @@ var FullZoom = {
|
|||||||
|
|
||||||
handleEvent: function FullZoom_handleEvent(event) {
|
handleEvent: function FullZoom_handleEvent(event) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "DOMMouseScroll":
|
case "ZoomChangeUsingMouseWheel":
|
||||||
this._handleMouseScrolled(event);
|
let browser = this._getTargetedBrowser(event);
|
||||||
|
this._ignorePendingZoomAccesses(browser);
|
||||||
|
this._applyZoomToPref(browser);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleMouseScrolled: function FullZoom__handleMouseScrolled(event) {
|
|
||||||
// Construct the "mousewheel action" pref key corresponding to this event.
|
|
||||||
// Based on EventStateManager::WheelPrefs::GetBasePrefName().
|
|
||||||
var pref = "mousewheel.";
|
|
||||||
|
|
||||||
var pressedModifierCount = event.shiftKey + event.ctrlKey + event.altKey +
|
|
||||||
event.metaKey + event.getModifierState("OS");
|
|
||||||
if (pressedModifierCount != 1) {
|
|
||||||
pref += "default.";
|
|
||||||
} else if (event.shiftKey) {
|
|
||||||
pref += "with_shift.";
|
|
||||||
} else if (event.ctrlKey) {
|
|
||||||
pref += "with_control.";
|
|
||||||
} else if (event.altKey) {
|
|
||||||
pref += "with_alt.";
|
|
||||||
} else if (event.metaKey) {
|
|
||||||
pref += "with_meta.";
|
|
||||||
} else {
|
|
||||||
pref += "with_win.";
|
|
||||||
}
|
|
||||||
|
|
||||||
pref += "action";
|
|
||||||
|
|
||||||
// Don't do anything if this isn't a "zoom" scroll event.
|
|
||||||
var isZoomEvent = false;
|
|
||||||
try {
|
|
||||||
isZoomEvent = (gPrefService.getIntPref(pref) == this.ACTION_ZOOM);
|
|
||||||
} catch (e) {}
|
|
||||||
if (!isZoomEvent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// XXX Lazily cache all the possible action prefs so we don't have to get
|
|
||||||
// them anew from the pref service for every scroll event? We'd have to
|
|
||||||
// make sure to observe them so we can update the cache when they change.
|
|
||||||
|
|
||||||
// We have to call _applyZoomToPref in a timeout because we handle the
|
|
||||||
// event before the event state manager has a chance to apply the zoom
|
|
||||||
// during EventStateManager::PostHandleEvent.
|
|
||||||
let browser = gBrowser.selectedBrowser;
|
|
||||||
let token = this._getBrowserToken(browser);
|
|
||||||
window.setTimeout(function () {
|
|
||||||
if (token.isCurrent)
|
|
||||||
this._applyZoomToPref(browser);
|
|
||||||
}.bind(this), 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIObserver
|
// nsIObserver
|
||||||
|
|
||||||
observe: function (aSubject, aTopic, aData) {
|
observe: function (aSubject, aTopic, aData) {
|
||||||
@ -469,6 +420,30 @@ var FullZoom = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the browser that the supplied zoom event is associated with.
|
||||||
|
* @param event The ZoomChangeUsingMouseWheel event.
|
||||||
|
* @return The associated browser element, if one exists, otherwise null.
|
||||||
|
*/
|
||||||
|
_getTargetedBrowser: function FullZoom__getTargetedBrowser(event) {
|
||||||
|
let target = event.originalTarget;
|
||||||
|
|
||||||
|
// With remote content browsers, the event's target is the browser
|
||||||
|
// we're looking for.
|
||||||
|
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
if (target instanceof window.XULElement &&
|
||||||
|
target.localName == "browser" &&
|
||||||
|
target.namespaceURI == XUL_NS)
|
||||||
|
return target;
|
||||||
|
|
||||||
|
// With in-process content browsers, the event's target is the content
|
||||||
|
// document.
|
||||||
|
if (target.nodeType == Node.DOCUMENT_NODE)
|
||||||
|
return gBrowser.getBrowserForDocument(target);
|
||||||
|
|
||||||
|
throw new Error("Unexpected ZoomChangeUsingMouseWheel event source");
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increments the zoom change token for the given browser so that pending
|
* Increments the zoom change token for the given browser so that pending
|
||||||
* async operations know that it may be unsafe to access they zoom when they
|
* async operations know that it may be unsafe to access they zoom when they
|
||||||
|
@ -23,6 +23,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
|
|||||||
"resource://gre/modules/ShortcutUtils.jsm");
|
"resource://gre/modules/ShortcutUtils.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "GMPInstallManager",
|
XPCOMUtils.defineLazyModuleGetter(this, "GMPInstallManager",
|
||||||
"resource://gre/modules/GMPInstallManager.jsm");
|
"resource://gre/modules/GMPInstallManager.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
|
||||||
|
"resource://gre/modules/NewTabUtils.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
|
XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
|
||||||
"resource:///modules/ContentSearch.jsm");
|
"resource:///modules/ContentSearch.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
|
XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
|
||||||
@ -1294,17 +1296,7 @@ var gBrowserInit = {
|
|||||||
if (Win7Features)
|
if (Win7Features)
|
||||||
Win7Features.onOpenWindow();
|
Win7Features.onOpenWindow();
|
||||||
|
|
||||||
// called when we go into full screen, even if initiated by a web page script
|
FullScreen.init();
|
||||||
window.addEventListener("fullscreen", onFullScreen, true);
|
|
||||||
|
|
||||||
// Called when we enter DOM full-screen mode. Note we can already be in browser
|
|
||||||
// full-screen mode when we enter DOM full-screen mode.
|
|
||||||
window.addEventListener("MozEnteredDomFullscreen", onMozEnteredDomFullscreen, true);
|
|
||||||
|
|
||||||
if (window.fullScreen)
|
|
||||||
onFullScreen();
|
|
||||||
if (document.mozFullScreen)
|
|
||||||
onMozEnteredDomFullscreen();
|
|
||||||
|
|
||||||
#ifdef MOZ_SERVICES_SYNC
|
#ifdef MOZ_SERVICES_SYNC
|
||||||
// initialize the sync UI
|
// initialize the sync UI
|
||||||
@ -1435,7 +1427,7 @@ var gBrowserInit = {
|
|||||||
|
|
||||||
gHistorySwipeAnimation.uninit();
|
gHistorySwipeAnimation.uninit();
|
||||||
|
|
||||||
FullScreen.cleanup();
|
FullScreen.uninit();
|
||||||
|
|
||||||
#ifdef MOZ_SERVICES_SYNC
|
#ifdef MOZ_SERVICES_SYNC
|
||||||
gFxAccounts.uninit();
|
gFxAccounts.uninit();
|
||||||
@ -2762,14 +2754,6 @@ function SwitchToMetro() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFullScreen(event) {
|
|
||||||
FullScreen.toggle(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMozEnteredDomFullscreen(event) {
|
|
||||||
FullScreen.enterDomFullscreen(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWebNavigation()
|
function getWebNavigation()
|
||||||
{
|
{
|
||||||
return gBrowser.webNavigation;
|
return gBrowser.webNavigation;
|
||||||
@ -3109,7 +3093,7 @@ const BrowserSearch = {
|
|||||||
let mm = gBrowser.selectedBrowser.messageManager;
|
let mm = gBrowser.selectedBrowser.messageManager;
|
||||||
if (url === "about:home") {
|
if (url === "about:home") {
|
||||||
AboutHome.focusInput(mm);
|
AboutHome.focusInput(mm);
|
||||||
} else if (url === "about:newtab") {
|
} else if (url === "about:newtab" && NewTabUtils.allPages.enabled) {
|
||||||
ContentSearch.focusInput(mm);
|
ContentSearch.focusInput(mm);
|
||||||
} else {
|
} else {
|
||||||
openUILinkIn("about:home", "current");
|
openUILinkIn("about:home", "current");
|
||||||
|
@ -578,3 +578,40 @@ if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
|
|||||||
Cu.import("resource:///modules/translation/TranslationContentHandler.jsm");
|
Cu.import("resource:///modules/translation/TranslationContentHandler.jsm");
|
||||||
trHandler = new TranslationContentHandler(global, docShell);
|
trHandler = new TranslationContentHandler(global, docShell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let DOMFullscreenHandler = {
|
||||||
|
_fullscreenDoc: null,
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
addMessageListener("DOMFullscreen:Approved", this);
|
||||||
|
addMessageListener("DOMFullscreen:CleanUp", this);
|
||||||
|
addEventListener("MozEnteredDomFullscreen", this);
|
||||||
|
},
|
||||||
|
|
||||||
|
receiveMessage: function(aMessage) {
|
||||||
|
switch(aMessage.name) {
|
||||||
|
case "DOMFullscreen:Approved": {
|
||||||
|
if (this._fullscreenDoc) {
|
||||||
|
Services.obs.notifyObservers(this._fullscreenDoc,
|
||||||
|
"fullscreen-approved",
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "DOMFullscreen:CleanUp": {
|
||||||
|
this._fullscreenDoc = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEvent: function(aEvent) {
|
||||||
|
if (aEvent.type == "MozEnteredDomFullscreen") {
|
||||||
|
this._fullscreenDoc = aEvent.target;
|
||||||
|
sendAsyncMessage("MozEnteredDomFullscreen", {
|
||||||
|
origin: this._fullscreenDoc.nodePrincipal.origin,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DOMFullscreenHandler.init();
|
@ -1225,6 +1225,15 @@
|
|||||||
// If we're using remote tabs, we have to wait until after we've finalized
|
// If we're using remote tabs, we have to wait until after we've finalized
|
||||||
// switching the tabs.
|
// switching the tabs.
|
||||||
|
|
||||||
|
if (newTab._skipContentFocus) {
|
||||||
|
// It's possible the tab we're switching to is ready to focus asynchronously,
|
||||||
|
// when we've already focused something else. In that case, this
|
||||||
|
// _skipContentFocus property can be set so that we skip focusing the
|
||||||
|
// content after we switch tabs.
|
||||||
|
delete newTab._skipContentFocus;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
||||||
let focusFlags = fm.FLAG_NOSCROLL;
|
let focusFlags = fm.FLAG_NOSCROLL;
|
||||||
|
|
||||||
|
@ -276,6 +276,8 @@ skip-if = e10s # Bug 921959 - reload with LOAD_FLAGS_ALLOW_MIXED_CONTENT fails i
|
|||||||
skip-if = buildapp == "mulet" || e10s # Bug ?????? - test directly manipulates content (strange - gets an element from a child which it tries to treat as a string, but that fails)
|
skip-if = buildapp == "mulet" || e10s # Bug ?????? - test directly manipulates content (strange - gets an element from a child which it tries to treat as a string, but that fails)
|
||||||
[browser_bug970746.js]
|
[browser_bug970746.js]
|
||||||
skip-if = e10s # Bug ?????? - test directly manipulates content (directly gets elements from the content)
|
skip-if = e10s # Bug ?????? - test directly manipulates content (directly gets elements from the content)
|
||||||
|
[browser_bug1015721.js]
|
||||||
|
skip-if = os == 'win' || e10s # Bug 1056146 - FullZoomHelper uses promiseTabLoadEvent() which isn't e10s friendly
|
||||||
[browser_canonizeURL.js]
|
[browser_canonizeURL.js]
|
||||||
skip-if = e10s # Bug ?????? - [JavaScript Error: "Error in AboutHome.sendAboutHomeData TypeError: target.messageManager is undefined" {file: "resource:///modules/AboutHome.jsm" line: 208}]
|
skip-if = e10s # Bug ?????? - [JavaScript Error: "Error in AboutHome.sendAboutHomeData TypeError: target.messageManager is undefined" {file: "resource:///modules/AboutHome.jsm" line: 208}]
|
||||||
[browser_contentAreaClick.js]
|
[browser_contentAreaClick.js]
|
||||||
|
55
browser/base/content/test/general/browser_bug1015721.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* 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";
|
||||||
|
|
||||||
|
const TEST_PAGE = "http://example.org/browser/browser/base/content/test/general/zoom_test.html";
|
||||||
|
|
||||||
|
var gTab1, gTab2, gLevel1;
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
Task.spawn(function () {
|
||||||
|
gTab1 = gBrowser.addTab();
|
||||||
|
gTab2 = gBrowser.addTab();
|
||||||
|
|
||||||
|
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
|
||||||
|
yield FullZoomHelper.load(gTab1, TEST_PAGE);
|
||||||
|
yield FullZoomHelper.load(gTab2, TEST_PAGE);
|
||||||
|
}).then(zoomTab1, FullZoomHelper.failAndContinue(finish));
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchZoomEventToBrowser(browser) {
|
||||||
|
EventUtils.synthesizeWheel(browser.contentDocument.documentElement, 10, 10, {
|
||||||
|
ctrlKey: true, deltaY: -1, deltaMode: WheelEvent.DOM_DELTA_LINE
|
||||||
|
}, browser.contentWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoomTab1() {
|
||||||
|
Task.spawn(function () {
|
||||||
|
is(gBrowser.selectedTab, gTab1, "Tab 1 is selected");
|
||||||
|
FullZoomHelper.zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
|
||||||
|
FullZoomHelper.zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
|
||||||
|
|
||||||
|
let browser1 = gBrowser.getBrowserForTab(gTab1);
|
||||||
|
dispatchZoomEventToBrowser(browser1);
|
||||||
|
|
||||||
|
gLevel1 = ZoomManager.getZoomForBrowser(browser1);
|
||||||
|
ok(gLevel1 > 1, "New zoom for tab 1 should be greater than 1");
|
||||||
|
|
||||||
|
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
|
||||||
|
FullZoomHelper.zoomTest(gTab2, gLevel1, "Tab 2 should have zoomed along with tab 1");
|
||||||
|
}).then(finishTest, FullZoomHelper.failAndContinue(finish));
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishTest() {
|
||||||
|
Task.spawn(function () {
|
||||||
|
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
|
||||||
|
FullZoom.reset();
|
||||||
|
yield FullZoomHelper.removeTabAndWaitForLocationChange(gTab1);
|
||||||
|
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
|
||||||
|
FullZoom.reset();
|
||||||
|
yield FullZoomHelper.removeTabAndWaitForLocationChange(gTab2);
|
||||||
|
}).then(finish, FullZoomHelper.failAndContinue(finish));
|
||||||
|
}
|
@ -205,6 +205,24 @@ function runTests() {
|
|||||||
EventUtils.synthesizeKey("k", { accelKey: true });
|
EventUtils.synthesizeKey("k", { accelKey: true });
|
||||||
is(searchBar.textbox.inputField, gWindow.document.activeElement, "Toolbar's search bar should be focused");
|
is(searchBar.textbox.inputField, gWindow.document.activeElement, "Toolbar's search bar should be focused");
|
||||||
|
|
||||||
|
// Test that Ctrl/Cmd + K will focus the search bar from a new about:home page if
|
||||||
|
// the newtab is disabled from `NewTabUtils.allPages.enabled`.
|
||||||
|
yield addNewTabPageTab();
|
||||||
|
// Remove the search bar from toolbar
|
||||||
|
CustomizableUI.removeWidgetFromArea("search-container");
|
||||||
|
NewTabUtils.allPages.enabled = false;
|
||||||
|
EventUtils.synthesizeKey("k", { accelKey: true });
|
||||||
|
let waitEvent = "AboutHomeLoadSnippetsCompleted";
|
||||||
|
yield promiseTabLoadEvent(gWindow.gBrowser.selectedTab, "about:home", waitEvent).then(TestRunner.next);
|
||||||
|
|
||||||
|
is(getContentDocument().documentURI.toLowerCase(), "about:home", "New tab's uri should be about:home");
|
||||||
|
let searchInput = getContentDocument().getElementById("searchText");
|
||||||
|
is(searchInput, getContentDocument().activeElement, "Search input must be the selected element");
|
||||||
|
|
||||||
|
NewTabUtils.allPages.enabled = true;
|
||||||
|
CustomizableUI.reset();
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
|
||||||
// Done. Revert the current engine and remove the new engines.
|
// Done. Revert the current engine and remove the new engines.
|
||||||
Services.search.currentEngine = oldCurrentEngine;
|
Services.search.currentEngine = oldCurrentEngine;
|
||||||
yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
|
yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
|
||||||
@ -414,3 +432,46 @@ function logoImg() {
|
|||||||
function gSearch() {
|
function gSearch() {
|
||||||
return getContentWindow().gSearch;
|
return getContentWindow().gSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for a load (or custom) event to finish in a given tab. If provided
|
||||||
|
* load an uri into the tab.
|
||||||
|
*
|
||||||
|
* @param tab
|
||||||
|
* The tab to load into.
|
||||||
|
* @param [optional] url
|
||||||
|
* The url to load, or the current url.
|
||||||
|
* @param [optional] event
|
||||||
|
* The load event type to wait for. Defaults to "load".
|
||||||
|
* @return {Promise} resolved when the event is handled.
|
||||||
|
* @resolves to the received event
|
||||||
|
* @rejects if a valid load event is not received within a meaningful interval
|
||||||
|
*/
|
||||||
|
function promiseTabLoadEvent(tab, url, eventType="load") {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
info("Wait tab event: " + eventType);
|
||||||
|
|
||||||
|
function handle(event) {
|
||||||
|
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
|
||||||
|
event.target.location.href == "about:blank" ||
|
||||||
|
(url && event.target.location.href != url)) {
|
||||||
|
info("Skipping spurious '" + eventType + "'' event" +
|
||||||
|
" for " + event.target.location.href);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearTimeout(timeout);
|
||||||
|
tab.linkedBrowser.removeEventListener(eventType, handle, true);
|
||||||
|
info("Tab event received: " + eventType);
|
||||||
|
deferred.resolve(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeout = setTimeout(() => {
|
||||||
|
tab.linkedBrowser.removeEventListener(eventType, handle, true);
|
||||||
|
deferred.reject(new Error("Timed out while waiting for a '" + eventType + "'' event"));
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
tab.linkedBrowser.addEventListener(eventType, handle, true, true);
|
||||||
|
if (url)
|
||||||
|
tab.linkedBrowser.loadURI(url);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
|||||||
.getService(Ci.mozIJSSubScriptLoader)
|
.getService(Ci.mozIJSSubScriptLoader)
|
||||||
.loadSubScript("chrome://browser/content/sanitize.js", tmp);
|
.loadSubScript("chrome://browser/content/sanitize.js", tmp);
|
||||||
Cu.import("resource://gre/modules/Timer.jsm", tmp);
|
Cu.import("resource://gre/modules/Timer.jsm", tmp);
|
||||||
let {Promise, NewTabUtils, Sanitizer, clearTimeout, DirectoryLinksProvider} = tmp;
|
let {Promise, NewTabUtils, Sanitizer, clearTimeout, setTimeout, DirectoryLinksProvider} = tmp;
|
||||||
|
|
||||||
let uri = Services.io.newURI("about:newtab", null, null);
|
let uri = Services.io.newURI("about:newtab", null, null);
|
||||||
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||||
|
@ -309,6 +309,8 @@ function openLinkIn(url, where, params) {
|
|||||||
// result in a new frontmost window (e.g. "javascript:window.open('');").
|
// result in a new frontmost window (e.g. "javascript:window.open('');").
|
||||||
w.focus();
|
w.focus();
|
||||||
|
|
||||||
|
let newTab;
|
||||||
|
|
||||||
switch (where) {
|
switch (where) {
|
||||||
case "current":
|
case "current":
|
||||||
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||||
@ -331,23 +333,30 @@ function openLinkIn(url, where, params) {
|
|||||||
loadInBackground = !loadInBackground;
|
loadInBackground = !loadInBackground;
|
||||||
// fall through
|
// fall through
|
||||||
case "tab":
|
case "tab":
|
||||||
let browser = w.gBrowser;
|
newTab = w.gBrowser.loadOneTab(url, {
|
||||||
browser.loadOneTab(url, {
|
referrerURI: aReferrerURI,
|
||||||
referrerURI: aReferrerURI,
|
charset: aCharset,
|
||||||
charset: aCharset,
|
postData: aPostData,
|
||||||
postData: aPostData,
|
inBackground: loadInBackground,
|
||||||
inBackground: loadInBackground,
|
allowThirdPartyFixup: aAllowThirdPartyFixup,
|
||||||
allowThirdPartyFixup: aAllowThirdPartyFixup,
|
relatedToCurrent: aRelatedToCurrent,
|
||||||
relatedToCurrent: aRelatedToCurrent,
|
skipAnimation: aSkipTabAnimation,
|
||||||
skipAnimation: aSkipTabAnimation,
|
allowMixedContent: aAllowMixedContent
|
||||||
allowMixedContent: aAllowMixedContent });
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
w.gBrowser.selectedBrowser.focus();
|
w.gBrowser.selectedBrowser.focus();
|
||||||
|
|
||||||
if (!loadInBackground && w.isBlankPageURL(url))
|
if (!loadInBackground && w.isBlankPageURL(url)) {
|
||||||
|
if (newTab && gMultiProcessBrowser) {
|
||||||
|
// Remote browsers are switched to asynchronously, and we need to
|
||||||
|
// ensure that the location bar remains focused in that case rather
|
||||||
|
// than the content area being focused.
|
||||||
|
newTab._skipContentFocus = true;
|
||||||
|
}
|
||||||
w.focusAndSelectUrlBar();
|
w.focusAndSelectUrlBar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used as an onclick handler for UI elements with link-like behavior.
|
// Used as an onclick handler for UI elements with link-like behavior.
|
||||||
|
@ -1320,7 +1320,7 @@ BrowserGlue.prototype = {
|
|||||||
|
|
||||||
_migrateUI: function BG__migrateUI() {
|
_migrateUI: function BG__migrateUI() {
|
||||||
const UI_VERSION = 23;
|
const UI_VERSION = 23;
|
||||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
|
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
|
||||||
let currentUIVersion = 0;
|
let currentUIVersion = 0;
|
||||||
try {
|
try {
|
||||||
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
|
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
|
||||||
@ -1328,28 +1328,22 @@ BrowserGlue.prototype = {
|
|||||||
if (currentUIVersion >= UI_VERSION)
|
if (currentUIVersion >= UI_VERSION)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
|
let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
|
||||||
this._dataSource = this._rdf.GetDataSource("rdf:local-store");
|
|
||||||
this._dirty = false;
|
|
||||||
|
|
||||||
if (currentUIVersion < 2) {
|
if (currentUIVersion < 2) {
|
||||||
// This code adds the customizable bookmarks button.
|
// This code adds the customizable bookmarks button.
|
||||||
let currentsetResource = this._rdf.GetResource("currentset");
|
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
|
||||||
let currentset = this._getPersist(toolbarResource, currentsetResource);
|
|
||||||
// Need to migrate only if toolbar is customized and the element is not found.
|
// Need to migrate only if toolbar is customized and the element is not found.
|
||||||
if (currentset &&
|
if (currentset &&
|
||||||
currentset.indexOf("bookmarks-menu-button-container") == -1) {
|
currentset.indexOf("bookmarks-menu-button-container") == -1) {
|
||||||
currentset += ",bookmarks-menu-button-container";
|
currentset += ",bookmarks-menu-button-container";
|
||||||
this._setPersist(toolbarResource, currentsetResource, currentset);
|
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentUIVersion < 3) {
|
if (currentUIVersion < 3) {
|
||||||
// This code merges the reload/stop/go button into the url bar.
|
// This code merges the reload/stop/go button into the url bar.
|
||||||
let currentsetResource = this._rdf.GetResource("currentset");
|
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
|
||||||
let currentset = this._getPersist(toolbarResource, currentsetResource);
|
|
||||||
// Need to migrate only if toolbar is customized and all 3 elements are found.
|
// Need to migrate only if toolbar is customized and all 3 elements are found.
|
||||||
if (currentset &&
|
if (currentset &&
|
||||||
currentset.indexOf("reload-button") != -1 &&
|
currentset.indexOf("reload-button") != -1 &&
|
||||||
@ -1360,15 +1354,13 @@ BrowserGlue.prototype = {
|
|||||||
.replace(/(^|,)stop-button($|,)/, "$1$2")
|
.replace(/(^|,)stop-button($|,)/, "$1$2")
|
||||||
.replace(/(^|,)urlbar-container($|,)/,
|
.replace(/(^|,)urlbar-container($|,)/,
|
||||||
"$1urlbar-container,reload-button,stop-button$2");
|
"$1urlbar-container,reload-button,stop-button$2");
|
||||||
this._setPersist(toolbarResource, currentsetResource, currentset);
|
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentUIVersion < 4) {
|
if (currentUIVersion < 4) {
|
||||||
// This code moves the home button to the immediate left of the bookmarks menu button.
|
// This code moves the home button to the immediate left of the bookmarks menu button.
|
||||||
let currentsetResource = this._rdf.GetResource("currentset");
|
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
|
||||||
let currentset = this._getPersist(toolbarResource, currentsetResource);
|
|
||||||
// Need to migrate only if toolbar is customized and the elements are found.
|
// Need to migrate only if toolbar is customized and the elements are found.
|
||||||
if (currentset &&
|
if (currentset &&
|
||||||
currentset.indexOf("home-button") != -1 &&
|
currentset.indexOf("home-button") != -1 &&
|
||||||
@ -1376,24 +1368,21 @@ BrowserGlue.prototype = {
|
|||||||
currentset = currentset.replace(/(^|,)home-button($|,)/, "$1$2")
|
currentset = currentset.replace(/(^|,)home-button($|,)/, "$1$2")
|
||||||
.replace(/(^|,)bookmarks-menu-button-container($|,)/,
|
.replace(/(^|,)bookmarks-menu-button-container($|,)/,
|
||||||
"$1home-button,bookmarks-menu-button-container$2");
|
"$1home-button,bookmarks-menu-button-container$2");
|
||||||
this._setPersist(toolbarResource, currentsetResource, currentset);
|
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentUIVersion < 5) {
|
if (currentUIVersion < 5) {
|
||||||
// This code uncollapses PersonalToolbar if its collapsed status is not
|
// This code uncollapses PersonalToolbar if its collapsed status is not
|
||||||
// persisted, and user customized it or changed default bookmarks.
|
// persisted, and user customized it or changed default bookmarks.
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "PersonalToolbar");
|
//
|
||||||
let collapsedResource = this._rdf.GetResource("collapsed");
|
|
||||||
let collapsed = this._getPersist(toolbarResource, collapsedResource);
|
|
||||||
// If the user does not have a persisted value for the toolbar's
|
// If the user does not have a persisted value for the toolbar's
|
||||||
// "collapsed" attribute, try to determine whether it's customized.
|
// "collapsed" attribute, try to determine whether it's customized.
|
||||||
if (collapsed === null) {
|
if (!xulStore.hasValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed")) {
|
||||||
// We consider the toolbar customized if it has more than
|
// We consider the toolbar customized if it has more than
|
||||||
// 3 children, or if it has a persisted currentset value.
|
// 3 children, or if it has a persisted currentset value.
|
||||||
let currentsetResource = this._rdf.GetResource("currentset");
|
let toolbarIsCustomized = xulStore.hasValue(BROWSER_DOCURL,
|
||||||
let toolbarIsCustomized = !!this._getPersist(toolbarResource,
|
"PersonalToolbar", "currentset");
|
||||||
currentsetResource);
|
|
||||||
let getToolbarFolderCount = function () {
|
let getToolbarFolderCount = function () {
|
||||||
let toolbarFolder =
|
let toolbarFolder =
|
||||||
PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
|
PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
|
||||||
@ -1403,7 +1392,7 @@ BrowserGlue.prototype = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (toolbarIsCustomized || getToolbarFolderCount() > 3) {
|
if (toolbarIsCustomized || getToolbarFolderCount() > 3) {
|
||||||
this._setPersist(toolbarResource, collapsedResource, "false");
|
xulStore.setValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed", "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1419,9 +1408,7 @@ BrowserGlue.prototype = {
|
|||||||
|
|
||||||
if (currentUIVersion < 9) {
|
if (currentUIVersion < 9) {
|
||||||
// This code adds the customizable downloads buttons.
|
// This code adds the customizable downloads buttons.
|
||||||
let currentsetResource = this._rdf.GetResource("currentset");
|
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
|
||||||
let currentset = this._getPersist(toolbarResource, currentsetResource);
|
|
||||||
|
|
||||||
// Since the Downloads button is located in the navigation bar by default,
|
// Since the Downloads button is located in the navigation bar by default,
|
||||||
// migration needs to happen only if the toolbar was customized using a
|
// migration needs to happen only if the toolbar was customized using a
|
||||||
@ -1442,7 +1429,7 @@ BrowserGlue.prototype = {
|
|||||||
currentset = currentset.replace(/(^|,)window-controls($|,)/,
|
currentset = currentset.replace(/(^|,)window-controls($|,)/,
|
||||||
"$1downloads-button,window-controls$2")
|
"$1downloads-button,window-controls$2")
|
||||||
}
|
}
|
||||||
this._setPersist(toolbarResource, currentsetResource, currentset);
|
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1472,15 +1459,13 @@ BrowserGlue.prototype = {
|
|||||||
if (currentUIVersion < 12) {
|
if (currentUIVersion < 12) {
|
||||||
// Remove bookmarks-menu-button-container, then place
|
// Remove bookmarks-menu-button-container, then place
|
||||||
// bookmarks-menu-button into its position.
|
// bookmarks-menu-button into its position.
|
||||||
let currentsetResource = this._rdf.GetResource("currentset");
|
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
|
||||||
let currentset = this._getPersist(toolbarResource, currentsetResource);
|
|
||||||
// Need to migrate only if toolbar is customized.
|
// Need to migrate only if toolbar is customized.
|
||||||
if (currentset) {
|
if (currentset) {
|
||||||
if (currentset.contains("bookmarks-menu-button-container")) {
|
if (currentset.contains("bookmarks-menu-button-container")) {
|
||||||
currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,
|
currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,
|
||||||
"$1bookmarks-menu-button$2");
|
"$1bookmarks-menu-button$2");
|
||||||
this._setPersist(toolbarResource, currentsetResource, currentset);
|
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1501,20 +1486,16 @@ BrowserGlue.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentUIVersion < 16) {
|
if (currentUIVersion < 16) {
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
let isCollapsed = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "collapsed");
|
||||||
let collapsedResource = this._rdf.GetResource("collapsed");
|
|
||||||
let isCollapsed = this._getPersist(toolbarResource, collapsedResource);
|
|
||||||
if (isCollapsed == "true") {
|
if (isCollapsed == "true") {
|
||||||
this._setPersist(toolbarResource, collapsedResource, "false");
|
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "collapsed", "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the bookmarks-menu-button into the nav-bar if it isn't already
|
// Insert the bookmarks-menu-button into the nav-bar if it isn't already
|
||||||
// there.
|
// there.
|
||||||
if (currentUIVersion < 17) {
|
if (currentUIVersion < 17) {
|
||||||
let currentsetResource = this._rdf.GetResource("currentset");
|
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
|
||||||
let currentset = this._getPersist(toolbarResource, currentsetResource);
|
|
||||||
// Need to migrate only if toolbar is customized.
|
// Need to migrate only if toolbar is customized.
|
||||||
if (currentset) {
|
if (currentset) {
|
||||||
if (!currentset.contains("bookmarks-menu-button")) {
|
if (!currentset.contains("bookmarks-menu-button")) {
|
||||||
@ -1531,7 +1512,7 @@ BrowserGlue.prototype = {
|
|||||||
currentset = currentset.replace(/(^|,)window-controls($|,)/,
|
currentset = currentset.replace(/(^|,)window-controls($|,)/,
|
||||||
"$1bookmarks-menu-button,window-controls$2")
|
"$1bookmarks-menu-button,window-controls$2")
|
||||||
}
|
}
|
||||||
this._setPersist(toolbarResource, currentsetResource, currentset);
|
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1541,12 +1522,8 @@ BrowserGlue.prototype = {
|
|||||||
let toolbars = ["navigator-toolbox", "nav-bar", "PersonalToolbar",
|
let toolbars = ["navigator-toolbox", "nav-bar", "PersonalToolbar",
|
||||||
"addon-bar", "TabsToolbar", "toolbar-menubar"];
|
"addon-bar", "TabsToolbar", "toolbar-menubar"];
|
||||||
for (let resourceName of ["mode", "iconsize"]) {
|
for (let resourceName of ["mode", "iconsize"]) {
|
||||||
let resource = this._rdf.GetResource(resourceName);
|
|
||||||
for (let toolbarId of toolbars) {
|
for (let toolbarId of toolbars) {
|
||||||
let toolbar = this._rdf.GetResource(BROWSER_DOCURL + toolbarId);
|
xulStore.removeValue(BROWSER_DOCURL, toolbarId, resourceName);
|
||||||
if (this._getPersist(toolbar, resource)) {
|
|
||||||
this._setPersist(toolbar, resource);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1569,21 +1546,13 @@ BrowserGlue.prototype = {
|
|||||||
|
|
||||||
if (currentUIVersion < 20) {
|
if (currentUIVersion < 20) {
|
||||||
// Remove persisted collapsed state from TabsToolbar.
|
// Remove persisted collapsed state from TabsToolbar.
|
||||||
let resource = this._rdf.GetResource("collapsed");
|
xulStore.removeValue(BROWSER_DOCURL, "TabsToolbar", "collapsed");
|
||||||
let toolbar = this._rdf.GetResource(BROWSER_DOCURL + "TabsToolbar");
|
|
||||||
if (this._getPersist(toolbar, resource)) {
|
|
||||||
this._setPersist(toolbar, resource);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentUIVersion < 21) {
|
if (currentUIVersion < 21) {
|
||||||
// Make sure the 'toolbarbutton-1' class will always be present from here
|
// Make sure the 'toolbarbutton-1' class will always be present from here
|
||||||
// on out.
|
// on out.
|
||||||
let button = this._rdf.GetResource(BROWSER_DOCURL + "bookmarks-menu-button");
|
xulStore.removeValue(BROWSER_DOCURL, "bookmarks-menu-button", "class");
|
||||||
let classResource = this._rdf.GetResource("class");
|
|
||||||
if (this._getPersist(button, classResource)) {
|
|
||||||
this._setPersist(button, classResource);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentUIVersion < 22) {
|
if (currentUIVersion < 22) {
|
||||||
@ -1603,49 +1572,10 @@ BrowserGlue.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._dirty)
|
|
||||||
this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
|
|
||||||
|
|
||||||
delete this._rdf;
|
|
||||||
delete this._dataSource;
|
|
||||||
|
|
||||||
// Update the migration version.
|
// Update the migration version.
|
||||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPersist: function BG__getPersist(aSource, aProperty) {
|
|
||||||
var target = this._dataSource.GetTarget(aSource, aProperty, true);
|
|
||||||
if (target instanceof Ci.nsIRDFLiteral)
|
|
||||||
return target.Value;
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setPersist: function BG__setPersist(aSource, aProperty, aTarget) {
|
|
||||||
this._dirty = true;
|
|
||||||
try {
|
|
||||||
var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true);
|
|
||||||
if (oldTarget) {
|
|
||||||
if (aTarget)
|
|
||||||
this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget));
|
|
||||||
else
|
|
||||||
this._dataSource.Unassert(aSource, aProperty, oldTarget);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the entry to the persisted set for this document if it's not there.
|
|
||||||
// This code is mostly borrowed from XULDocument::Persist.
|
|
||||||
let docURL = aSource.ValueUTF8.split("#")[0];
|
|
||||||
let docResource = this._rdf.GetResource(docURL);
|
|
||||||
let persistResource = this._rdf.GetResource("http://home.netscape.com/NC-rdf#persist");
|
|
||||||
if (!this._dataSource.HasAssertion(docResource, persistResource, aSource, true)) {
|
|
||||||
this._dataSource.Assert(docResource, persistResource, aSource, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(ex) {}
|
|
||||||
},
|
|
||||||
|
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
// public nsIBrowserGlue members
|
// public nsIBrowserGlue members
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
|
@ -1020,10 +1020,6 @@ XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF",
|
|||||||
"@mozilla.org/rdf/rdf-service;1",
|
"@mozilla.org/rdf/rdf-service;1",
|
||||||
"nsIRDFService");
|
"nsIRDFService");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "localStore", function() {
|
|
||||||
return PlacesUIUtils.RDF.GetDataSource("rdf:local-store");
|
|
||||||
});
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
|
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
|
||||||
return Services.prefs.getComplexValue("intl.ellipsis",
|
return Services.prefs.getComplexValue("intl.ellipsis",
|
||||||
Ci.nsIPrefLocalizedString).data;
|
Ci.nsIPrefLocalizedString).data;
|
||||||
|
@ -23,6 +23,14 @@ function PlacesTreeView(aFlatList, aOnOpenFlatContainer, aController) {
|
|||||||
PlacesTreeView.prototype = {
|
PlacesTreeView.prototype = {
|
||||||
get wrappedJSObject() this,
|
get wrappedJSObject() this,
|
||||||
|
|
||||||
|
__xulStore: null,
|
||||||
|
get _xulStore() {
|
||||||
|
if (!this.__xulStore) {
|
||||||
|
this.__xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
|
||||||
|
}
|
||||||
|
return this.__xulStore;
|
||||||
|
},
|
||||||
|
|
||||||
__dateService: null,
|
__dateService: null,
|
||||||
get _dateService() {
|
get _dateService() {
|
||||||
if (!this.__dateService) {
|
if (!this.__dateService) {
|
||||||
@ -307,11 +315,15 @@ PlacesTreeView.prototype = {
|
|||||||
if (!this._flatList &&
|
if (!this._flatList &&
|
||||||
curChild instanceof Ci.nsINavHistoryContainerResultNode &&
|
curChild instanceof Ci.nsINavHistoryContainerResultNode &&
|
||||||
!this._controller.hasCachedLivemarkInfo(curChild)) {
|
!this._controller.hasCachedLivemarkInfo(curChild)) {
|
||||||
let resource = this._getResourceForNode(curChild);
|
let uri = curChild.uri;
|
||||||
let isopen = resource != null &&
|
let isopen = false;
|
||||||
PlacesUIUtils.localStore.HasAssertion(resource,
|
|
||||||
openLiteral,
|
if (uri) {
|
||||||
trueLiteral, true);
|
let docURI = this._getDocumentURI();
|
||||||
|
let val = this._xulStore.getValue(docURI, uri, "open");
|
||||||
|
isopen = (val == "true");
|
||||||
|
}
|
||||||
|
|
||||||
if (isopen != curChild.containerOpen)
|
if (isopen != curChild.containerOpen)
|
||||||
aToOpen.push(curChild);
|
aToOpen.push(curChild);
|
||||||
else if (curChild.containerOpen && curChild.childCount > 0)
|
else if (curChild.containerOpen && curChild.childCount > 0)
|
||||||
@ -1109,11 +1121,16 @@ PlacesTreeView.prototype = {
|
|||||||
return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE;
|
return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getResourceForNode: function PTV_getResourceForNode(aNode)
|
// Retrieves an nsIURI for the document
|
||||||
|
_documentURI: null,
|
||||||
|
_getDocumentURI: function()
|
||||||
{
|
{
|
||||||
let uri = aNode.uri;
|
if (!this._documentURI) {
|
||||||
NS_ASSERT(uri, "if there is no uri, we can't persist the open state");
|
let ioService = Cc["@mozilla.org/network/io-service;1"].
|
||||||
return uri ? PlacesUIUtils.RDF.GetResource(uri) : null;
|
getService(Ci.nsIIOService);
|
||||||
|
this._documentURI = ioService.newURI(document.URL, null, null);
|
||||||
|
}
|
||||||
|
return this._documentURI;
|
||||||
},
|
},
|
||||||
|
|
||||||
// nsITreeView
|
// nsITreeView
|
||||||
@ -1497,15 +1514,16 @@ PlacesTreeView.prototype = {
|
|||||||
|
|
||||||
// Persist containers open status, but never persist livemarks.
|
// Persist containers open status, but never persist livemarks.
|
||||||
if (!this._controller.hasCachedLivemarkInfo(node)) {
|
if (!this._controller.hasCachedLivemarkInfo(node)) {
|
||||||
let resource = this._getResourceForNode(node);
|
let uri = node.uri;
|
||||||
if (resource) {
|
|
||||||
const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
|
|
||||||
const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
|
|
||||||
|
|
||||||
if (node.containerOpen)
|
if (uri) {
|
||||||
PlacesUIUtils.localStore.Unassert(resource, openLiteral, trueLiteral);
|
let docURI = this._getDocumentURI();
|
||||||
else
|
|
||||||
PlacesUIUtils.localStore.Assert(resource, openLiteral, trueLiteral, true);
|
if (node.containerOpen) {
|
||||||
|
this._xulStore.removeValue(docURI, uri, "open");
|
||||||
|
} else {
|
||||||
|
this._xulStore.setValue(docURI, uri, "open", "true");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,62 +5,25 @@
|
|||||||
/**
|
/**
|
||||||
* Tests PersonalToolbar migration path.
|
* Tests PersonalToolbar migration path.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
|
let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
|
||||||
let gOriginalMigrationVersion;
|
let gOriginalMigrationVersion;
|
||||||
const BROWSER_URL = getBrowserURL();
|
const BROWSER_URL = getBrowserURL();
|
||||||
|
|
||||||
let localStore = {
|
let localStore = {
|
||||||
get RDF() Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService),
|
get xulStore() Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore),
|
||||||
get store() this.RDF.GetDataSource("rdf:local-store"),
|
|
||||||
|
|
||||||
get toolbar()
|
getValue: function getValue(aProperty)
|
||||||
{
|
{
|
||||||
delete this.toolbar;
|
return this.xulStore.getValue(BROWSER_URL, "PersonalToolbar", aProperty);
|
||||||
let toolbar = this.RDF.GetResource(BROWSER_URL + "#PersonalToolbar");
|
|
||||||
// Add the entry to the persisted set for this document if it's not there.
|
|
||||||
// See XULDocument::Persist.
|
|
||||||
let doc = this.RDF.GetResource(BROWSER_URL);
|
|
||||||
let persist = this.RDF.GetResource("http://home.netscape.com/NC-rdf#persist");
|
|
||||||
if (!this.store.HasAssertion(doc, persist, toolbar, true)) {
|
|
||||||
this.store.Assert(doc, persist, toolbar, true);
|
|
||||||
}
|
|
||||||
return this.toolbar = toolbar;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getPersist: function getPersist(aProperty)
|
setValue: function setValue(aProperty, aValue)
|
||||||
{
|
{
|
||||||
let property = this.RDF.GetResource(aProperty);
|
if (aValue) {
|
||||||
let target = this.store.GetTarget(this.toolbar, property, true);
|
this.xulStore.setValue(BROWSER_URL, "PersonalToolbar", aProperty, aValue);
|
||||||
if (target instanceof Ci.nsIRDFLiteral)
|
} else {
|
||||||
return target.Value;
|
this.xulStore.removeValue(BROWSER_URL, "PersonalToolbar", aProperty);
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
setPersist: function setPersist(aProperty, aValue)
|
|
||||||
{
|
|
||||||
let property = this.RDF.GetResource(aProperty);
|
|
||||||
let value = aValue ? this.RDF.GetLiteral(aValue) : null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let oldTarget = this.store.GetTarget(this.toolbar, property, true);
|
|
||||||
if (oldTarget && value) {
|
|
||||||
this.store.Change(this.toolbar, property, oldTarget, value);
|
|
||||||
}
|
|
||||||
else if (value) {
|
|
||||||
this.store.Assert(this.toolbar, property, value, true);
|
|
||||||
}
|
|
||||||
else if (oldTarget) {
|
|
||||||
this.store.Unassert(this.toolbar, property, oldTarget);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch(ex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.store.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,17 +32,17 @@ let gTests = [
|
|||||||
function test_explicitly_collapsed_toolbar()
|
function test_explicitly_collapsed_toolbar()
|
||||||
{
|
{
|
||||||
info("An explicitly collapsed toolbar should not be uncollapsed.");
|
info("An explicitly collapsed toolbar should not be uncollapsed.");
|
||||||
localStore.setPersist("collapsed", "true");
|
localStore.setValue("collapsed", "true");
|
||||||
bg.observe(null, "browser-glue-test", "force-ui-migration");
|
bg.observe(null, "browser-glue-test", "force-ui-migration");
|
||||||
is(localStore.getPersist("collapsed"), "true", "Toolbar is collapsed");
|
is(localStore.getValue("collapsed"), "true", "Toolbar is collapsed");
|
||||||
},
|
},
|
||||||
|
|
||||||
function test_customized_toolbar()
|
function test_customized_toolbar()
|
||||||
{
|
{
|
||||||
info("A customized toolbar should be uncollapsed.");
|
info("A customized toolbar should be uncollapsed.");
|
||||||
localStore.setPersist("currentset", "splitter");
|
localStore.setValue("currentset", "splitter");
|
||||||
bg.observe(null, "browser-glue-test", "force-ui-migration");
|
bg.observe(null, "browser-glue-test", "force-ui-migration");
|
||||||
is(localStore.getPersist("collapsed"), "false", "Toolbar has been uncollapsed");
|
is(localStore.getValue("collapsed"), "false", "Toolbar has been uncollapsed");
|
||||||
},
|
},
|
||||||
|
|
||||||
function test_many_bookmarks_toolbar()
|
function test_many_bookmarks_toolbar()
|
||||||
@ -98,8 +61,12 @@ function test_many_bookmarks_toolbar()
|
|||||||
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.toolbarFolderId,
|
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.toolbarFolderId,
|
||||||
PlacesUtils.bookmarks.DEFAULT_INDEX)
|
PlacesUtils.bookmarks.DEFAULT_INDEX)
|
||||||
);
|
);
|
||||||
|
ids.push(
|
||||||
|
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.toolbarFolderId,
|
||||||
|
PlacesUtils.bookmarks.DEFAULT_INDEX)
|
||||||
|
);
|
||||||
bg.observe(null, "browser-glue-test", "force-ui-migration");
|
bg.observe(null, "browser-glue-test", "force-ui-migration");
|
||||||
is(localStore.getPersist("collapsed"), "false", "Toolbar has been uncollapsed");
|
is(localStore.getValue("collapsed"), "false", "Toolbar has been uncollapsed");
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
@ -109,14 +76,14 @@ function test()
|
|||||||
gOriginalMigrationVersion = Services.prefs.getIntPref("browser.migration.version");
|
gOriginalMigrationVersion = Services.prefs.getIntPref("browser.migration.version");
|
||||||
registerCleanupFunction(clean);
|
registerCleanupFunction(clean);
|
||||||
|
|
||||||
if (localStore.getPersist("currentset") !== null) {
|
if (localStore.getValue("currentset") !== null) {
|
||||||
info("Toolbar currentset was persisted by a previous test, fixing it.");
|
info("Toolbar currentset was persisted by a previous test, fixing it.");
|
||||||
localStore.setPersist("currentset", null);
|
localStore.setValue("currentset", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localStore.getPersist("collapsed") !== null) {
|
if (localStore.getValue("collapsed") !== null) {
|
||||||
info("Toolbar collapsed status was persisted by a previous test, fixing it.");
|
info("Toolbar collapsed status was persisted by a previous test, fixing it.");
|
||||||
localStore.setPersist("collapsed", null);
|
localStore.setValue("collapsed", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (gTests.length) {
|
while (gTests.length) {
|
||||||
@ -129,7 +96,7 @@ function test()
|
|||||||
function clean()
|
function clean()
|
||||||
{
|
{
|
||||||
Services.prefs.setIntPref("browser.migration.version", gOriginalMigrationVersion);
|
Services.prefs.setIntPref("browser.migration.version", gOriginalMigrationVersion);
|
||||||
localStore.setPersist("currentset", null);
|
localStore.setValue("currentset", null);
|
||||||
localStore.setPersist("collapsed", null);
|
localStore.setValue("collapsed", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,24 +7,75 @@ this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/Chr
|
|||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||||
"resource://gre/modules/FormHistory.jsm");
|
"resource://gre/modules/FormHistory.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||||
|
"resource://gre/modules/Promise.jsm");
|
||||||
|
|
||||||
function test() {
|
function expectedURL(aSearchTerms) {
|
||||||
waitForExplicitFinish();
|
|
||||||
|
|
||||||
const ENGINE_HTML_BASE = "http://mochi.test:8888/browser/browser/components/search/test/test.html";
|
const ENGINE_HTML_BASE = "http://mochi.test:8888/browser/browser/components/search/test/test.html";
|
||||||
|
var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
|
||||||
|
getService(Ci.nsITextToSubURI);
|
||||||
|
var searchArg = textToSubURI.ConvertAndEscape("utf-8", aSearchTerms);
|
||||||
|
return ENGINE_HTML_BASE + "?test=" + searchArg;
|
||||||
|
}
|
||||||
|
|
||||||
var searchEntries = ["test", "More Text", "Some Text"];
|
function simulateClick(aEvent, aTarget) {
|
||||||
var searchBar = BrowserSearch.searchBar;
|
var event = document.createEvent("MouseEvent");
|
||||||
var searchButton = document.getAnonymousElementByAttribute(searchBar,
|
var ctrlKeyArg = aEvent.ctrlKey || false;
|
||||||
"anonid", "search-go-button");
|
var altKeyArg = aEvent.altKey || false;
|
||||||
ok(searchButton, "got search-go-button");
|
var shiftKeyArg = aEvent.shiftKey || false;
|
||||||
|
var metaKeyArg = aEvent.metaKey || false;
|
||||||
|
var buttonArg = aEvent.button || 0;
|
||||||
|
event.initMouseEvent("click", true, true, window,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
|
||||||
|
buttonArg, null);
|
||||||
|
aTarget.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
searchBar.value = "test";
|
// modified from toolkit/components/satchel/test/test_form_autocomplete.html
|
||||||
|
function checkMenuEntries(expectedValues) {
|
||||||
|
var actualValues = getMenuEntries();
|
||||||
|
is(actualValues.length, expectedValues.length, "Checking length of expected menu");
|
||||||
|
for (var i = 0; i < expectedValues.length; i++)
|
||||||
|
is(actualValues[i], expectedValues[i], "Checking menu entry #" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMenuEntries() {
|
||||||
|
var entries = [];
|
||||||
|
var autocompleteMenu = searchBar.textbox.popup;
|
||||||
|
// Could perhaps pull values directly from the controller, but it seems
|
||||||
|
// more reliable to test the values that are actually in the tree?
|
||||||
|
var column = autocompleteMenu.tree.columns[0];
|
||||||
|
var numRows = autocompleteMenu.tree.view.rowCount;
|
||||||
|
for (var i = 0; i < numRows; i++) {
|
||||||
|
entries.push(autocompleteMenu.tree.view.getValueAt(i, column));
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* countEntries(name, value) {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
let count = 0;
|
||||||
|
let obj = name && value ? {fieldname: name, value: value} : {};
|
||||||
|
FormHistory.count(obj,
|
||||||
|
{ handleResult: function(result) { count = result; },
|
||||||
|
handleError: function(error) { throw error; },
|
||||||
|
handleCompletion: function(reason) {
|
||||||
|
if (!reason) {
|
||||||
|
deferred.resolve(count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchBar;
|
||||||
|
var searchButton;
|
||||||
|
var searchEntries = ["test", "More Text", "Some Text"];
|
||||||
|
function* promiseSetEngine() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
var ss = Services.search;
|
var ss = Services.search;
|
||||||
|
|
||||||
let testIterator;
|
|
||||||
|
|
||||||
function observer(aSub, aTopic, aData) {
|
function observer(aSub, aTopic, aData) {
|
||||||
switch (aData) {
|
switch (aData) {
|
||||||
case "engine-added":
|
case "engine-added":
|
||||||
@ -34,266 +85,221 @@ function test() {
|
|||||||
break;
|
break;
|
||||||
case "engine-current":
|
case "engine-current":
|
||||||
ok(ss.currentEngine.name == "Bug 426329", "currentEngine set");
|
ok(ss.currentEngine.name == "Bug 426329", "currentEngine set");
|
||||||
testReturn();
|
searchBar = BrowserSearch.searchBar;
|
||||||
break;
|
searchButton = document.getAnonymousElementByAttribute(searchBar,
|
||||||
case "engine-removed":
|
"anonid", "search-go-button");
|
||||||
|
ok(searchButton, "got search-go-button");
|
||||||
|
searchBar.value = "test";
|
||||||
|
|
||||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||||
finish();
|
deferred.resolve();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||||
ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml",
|
ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml",
|
||||||
Ci.nsISearchEngine.DATA_XML, "data:image/x-icon,%00",
|
Ci.nsISearchEngine.DATA_XML, "data:image/x-icon,%00",
|
||||||
false);
|
false);
|
||||||
|
|
||||||
var preSelectedBrowser, preTabNo;
|
return deferred.promise;
|
||||||
function init() {
|
|
||||||
preSelectedBrowser = gBrowser.selectedBrowser;
|
|
||||||
preTabNo = gBrowser.tabs.length;
|
|
||||||
searchBar.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testReturn() {
|
|
||||||
init();
|
|
||||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
|
||||||
doOnloadOnce(function(event) {
|
|
||||||
|
|
||||||
is(gBrowser.tabs.length, preTabNo, "Return key did not open new tab");
|
|
||||||
is(event.originalTarget, preSelectedBrowser.contentDocument,
|
|
||||||
"Return key loaded results in current tab");
|
|
||||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testReturn opened correct search page");
|
|
||||||
|
|
||||||
testAltReturn();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testAltReturn() {
|
|
||||||
init();
|
|
||||||
EventUtils.synthesizeKey("VK_RETURN", { altKey: true });
|
|
||||||
doOnloadOnce(function(event) {
|
|
||||||
|
|
||||||
is(gBrowser.tabs.length, preTabNo + 1, "Alt+Return key added new tab");
|
|
||||||
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
|
||||||
"Alt+Return key loaded results in new tab");
|
|
||||||
is(event.originalTarget, gBrowser.contentDocument,
|
|
||||||
"Alt+Return key loaded results in foreground tab");
|
|
||||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testAltReturn opened correct search page");
|
|
||||||
|
|
||||||
//Shift key has no effect for now, so skip it
|
|
||||||
//testShiftAltReturn();
|
|
||||||
testLeftClick();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testShiftAltReturn() {
|
|
||||||
init();
|
|
||||||
EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true, altKey: true });
|
|
||||||
doOnloadOnce(function(event) {
|
|
||||||
|
|
||||||
is(gBrowser.tabs.length, preTabNo + 1, "Shift+Alt+Return key added new tab");
|
|
||||||
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
|
||||||
"Shift+Alt+Return key loaded results in new tab");
|
|
||||||
isnot(event.originalTarget, gBrowser.contentDocument,
|
|
||||||
"Shift+Alt+Return key loaded results in background tab");
|
|
||||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftAltReturn opened correct search page");
|
|
||||||
|
|
||||||
testLeftClick();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testLeftClick() {
|
|
||||||
init();
|
|
||||||
simulateClick({ button: 0 }, searchButton);
|
|
||||||
doOnloadOnce(function(event) {
|
|
||||||
|
|
||||||
is(gBrowser.tabs.length, preTabNo, "LeftClick did not open new tab");
|
|
||||||
is(event.originalTarget, preSelectedBrowser.contentDocument,
|
|
||||||
"LeftClick loaded results in current tab");
|
|
||||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testLeftClick opened correct search page");
|
|
||||||
|
|
||||||
testMiddleClick();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testMiddleClick() {
|
|
||||||
init();
|
|
||||||
simulateClick({ button: 1 }, searchButton);
|
|
||||||
doOnloadOnce(function(event) {
|
|
||||||
|
|
||||||
is(gBrowser.tabs.length, preTabNo + 1, "MiddleClick added new tab");
|
|
||||||
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
|
||||||
"MiddleClick loaded results in new tab");
|
|
||||||
is(event.originalTarget, gBrowser.contentDocument,
|
|
||||||
"MiddleClick loaded results in foreground tab");
|
|
||||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testMiddleClick opened correct search page");
|
|
||||||
|
|
||||||
testShiftMiddleClick();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testShiftMiddleClick() {
|
|
||||||
init();
|
|
||||||
simulateClick({ button: 1, shiftKey: true }, searchButton);
|
|
||||||
doOnloadOnce(function(event) {
|
|
||||||
|
|
||||||
is(gBrowser.tabs.length, preTabNo + 1, "Shift+MiddleClick added new tab");
|
|
||||||
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
|
||||||
"Shift+MiddleClick loaded results in new tab");
|
|
||||||
isnot(event.originalTarget, gBrowser.contentDocument,
|
|
||||||
"Shift+MiddleClick loaded results in background tab");
|
|
||||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftMiddleClick opened correct search page");
|
|
||||||
|
|
||||||
testDropText();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent the search buttonmenu from opening during the drag tests
|
|
||||||
function stopPopup(event) { event.preventDefault(); }
|
|
||||||
|
|
||||||
function testDropText() {
|
|
||||||
init();
|
|
||||||
searchBar.addEventListener("popupshowing", stopPopup, true);
|
|
||||||
// drop on the search button so that we don't need to worry about the
|
|
||||||
// default handlers for textboxes.
|
|
||||||
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/plain", data: "Some Text" } ]], "copy", window);
|
|
||||||
doOnloadOnce(function(event) {
|
|
||||||
is(searchBar.value, "Some Text", "drop text/plain on searchbar");
|
|
||||||
testDropInternalText();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDropInternalText() {
|
|
||||||
init();
|
|
||||||
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/x-moz-text-internal", data: "More Text" } ]], "copy", window);
|
|
||||||
doOnloadOnce(function(event) {
|
|
||||||
is(searchBar.value, "More Text", "drop text/x-moz-text-internal on searchbar");
|
|
||||||
testDropLink();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDropLink() {
|
|
||||||
init();
|
|
||||||
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/uri-list", data: "http://www.mozilla.org" } ]], "copy", window);
|
|
||||||
is(searchBar.value, "More Text", "drop text/uri-list on searchbar");
|
|
||||||
SimpleTest.executeSoon(testRightClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testRightClick() {
|
|
||||||
init();
|
|
||||||
searchBar.removeEventListener("popupshowing", stopPopup, true);
|
|
||||||
content.location.href = "about:blank";
|
|
||||||
simulateClick({ button: 2 }, searchButton);
|
|
||||||
setTimeout(function() {
|
|
||||||
is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab");
|
|
||||||
is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing");
|
|
||||||
|
|
||||||
testIterator = testSearchHistory();
|
|
||||||
testIterator.next();
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function countEntries(name, value, message) {
|
|
||||||
let count = 0;
|
|
||||||
FormHistory.count({ fieldname: name, value: value },
|
|
||||||
{ handleResult: function(result) { count = result; },
|
|
||||||
handleError: function(error) { throw error; },
|
|
||||||
handleCompletion: function(reason) {
|
|
||||||
if (!reason) {
|
|
||||||
ok(count > 0, message);
|
|
||||||
testIterator.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSearchHistory() {
|
|
||||||
var textbox = searchBar._textbox;
|
|
||||||
for (var i = 0; i < searchEntries.length; i++) {
|
|
||||||
yield countEntries(textbox.getAttribute("autocompletesearchparam"), searchEntries[i],
|
|
||||||
"form history entry '" + searchEntries[i] + "' should exist");
|
|
||||||
}
|
|
||||||
testAutocomplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testAutocomplete() {
|
|
||||||
var popup = searchBar.textbox.popup;
|
|
||||||
popup.addEventListener("popupshown", function testACPopupShowing() {
|
|
||||||
popup.removeEventListener("popupshown", testACPopupShowing);
|
|
||||||
checkMenuEntries(searchEntries);
|
|
||||||
testClearHistory();
|
|
||||||
});
|
|
||||||
searchBar.textbox.showHistoryPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testClearHistory() {
|
|
||||||
let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory")
|
|
||||||
ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled");
|
|
||||||
controller.doCommand("cmd_clearhistory");
|
|
||||||
let count = 0;
|
|
||||||
FormHistory.count({ },
|
|
||||||
{ handleResult: function(result) { count = result; },
|
|
||||||
handleError: function(error) { throw error; },
|
|
||||||
handleCompletion: function(reason) {
|
|
||||||
if (!reason) {
|
|
||||||
ok(count == 0, "History cleared");
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function finalize() {
|
|
||||||
searchBar.value = "";
|
|
||||||
while (gBrowser.tabs.length != 1) {
|
|
||||||
gBrowser.removeTab(gBrowser.tabs[0]);
|
|
||||||
}
|
|
||||||
content.location.href = "about:blank";
|
|
||||||
var engine = ss.getEngineByName("Bug 426329");
|
|
||||||
ss.removeEngine(engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
function simulateClick(aEvent, aTarget) {
|
|
||||||
var event = document.createEvent("MouseEvent");
|
|
||||||
var ctrlKeyArg = aEvent.ctrlKey || false;
|
|
||||||
var altKeyArg = aEvent.altKey || false;
|
|
||||||
var shiftKeyArg = aEvent.shiftKey || false;
|
|
||||||
var metaKeyArg = aEvent.metaKey || false;
|
|
||||||
var buttonArg = aEvent.button || 0;
|
|
||||||
event.initMouseEvent("click", true, true, window,
|
|
||||||
0, 0, 0, 0, 0,
|
|
||||||
ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
|
|
||||||
buttonArg, null);
|
|
||||||
aTarget.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectedURL(aSearchTerms) {
|
|
||||||
var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
|
|
||||||
getService(Ci.nsITextToSubURI);
|
|
||||||
var searchArg = textToSubURI.ConvertAndEscape("utf-8", aSearchTerms);
|
|
||||||
return ENGINE_HTML_BASE + "?test=" + searchArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// modified from toolkit/components/satchel/test/test_form_autocomplete.html
|
|
||||||
function checkMenuEntries(expectedValues) {
|
|
||||||
var actualValues = getMenuEntries();
|
|
||||||
is(actualValues.length, expectedValues.length, "Checking length of expected menu");
|
|
||||||
for (var i = 0; i < expectedValues.length; i++)
|
|
||||||
is(actualValues[i], expectedValues[i], "Checking menu entry #" + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMenuEntries() {
|
|
||||||
var entries = [];
|
|
||||||
var autocompleteMenu = searchBar.textbox.popup;
|
|
||||||
// Could perhaps pull values directly from the controller, but it seems
|
|
||||||
// more reliable to test the values that are actually in the tree?
|
|
||||||
var column = autocompleteMenu.tree.columns[0];
|
|
||||||
var numRows = autocompleteMenu.tree.view.rowCount;
|
|
||||||
for (var i = 0; i < numRows; i++) {
|
|
||||||
entries.push(autocompleteMenu.tree.view.getValueAt(i, column));
|
|
||||||
}
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* promiseRemoveEngine() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
var ss = Services.search;
|
||||||
|
|
||||||
|
function observer(aSub, aTopic, aData) {
|
||||||
|
if (aData == "engine-removed") {
|
||||||
|
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||||
|
var engine = ss.getEngineByName("Bug 426329");
|
||||||
|
ss.removeEngine(engine);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var preSelectedBrowser;
|
||||||
|
var preTabNo;
|
||||||
|
function* prepareTest() {
|
||||||
|
preSelectedBrowser = gBrowser.selectedBrowser;
|
||||||
|
preTabNo = gBrowser.tabs.length;
|
||||||
|
searchBar = BrowserSearch.searchBar;
|
||||||
|
|
||||||
|
let windowFocused = Promise.defer();
|
||||||
|
SimpleTest.waitForFocus(windowFocused.resolve, window);
|
||||||
|
yield windowFocused.promise;
|
||||||
|
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
if (document.activeElement != searchBar) {
|
||||||
|
searchBar.addEventListener("focus", function onFocus() {
|
||||||
|
searchBar.removeEventListener("focus", onFocus);
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
searchBar.focus();
|
||||||
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(function testSetupEngine() {
|
||||||
|
yield promiseSetEngine();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testReturn() {
|
||||||
|
yield prepareTest();
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||||
|
let event = yield promiseOnLoad();
|
||||||
|
|
||||||
|
is(gBrowser.tabs.length, preTabNo, "Return key did not open new tab");
|
||||||
|
is(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||||
|
"Return key loaded results in current tab");
|
||||||
|
is(event.originalTarget.URL, expectedURL(searchBar.value), "testReturn opened correct search page");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testAltReturn() {
|
||||||
|
yield prepareTest();
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", { altKey: true });
|
||||||
|
let event = yield promiseOnLoad();
|
||||||
|
|
||||||
|
is(gBrowser.tabs.length, preTabNo + 1, "Alt+Return key added new tab");
|
||||||
|
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||||
|
"Alt+Return key loaded results in new tab");
|
||||||
|
is(event.originalTarget, gBrowser.contentDocument,
|
||||||
|
"Alt+Return key loaded results in foreground tab");
|
||||||
|
is(event.originalTarget.URL, expectedURL(searchBar.value), "testAltReturn opened correct search page");
|
||||||
|
});
|
||||||
|
|
||||||
|
//Shift key has no effect for now, so skip it
|
||||||
|
add_task(function testShiftAltReturn() {
|
||||||
|
return;
|
||||||
|
|
||||||
|
yield prepareTest();
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true, altKey: true });
|
||||||
|
let event = yield promiseOnLoad();
|
||||||
|
|
||||||
|
is(gBrowser.tabs.length, preTabNo + 1, "Shift+Alt+Return key added new tab");
|
||||||
|
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||||
|
"Shift+Alt+Return key loaded results in new tab");
|
||||||
|
isnot(event.originalTarget, gBrowser.contentDocument,
|
||||||
|
"Shift+Alt+Return key loaded results in background tab");
|
||||||
|
is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftAltReturn opened correct search page");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testLeftClick() {
|
||||||
|
yield prepareTest();
|
||||||
|
simulateClick({ button: 0 }, searchButton);
|
||||||
|
let event = yield promiseOnLoad();
|
||||||
|
is(gBrowser.tabs.length, preTabNo, "LeftClick did not open new tab");
|
||||||
|
is(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||||
|
"LeftClick loaded results in current tab");
|
||||||
|
is(event.originalTarget.URL, expectedURL(searchBar.value), "testLeftClick opened correct search page");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testMiddleClick() {
|
||||||
|
yield prepareTest();
|
||||||
|
simulateClick({ button: 1 }, searchButton);
|
||||||
|
let event = yield promiseOnLoad();
|
||||||
|
is(gBrowser.tabs.length, preTabNo + 1, "MiddleClick added new tab");
|
||||||
|
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||||
|
"MiddleClick loaded results in new tab");
|
||||||
|
is(event.originalTarget, gBrowser.contentDocument,
|
||||||
|
"MiddleClick loaded results in foreground tab");
|
||||||
|
is(event.originalTarget.URL, expectedURL(searchBar.value), "testMiddleClick opened correct search page");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testShiftMiddleClick() {
|
||||||
|
yield prepareTest();
|
||||||
|
simulateClick({ button: 1, shiftKey: true }, searchButton);
|
||||||
|
let event = yield promiseOnLoad();
|
||||||
|
is(gBrowser.tabs.length, preTabNo + 1, "Shift+MiddleClick added new tab");
|
||||||
|
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||||
|
"Shift+MiddleClick loaded results in new tab");
|
||||||
|
isnot(event.originalTarget, gBrowser.contentDocument,
|
||||||
|
"Shift+MiddleClick loaded results in background tab");
|
||||||
|
is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftMiddleClick opened correct search page");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testDropText() {
|
||||||
|
yield prepareTest();
|
||||||
|
let promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
|
||||||
|
// drop on the search button so that we don't need to worry about the
|
||||||
|
// default handlers for textboxes.
|
||||||
|
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/plain", data: "Some Text" } ]], "copy", window);
|
||||||
|
yield promisePreventPopup;
|
||||||
|
let event = yield promiseOnLoad();
|
||||||
|
is(event.originalTarget.URL, expectedURL(searchBar.value), "testDropText opened correct search page");
|
||||||
|
is(searchBar.value, "Some Text", "drop text/plain on searchbar");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testDropInternalText() {
|
||||||
|
yield prepareTest();
|
||||||
|
let promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
|
||||||
|
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/x-moz-text-internal", data: "More Text" } ]], "copy", window);
|
||||||
|
yield promisePreventPopup;
|
||||||
|
let event = yield promiseOnLoad();
|
||||||
|
is(event.originalTarget.URL, expectedURL(searchBar.value), "testDropInternalText opened correct search page");
|
||||||
|
is(searchBar.value, "More Text", "drop text/x-moz-text-internal on searchbar");
|
||||||
|
|
||||||
|
// testDropLink implicitly depended on testDropInternalText, so these two tests
|
||||||
|
// were merged so that if testDropInternalText failed it wouldn't cause testDropLink
|
||||||
|
// to fail unexplainably.
|
||||||
|
yield prepareTest();
|
||||||
|
let promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
|
||||||
|
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/uri-list", data: "http://www.mozilla.org" } ]], "copy", window);
|
||||||
|
yield promisePreventPopup;
|
||||||
|
is(searchBar.value, "More Text", "drop text/uri-list on searchbar shouldn't change anything");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testRightClick() {
|
||||||
|
preTabNo = gBrowser.tabs.length;
|
||||||
|
content.location.href = "about:blank";
|
||||||
|
simulateClick({ button: 2 }, searchButton);
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
setTimeout(function() {
|
||||||
|
is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab");
|
||||||
|
is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing");
|
||||||
|
deferred.resolve();
|
||||||
|
}, 5000);
|
||||||
|
yield deferred.promise;
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testSearchHistory() {
|
||||||
|
var textbox = searchBar._textbox;
|
||||||
|
for (var i = 0; i < searchEntries.length; i++) {
|
||||||
|
let count = yield countEntries(textbox.getAttribute("autocompletesearchparam"), searchEntries[i]);
|
||||||
|
ok(count > 0, "form history entry '" + searchEntries[i] + "' should exist");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testAutocomplete() {
|
||||||
|
var popup = searchBar.textbox.popup;
|
||||||
|
let popupShownPromise = promiseEvent(popup, "popupshown");
|
||||||
|
searchBar.textbox.showHistoryPopup();
|
||||||
|
yield popupShownPromise;
|
||||||
|
checkMenuEntries(searchEntries);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function testClearHistory() {
|
||||||
|
let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory")
|
||||||
|
ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled");
|
||||||
|
controller.doCommand("cmd_clearhistory");
|
||||||
|
let count = yield countEntries();
|
||||||
|
ok(count == 0, "History cleared");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function asyncCleanup() {
|
||||||
|
searchBar.value = "";
|
||||||
|
while (gBrowser.tabs.length != 1) {
|
||||||
|
gBrowser.removeTab(gBrowser.tabs[0], {animate: false});
|
||||||
|
}
|
||||||
|
content.location.href = "about:blank";
|
||||||
|
yield promiseRemoveEngine();
|
||||||
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||||
|
"resource://gre/modules/Promise.jsm");
|
||||||
|
|
||||||
function whenNewWindowLoaded(aOptions, aCallback) {
|
function whenNewWindowLoaded(aOptions, aCallback) {
|
||||||
let win = OpenBrowserWindow(aOptions);
|
let win = OpenBrowserWindow(aOptions);
|
||||||
let gotLoad = false;
|
let gotLoad = false;
|
||||||
@ -88,6 +91,18 @@ function waitForPopupShown(aPopupId, aCallback) {
|
|||||||
registerCleanupFunction(removePopupShownListener);
|
registerCleanupFunction(removePopupShownListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* promiseEvent(aTarget, aEventName, aPreventDefault) {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
aTarget.addEventListener(aEventName, function onEvent(aEvent) {
|
||||||
|
aTarget.removeEventListener(aEventName, onEvent, true);
|
||||||
|
if (aPreventDefault) {
|
||||||
|
aEvent.preventDefault();
|
||||||
|
}
|
||||||
|
deferred.resolve();
|
||||||
|
}, true);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
function waitForBrowserContextMenu(aCallback) {
|
function waitForBrowserContextMenu(aCallback) {
|
||||||
waitForPopupShown(gBrowser.selectedBrowser.contextMenu, aCallback);
|
waitForPopupShown(gBrowser.selectedBrowser.contextMenu, aCallback);
|
||||||
}
|
}
|
||||||
@ -106,3 +121,16 @@ function doOnloadOnce(aCallback) {
|
|||||||
gBrowser.addEventListener("load", doOnloadOnceListener, true);
|
gBrowser.addEventListener("load", doOnloadOnceListener, true);
|
||||||
registerCleanupFunction(removeDoOnloadOnceListener);
|
registerCleanupFunction(removeDoOnloadOnceListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* promiseOnLoad() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
|
||||||
|
gBrowser.addEventListener("load", function onLoadListener(aEvent) {
|
||||||
|
info("onLoadListener: " + aEvent.originalTarget.location);
|
||||||
|
gBrowser.removeEventListener("load", onLoadListener, true);
|
||||||
|
deferred.resolve(aEvent);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -22,13 +22,13 @@ const EventEmitter = devtools.require("devtools/toolkit/event-emitter");
|
|||||||
const FORBIDDEN_IDS = new Set(["toolbox", ""]);
|
const FORBIDDEN_IDS = new Set(["toolbox", ""]);
|
||||||
const MAX_ORDINAL = 99;
|
const MAX_ORDINAL = 99;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DevTools is a class that represents a set of developer tools, it holds a
|
* DevTools is a class that represents a set of developer tools, it holds a
|
||||||
* set of tools and keeps track of open toolboxes in the browser.
|
* set of tools and keeps track of open toolboxes in the browser.
|
||||||
*/
|
*/
|
||||||
this.DevTools = function DevTools() {
|
this.DevTools = function DevTools() {
|
||||||
this._tools = new Map(); // Map<toolId, tool>
|
this._tools = new Map(); // Map<toolId, tool>
|
||||||
|
this._themes = new Map(); // Map<themeId, theme>
|
||||||
this._toolboxes = new Map(); // Map<target, toolbox>
|
this._toolboxes = new Map(); // Map<target, toolbox>
|
||||||
|
|
||||||
// destroy() is an observer's handler so we need to preserve context.
|
// destroy() is an observer's handler so we need to preserve context.
|
||||||
@ -229,6 +229,136 @@ DevTools.prototype = {
|
|||||||
return definitions.sort(this.ordinalSort);
|
return definitions.sort(this.ordinalSort);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new theme for developer tools toolbox.
|
||||||
|
*
|
||||||
|
* A definition is a light object that holds various information about a
|
||||||
|
* theme.
|
||||||
|
*
|
||||||
|
* Each themeDefinition has the following properties:
|
||||||
|
* - id: Unique identifier for this theme (string|required)
|
||||||
|
* - label: Localized name for the theme to be displayed to the user
|
||||||
|
* (string|required)
|
||||||
|
* - stylesheets: Array of URLs pointing to a CSS document(s) containing
|
||||||
|
* the theme style rules (array|required)
|
||||||
|
* - classList: Array of class names identifying the theme within a document.
|
||||||
|
* These names are set to document element when applying
|
||||||
|
* the theme (array|required)
|
||||||
|
* - onApply: Function that is executed by the framework when the theme
|
||||||
|
* is applied. The function takes the current iframe window
|
||||||
|
* and the previous theme id as arguments (function)
|
||||||
|
* - onUnapply: Function that is executed by the framework when the theme
|
||||||
|
* is unapplied. The function takes the current iframe window
|
||||||
|
* and the new theme id as arguments (function)
|
||||||
|
*/
|
||||||
|
registerTheme: function DT_registerTheme(themeDefinition) {
|
||||||
|
let themeId = themeDefinition.id;
|
||||||
|
|
||||||
|
if (!themeId) {
|
||||||
|
throw new Error("Invalid theme id");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._themes.get(themeId)) {
|
||||||
|
throw new Error("Theme with the same id is already registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._themes.set(themeId, themeDefinition);
|
||||||
|
|
||||||
|
this.emit("theme-registered", themeId);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an existing theme from the list of registered themes.
|
||||||
|
* Needed so that add-ons can remove themselves when they are deactivated
|
||||||
|
*
|
||||||
|
* @param {string|object} theme
|
||||||
|
* Definition or the id of the theme to unregister.
|
||||||
|
*/
|
||||||
|
unregisterTheme: function DT_unregisterTheme(theme) {
|
||||||
|
let themeId = null;
|
||||||
|
if (typeof theme == "string") {
|
||||||
|
themeId = theme;
|
||||||
|
theme = this._themes.get(theme);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
themeId = theme.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currTheme = Services.prefs.getCharPref("devtools.theme");
|
||||||
|
|
||||||
|
// Change the current theme if it's being dynamically removed together
|
||||||
|
// with the owner (bootstrapped) extension.
|
||||||
|
// But, do not change it if the application is just shutting down.
|
||||||
|
if (!Services.startup.shuttingDown && theme.id == currTheme) {
|
||||||
|
Services.prefs.setCharPref("devtools.theme", "light");
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
pref: "devtools.theme",
|
||||||
|
newValue: "light",
|
||||||
|
oldValue: currTheme
|
||||||
|
};
|
||||||
|
|
||||||
|
gDevTools.emit("pref-changed", data);
|
||||||
|
|
||||||
|
this.emit("theme-unregistered", theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._themes.delete(themeId);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a theme definition if it exists.
|
||||||
|
*
|
||||||
|
* @param {string} themeId
|
||||||
|
* The id of the theme
|
||||||
|
*
|
||||||
|
* @return {ThemeDefinition|null} theme
|
||||||
|
* The ThemeDefinition for the id or null.
|
||||||
|
*/
|
||||||
|
getThemeDefinition: function DT_getThemeDefinition(themeId) {
|
||||||
|
let theme = this._themes.get(themeId);
|
||||||
|
if (!theme) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return theme;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get map of registered themes.
|
||||||
|
*
|
||||||
|
* @return {Map} themes
|
||||||
|
* A map of the the theme definitions registered in this instance
|
||||||
|
*/
|
||||||
|
getThemeDefinitionMap: function DT_getThemeDefinitionMap() {
|
||||||
|
let themes = new Map();
|
||||||
|
|
||||||
|
for (let [id, definition] of this._themes) {
|
||||||
|
if (this.getThemeDefinition(id)) {
|
||||||
|
themes.set(id, definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return themes;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get registered themes definitions sorted by ordinal value.
|
||||||
|
*
|
||||||
|
* @return {Array} themes
|
||||||
|
* A sorted array of the theme definitions registered in this instance
|
||||||
|
*/
|
||||||
|
getThemeDefinitionArray: function DT_getThemeDefinitionArray() {
|
||||||
|
let definitions = [];
|
||||||
|
|
||||||
|
for (let [id, definition] of this._themes) {
|
||||||
|
if (this.getThemeDefinition(id)) {
|
||||||
|
definitions.push(definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return definitions.sort(this.ordinalSort);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a Toolbox for a target (either by creating a new one, or if a toolbox
|
* Show a Toolbox for a target (either by creating a new one, or if a toolbox
|
||||||
* already exists for the target, by bring to the front the existing one)
|
* already exists for the target, by bring to the front the existing one)
|
||||||
|
@ -5,6 +5,7 @@ support-files =
|
|||||||
browser_toolbox_options_disable_js_iframe.html
|
browser_toolbox_options_disable_js_iframe.html
|
||||||
browser_toolbox_options_disable_cache.sjs
|
browser_toolbox_options_disable_cache.sjs
|
||||||
head.js
|
head.js
|
||||||
|
doc_theme.css
|
||||||
|
|
||||||
[browser_devtools_api.js]
|
[browser_devtools_api.js]
|
||||||
[browser_dynamic_tool_enabling.js]
|
[browser_dynamic_tool_enabling.js]
|
||||||
@ -32,6 +33,7 @@ skip-if = e10s # Bug 1030318
|
|||||||
[browser_toolbox_window_title_changes.js]
|
[browser_toolbox_window_title_changes.js]
|
||||||
[browser_toolbox_zoom.js]
|
[browser_toolbox_zoom.js]
|
||||||
[browser_toolbox_custom_host.js]
|
[browser_toolbox_custom_host.js]
|
||||||
|
[browser_toolbox_theme_registration.js]
|
||||||
|
|
||||||
# We want this test to run for mochitest-dt as well, so we include it here:
|
# We want this test to run for mochitest-dt as well, so we include it here:
|
||||||
[../../../base/content/test/general/browser_parsable_css.js]
|
[../../../base/content/test/general/browser_parsable_css.js]
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const CHROME_URL = "chrome://mochitests/content/browser/browser/devtools/framework/test/";
|
||||||
|
|
||||||
|
let toolbox;
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
gBrowser.selectedTab = gBrowser.addTab();
|
||||||
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
|
||||||
|
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||||
|
gDevTools.showToolbox(target).then(testRegister);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
content.location = "data:text/html,test for dynamically registering and unregistering themes";
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRegister(aToolbox)
|
||||||
|
{
|
||||||
|
toolbox = aToolbox
|
||||||
|
gDevTools.once("theme-registered", themeRegistered);
|
||||||
|
|
||||||
|
gDevTools.registerTheme({
|
||||||
|
id: "test-theme",
|
||||||
|
label: "Test theme",
|
||||||
|
stylesheets: [CHROME_URL + "doc_theme.css"],
|
||||||
|
classList: ["theme-test"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function themeRegistered(event, themeId)
|
||||||
|
{
|
||||||
|
is(themeId, "test-theme", "theme-registered event handler sent theme id");
|
||||||
|
|
||||||
|
ok(gDevTools.getThemeDefinitionMap().has(themeId), "theme added to map");
|
||||||
|
|
||||||
|
// Test that new theme appears in the Options panel
|
||||||
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
gDevTools.showToolbox(target, "options").then(() => {
|
||||||
|
let panel = toolbox.getCurrentPanel();
|
||||||
|
let doc = panel.panelWin.frameElement.contentDocument;
|
||||||
|
let themeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
|
||||||
|
|
||||||
|
ok(themeOption, "new theme exists in the Options panel");
|
||||||
|
|
||||||
|
// Apply the new theme.
|
||||||
|
applyTheme();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTheme()
|
||||||
|
{
|
||||||
|
let panelWin = toolbox.getCurrentPanel().panelWin;
|
||||||
|
let doc = panelWin.frameElement.contentDocument;
|
||||||
|
let testThemeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
|
||||||
|
let lightThemeOption = doc.querySelector("#devtools-theme-box > radio[value=light]");
|
||||||
|
|
||||||
|
let color = panelWin.getComputedStyle(testThemeOption).color;
|
||||||
|
isnot(color, "rgb(255, 0, 0)", "style unapplied");
|
||||||
|
|
||||||
|
// Select test theme.
|
||||||
|
testThemeOption.click();
|
||||||
|
|
||||||
|
let color = panelWin.getComputedStyle(testThemeOption).color;
|
||||||
|
is(color, "rgb(255, 0, 0)", "style applied");
|
||||||
|
|
||||||
|
// Select light theme
|
||||||
|
lightThemeOption.click();
|
||||||
|
|
||||||
|
let color = panelWin.getComputedStyle(testThemeOption).color;
|
||||||
|
isnot(color, "rgb(255, 0, 0)", "style unapplied");
|
||||||
|
|
||||||
|
// Select test theme again.
|
||||||
|
testThemeOption.click();
|
||||||
|
|
||||||
|
// Then unregister the test theme.
|
||||||
|
testUnregister();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testUnregister()
|
||||||
|
{
|
||||||
|
gDevTools.unregisterTheme("test-theme");
|
||||||
|
|
||||||
|
ok(!gDevTools.getThemeDefinitionMap().has("test-theme"), "theme removed from map");
|
||||||
|
|
||||||
|
let panelWin = toolbox.getCurrentPanel().panelWin;
|
||||||
|
let doc = panelWin.frameElement.contentDocument;
|
||||||
|
let themeBox = doc.querySelector("#devtools-theme-box");
|
||||||
|
|
||||||
|
// The default light theme must be selected now.
|
||||||
|
is(themeBox.selectedItem, themeBox.querySelector("[value=light]"),
|
||||||
|
"theme light must be selected");
|
||||||
|
|
||||||
|
// Make sure the tab-attaching process is done before we destroy the toolbox.
|
||||||
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
let actor = target.activeTab.actor;
|
||||||
|
target.client.attachTab(actor, (response) => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup()
|
||||||
|
{
|
||||||
|
toolbox.destroy().then(function() {
|
||||||
|
toolbox = null;
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
3
browser/devtools/framework/test/doc_theme.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.theme-test #devtools-theme-box radio {
|
||||||
|
color: red !important;
|
||||||
|
}
|
@ -75,6 +75,8 @@ function OptionsPanel(iframeWindow, toolbox) {
|
|||||||
this.isReady = false;
|
this.isReady = false;
|
||||||
|
|
||||||
this._prefChanged = this._prefChanged.bind(this);
|
this._prefChanged = this._prefChanged.bind(this);
|
||||||
|
this._themeRegistered = this._themeRegistered.bind(this);
|
||||||
|
this._themeUnregistered = this._themeUnregistered.bind(this);
|
||||||
|
|
||||||
this._addListeners();
|
this._addListeners();
|
||||||
|
|
||||||
@ -101,7 +103,9 @@ OptionsPanel.prototype = {
|
|||||||
return targetPromise.then(() => {
|
return targetPromise.then(() => {
|
||||||
this.setupToolsList();
|
this.setupToolsList();
|
||||||
this.setupToolbarButtonsList();
|
this.setupToolbarButtonsList();
|
||||||
|
this.setupThemeList();
|
||||||
this.populatePreferences();
|
this.populatePreferences();
|
||||||
|
this.updateDefaultTheme();
|
||||||
|
|
||||||
this._disableJSClicked = this._disableJSClicked.bind(this);
|
this._disableJSClicked = this._disableJSClicked.bind(this);
|
||||||
|
|
||||||
@ -119,10 +123,14 @@ OptionsPanel.prototype = {
|
|||||||
|
|
||||||
_addListeners: function() {
|
_addListeners: function() {
|
||||||
gDevTools.on("pref-changed", this._prefChanged);
|
gDevTools.on("pref-changed", this._prefChanged);
|
||||||
|
gDevTools.on("theme-registered", this._themeRegistered);
|
||||||
|
gDevTools.on("theme-unregistered", this._themeUnregistered);
|
||||||
},
|
},
|
||||||
|
|
||||||
_removeListeners: function() {
|
_removeListeners: function() {
|
||||||
gDevTools.off("pref-changed", this._prefChanged);
|
gDevTools.off("pref-changed", this._prefChanged);
|
||||||
|
gDevTools.off("theme-registered", this._themeRegistered);
|
||||||
|
gDevTools.off("theme-unregistered", this._themeUnregistered);
|
||||||
},
|
},
|
||||||
|
|
||||||
_prefChanged: function(event, data) {
|
_prefChanged: function(event, data) {
|
||||||
@ -132,6 +140,22 @@ OptionsPanel.prototype = {
|
|||||||
|
|
||||||
cbx.checked = cacheDisabled;
|
cbx.checked = cacheDisabled;
|
||||||
}
|
}
|
||||||
|
else if (data.pref === "devtools.theme") {
|
||||||
|
this.updateCurrentTheme();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_themeRegistered: function(event, themeId) {
|
||||||
|
this.setupThemeList();
|
||||||
|
},
|
||||||
|
|
||||||
|
_themeUnregistered: function(event, theme) {
|
||||||
|
let themeBox = this.panelDoc.getElementById("devtools-theme-box");
|
||||||
|
let themeOption = themeBox.querySelector("[value=" + theme.id + "]");
|
||||||
|
|
||||||
|
if (themeOption) {
|
||||||
|
themeBox.removeChild(themeOption);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setupToolbarButtonsList: function() {
|
setupToolbarButtonsList: function() {
|
||||||
@ -229,6 +253,26 @@ OptionsPanel.prototype = {
|
|||||||
this.panelWin.focus();
|
this.panelWin.focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setupThemeList: function() {
|
||||||
|
let themeBox = this.panelDoc.getElementById("devtools-theme-box");
|
||||||
|
themeBox.textContent = "";
|
||||||
|
|
||||||
|
let createThemeOption = theme => {
|
||||||
|
let radio = this.panelDoc.createElement("radio");
|
||||||
|
radio.setAttribute("value", theme.id);
|
||||||
|
radio.setAttribute("label", theme.label);
|
||||||
|
return radio;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Populating the default theme list
|
||||||
|
let themes = gDevTools.getThemeDefinitionArray();
|
||||||
|
for (let theme of themes) {
|
||||||
|
themeBox.appendChild(createThemeOption(theme));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateCurrentTheme();
|
||||||
|
},
|
||||||
|
|
||||||
populatePreferences: function() {
|
populatePreferences: function() {
|
||||||
let prefCheckboxes = this.panelDoc.querySelectorAll("checkbox[data-pref]");
|
let prefCheckboxes = this.panelDoc.querySelectorAll("checkbox[data-pref]");
|
||||||
for (let checkbox of prefCheckboxes) {
|
for (let checkbox of prefCheckboxes) {
|
||||||
@ -258,9 +302,13 @@ OptionsPanel.prototype = {
|
|||||||
pref: this.getAttribute("data-pref"),
|
pref: this.getAttribute("data-pref"),
|
||||||
newValue: this.selectedItem.getAttribute("value")
|
newValue: this.selectedItem.getAttribute("value")
|
||||||
};
|
};
|
||||||
|
|
||||||
data.oldValue = GetPref(data.pref);
|
data.oldValue = GetPref(data.pref);
|
||||||
SetPref(data.pref, data.newValue);
|
SetPref(data.pref, data.newValue);
|
||||||
gDevTools.emit("pref-changed", data);
|
|
||||||
|
if (data.newValue != data.oldValue) {
|
||||||
|
gDevTools.emit("pref-changed", data);
|
||||||
|
}
|
||||||
}.bind(radiogroup));
|
}.bind(radiogroup));
|
||||||
}
|
}
|
||||||
let prefMenulists = this.panelDoc.querySelectorAll("menulist[data-pref]");
|
let prefMenulists = this.panelDoc.querySelectorAll("menulist[data-pref]");
|
||||||
@ -292,6 +340,25 @@ OptionsPanel.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateDefaultTheme: function() {
|
||||||
|
// Make sure a theme is set in case the previous one coming from
|
||||||
|
// an extension isn't available anymore.
|
||||||
|
let themeBox = this.panelDoc.getElementById("devtools-theme-box");
|
||||||
|
if (themeBox.selectedIndex == -1) {
|
||||||
|
themeBox.selectedItem = themeBox.querySelector("[value=light]");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateCurrentTheme: function() {
|
||||||
|
let currentTheme = GetPref("devtools.theme");
|
||||||
|
let themeBox = this.panelDoc.getElementById("devtools-theme-box");
|
||||||
|
let themeOption = themeBox.querySelector("[value=" + currentTheme + "]");
|
||||||
|
|
||||||
|
if (themeOption) {
|
||||||
|
themeBox.selectedItem = themeOption;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_populateDisableJSCheckbox: function() {
|
_populateDisableJSCheckbox: function() {
|
||||||
let cbx = this.panelDoc.getElementById("devtools-disable-javascript");
|
let cbx = this.panelDoc.getElementById("devtools-disable-javascript");
|
||||||
cbx.checked = !this._origJavascriptEnabled;
|
cbx.checked = !this._origJavascriptEnabled;
|
||||||
|
@ -33,8 +33,6 @@
|
|||||||
class="options-groupbox"
|
class="options-groupbox"
|
||||||
data-pref="devtools.theme"
|
data-pref="devtools.theme"
|
||||||
orient="horizontal">
|
orient="horizontal">
|
||||||
<radio value="light" label="&options.lightTheme.label;"/>
|
|
||||||
<radio value="dark" label="&options.darkTheme.label;"/>
|
|
||||||
</radiogroup>
|
</radiogroup>
|
||||||
<label>&options.commonPrefs.label;</label>
|
<label>&options.commonPrefs.label;</label>
|
||||||
<vbox id="commonprefs-options" class="options-groupbox">
|
<vbox id="commonprefs-options" class="options-groupbox">
|
||||||
|
@ -357,6 +357,31 @@ for (let definition of defaultTools) {
|
|||||||
gDevTools.registerTool(definition);
|
gDevTools.registerTool(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tools.darkTheme = {
|
||||||
|
id: "dark",
|
||||||
|
label: l10n("options.darkTheme.label", toolboxStrings),
|
||||||
|
ordinal: 1,
|
||||||
|
stylesheets: ["chrome://browser/skin/devtools/dark-theme.css"],
|
||||||
|
classList: ["theme-dark"],
|
||||||
|
};
|
||||||
|
|
||||||
|
Tools.lightTheme = {
|
||||||
|
id: "light",
|
||||||
|
label: l10n("options.lightTheme.label", toolboxStrings),
|
||||||
|
ordinal: 2,
|
||||||
|
stylesheets: ["chrome://browser/skin/devtools/light-theme.css"],
|
||||||
|
classList: ["theme-light"],
|
||||||
|
};
|
||||||
|
|
||||||
|
let defaultThemes = [
|
||||||
|
Tools.darkTheme,
|
||||||
|
Tools.lightTheme,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let definition of defaultThemes) {
|
||||||
|
gDevTools.registerTheme(definition);
|
||||||
|
}
|
||||||
|
|
||||||
var unloadObserver = {
|
var unloadObserver = {
|
||||||
observe: function(subject, topic, data) {
|
observe: function(subject, topic, data) {
|
||||||
if (subject.wrappedJSObject === require("@loader/unload")) {
|
if (subject.wrappedJSObject === require("@loader/unload")) {
|
||||||
@ -364,6 +389,9 @@ var unloadObserver = {
|
|||||||
for (let definition of gDevTools.getToolDefinitionArray()) {
|
for (let definition of gDevTools.getToolDefinitionArray()) {
|
||||||
gDevTools.unregisterTool(definition.id);
|
gDevTools.unregisterTool(definition.id);
|
||||||
}
|
}
|
||||||
|
for (let definition of gDevTools.getThemeDefinitionArray()) {
|
||||||
|
gDevTools.unregisterTheme(definition.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
let { utils: Cu, interfaces: Ci } = Components;
|
||||||
|
|
||||||
addMessageListener("devtools:test:history", function ({ data }) {
|
addMessageListener("devtools:test:history", function ({ data }) {
|
||||||
content.history[data.direction]();
|
content.history[data.direction]();
|
||||||
});
|
});
|
||||||
@ -16,3 +18,11 @@ addMessageListener("devtools:test:reload", function ({ data }) {
|
|||||||
data = data || {};
|
data = data || {};
|
||||||
content.location.reload(data.forceget);
|
content.location.reload(data.forceget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addMessageListener("devtools:test:forceCC", function () {
|
||||||
|
let DOMWindowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils)
|
||||||
|
DOMWindowUtils.cycleCollect();
|
||||||
|
DOMWindowUtils.garbageCollect();
|
||||||
|
DOMWindowUtils.garbageCollect();
|
||||||
|
});
|
||||||
|
@ -24,24 +24,35 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldTheme && newTheme != oldTheme) {
|
let oldThemeDef = gDevTools.getThemeDefinition(oldTheme);
|
||||||
StylesheetUtils.removeSheet(
|
let newThemeDef = gDevTools.getThemeDefinition(newTheme);
|
||||||
window,
|
|
||||||
DEVTOOLS_SKIN_URL + oldTheme + "-theme.css",
|
// Unload all theme stylesheets related to the old theme.
|
||||||
"author"
|
if (oldThemeDef) {
|
||||||
);
|
for (let url of oldThemeDef.stylesheets) {
|
||||||
|
StylesheetUtils.removeSheet(window, url, "author");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StylesheetUtils.loadSheet(
|
// Load all stylesheets associated with the new theme.
|
||||||
window,
|
let newThemeDef = gDevTools.getThemeDefinition(newTheme);
|
||||||
DEVTOOLS_SKIN_URL + newTheme + "-theme.css",
|
|
||||||
"author"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Floating scrollbars à la osx
|
// The theme might not be available anymore (e.g. uninstalled)
|
||||||
|
// Use the default one.
|
||||||
|
if (!newThemeDef) {
|
||||||
|
newThemeDef = gDevTools.getThemeDefinition("light");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let url of newThemeDef.stylesheets) {
|
||||||
|
StylesheetUtils.loadSheet(window, url, "author");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Floating scroll-bars like in OSX
|
||||||
let hiddenDOMWindow = Cc["@mozilla.org/appshell/appShellService;1"]
|
let hiddenDOMWindow = Cc["@mozilla.org/appshell/appShellService;1"]
|
||||||
.getService(Ci.nsIAppShellService)
|
.getService(Ci.nsIAppShellService)
|
||||||
.hiddenDOMWindow;
|
.hiddenDOMWindow;
|
||||||
|
|
||||||
|
// TODO: extensions might want to customize scrollbar styles too.
|
||||||
if (!hiddenDOMWindow.matchMedia("(-moz-overlay-scrollbars)").matches) {
|
if (!hiddenDOMWindow.matchMedia("(-moz-overlay-scrollbars)").matches) {
|
||||||
let scrollbarsUrl = Services.io.newURI(
|
let scrollbarsUrl = Services.io.newURI(
|
||||||
DEVTOOLS_SKIN_URL + "floating-scrollbars-light.css", null, null);
|
DEVTOOLS_SKIN_URL + "floating-scrollbars-light.css", null, null);
|
||||||
@ -62,8 +73,26 @@
|
|||||||
forceStyle();
|
forceStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
documentElement.classList.remove("theme-" + oldTheme);
|
if (oldThemeDef) {
|
||||||
documentElement.classList.add("theme-" + newTheme);
|
for (let name of oldThemeDef.classList) {
|
||||||
|
documentElement.classList.remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldThemeDef.onUnapply) {
|
||||||
|
oldThemeDef.onUnapply(window, newTheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let name of newThemeDef.classList) {
|
||||||
|
documentElement.classList.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newThemeDef.onApply) {
|
||||||
|
newThemeDef.onApply(window, oldTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final notification for further theme-switching related logic.
|
||||||
|
gDevTools.emit("theme-switched", window, newTheme, oldTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePrefChange(event, data) {
|
function handlePrefChange(event, data) {
|
||||||
|
@ -10,18 +10,23 @@ support-files =
|
|||||||
doc_connect-toggle.html
|
doc_connect-toggle.html
|
||||||
doc_connect-param.html
|
doc_connect-param.html
|
||||||
doc_connect-multi-param.html
|
doc_connect-multi-param.html
|
||||||
|
doc_change-param.html
|
||||||
440hz_sine.ogg
|
440hz_sine.ogg
|
||||||
head.js
|
head.js
|
||||||
|
|
||||||
[browser_audionode-actor-get-set-param.js]
|
[browser_audionode-actor-get-param-flags.js]
|
||||||
[browser_audionode-actor-get-type.js]
|
|
||||||
[browser_audionode-actor-get-params-01.js]
|
[browser_audionode-actor-get-params-01.js]
|
||||||
[browser_audionode-actor-get-params-02.js]
|
[browser_audionode-actor-get-params-02.js]
|
||||||
[browser_audionode-actor-get-param-flags.js]
|
[browser_audionode-actor-get-set-param.js]
|
||||||
|
[browser_audionode-actor-get-type.js]
|
||||||
[browser_audionode-actor-is-source.js]
|
[browser_audionode-actor-is-source.js]
|
||||||
[browser_webaudio-actor-simple.js]
|
|
||||||
[browser_webaudio-actor-destroy-node.js]
|
[browser_webaudio-actor-change-params-01.js]
|
||||||
|
[browser_webaudio-actor-change-params-02.js]
|
||||||
|
[browser_webaudio-actor-change-params-03.js]
|
||||||
[browser_webaudio-actor-connect-param.js]
|
[browser_webaudio-actor-connect-param.js]
|
||||||
|
[browser_webaudio-actor-destroy-node.js]
|
||||||
|
[browser_webaudio-actor-simple.js]
|
||||||
|
|
||||||
[browser_wa_destroy-node-01.js]
|
[browser_wa_destroy-node-01.js]
|
||||||
|
|
||||||
@ -48,4 +53,5 @@ support-files =
|
|||||||
# [browser_wa_properties-view-edit-02.js]
|
# [browser_wa_properties-view-edit-02.js]
|
||||||
# Disabled for too many intermittents bug 1010423
|
# Disabled for too many intermittents bug 1010423
|
||||||
[browser_wa_properties-view-params.js]
|
[browser_wa_properties-view-params.js]
|
||||||
|
[browser_wa_properties-view-change-params.js]
|
||||||
[browser_wa_properties-view-params-objects.js]
|
[browser_wa_properties-view-params-objects.js]
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that params view correctly updates changed parameters
|
||||||
|
* when source code updates them, as well as CHANGE_PARAM events.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function spawnTest() {
|
||||||
|
let [target, debuggee, panel] = yield initWebAudioEditor(CHANGE_PARAM_URL);
|
||||||
|
let { panelWin } = panel;
|
||||||
|
let { gFront, $, $$, EVENTS, WebAudioInspectorView } = panelWin;
|
||||||
|
let gVars = WebAudioInspectorView._propsView;
|
||||||
|
|
||||||
|
// Set parameter polling to 20ms for tests
|
||||||
|
panelWin.PARAM_POLLING_FREQUENCY = 20;
|
||||||
|
|
||||||
|
let started = once(gFront, "start-context");
|
||||||
|
|
||||||
|
reload(target);
|
||||||
|
|
||||||
|
let [actors] = yield Promise.all([
|
||||||
|
getN(gFront, "create-node", 3),
|
||||||
|
waitForGraphRendered(panelWin, 3, 0)
|
||||||
|
]);
|
||||||
|
|
||||||
|
let oscId = actors[1].actorID;
|
||||||
|
|
||||||
|
click(panelWin, findGraphNode(panelWin, oscId));
|
||||||
|
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
||||||
|
|
||||||
|
// Yield twice so we get a diff
|
||||||
|
yield once(panelWin, EVENTS.CHANGE_PARAM);
|
||||||
|
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
|
||||||
|
is(args.actorID, oscId, "EVENTS.CHANGE_PARAM has correct `actorID`");
|
||||||
|
ok(args.oldValue < args.newValue, "EVENTS.CHANGE_PARAM has correct `newValue` and `oldValue`");
|
||||||
|
is(args.param, "detune", "EVENTS.CHANGE_PARAM has correct `param`");
|
||||||
|
|
||||||
|
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
|
||||||
|
checkVariableView(gVars, 0, { "detune": args.newValue }, "`detune` parameter updated.");
|
||||||
|
let [[_, args]] = yield getSpread(panelWin, EVENTS.CHANGE_PARAM);
|
||||||
|
checkVariableView(gVars, 0, { "detune": args.newValue }, "`detune` parameter updated.");
|
||||||
|
|
||||||
|
yield teardown(panel);
|
||||||
|
finish();
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test WebAudioActor `change-param` events and front.[en|dis]ableChangeParamEvents
|
||||||
|
*/
|
||||||
|
|
||||||
|
function spawnTest () {
|
||||||
|
let [target, debuggee, front] = yield initBackend(CHANGE_PARAM_URL);
|
||||||
|
let [_, nodes] = yield Promise.all([
|
||||||
|
front.setup({ reload: true }),
|
||||||
|
getN(front, "create-node", 3)
|
||||||
|
]);
|
||||||
|
|
||||||
|
let osc = nodes[1];
|
||||||
|
let eventCount = 0;
|
||||||
|
|
||||||
|
yield front.enableChangeParamEvents(osc, 20);
|
||||||
|
|
||||||
|
front.on("change-param", onChangeParam);
|
||||||
|
|
||||||
|
yield getN(front, "change-param", 3);
|
||||||
|
yield front.disableChangeParamEvents();
|
||||||
|
|
||||||
|
let currEventCount = eventCount;
|
||||||
|
|
||||||
|
// Be flexible here incase we get an extra counter before the listener is turned off
|
||||||
|
ok(eventCount >= 3, "Calling `enableChangeParamEvents` should allow front to emit `change-param`.");
|
||||||
|
|
||||||
|
yield wait(100);
|
||||||
|
|
||||||
|
ok((eventCount - currEventCount) <= 2, "Calling `disableChangeParamEvents` should turn off the listener.");
|
||||||
|
|
||||||
|
front.off("change-param", onChangeParam);
|
||||||
|
|
||||||
|
yield removeTab(target.tab);
|
||||||
|
finish();
|
||||||
|
|
||||||
|
function onChangeParam ({ newValue, oldValue, param, actorID }) {
|
||||||
|
is(actorID, osc.actorID, "correct `actorID` in `change-param`.");
|
||||||
|
is(param, "detune", "correct `param` property in `change-param`.");
|
||||||
|
ok(newValue > oldValue,
|
||||||
|
"correct `newValue` (" + newValue + ") and `oldValue` (" + oldValue + ") in `change-param`");
|
||||||
|
eventCount++;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that listening to param change polling does not break when the AudioNode is collected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function spawnTest () {
|
||||||
|
let [target, debuggee, front] = yield initBackend(DESTROY_NODES_URL);
|
||||||
|
let waitUntilDestroyed = getN(front, "destroy-node", 10);
|
||||||
|
let [_, nodes] = yield Promise.all([
|
||||||
|
front.setup({ reload: true }),
|
||||||
|
getN(front, "create-node", 13)
|
||||||
|
]);
|
||||||
|
|
||||||
|
let bufferNode = nodes[6];
|
||||||
|
|
||||||
|
yield front.enableChangeParamEvents(bufferNode, 20);
|
||||||
|
|
||||||
|
front.on("change-param", onChangeParam);
|
||||||
|
|
||||||
|
forceCC();
|
||||||
|
|
||||||
|
yield waitUntilDestroyed;
|
||||||
|
yield wait(50);
|
||||||
|
|
||||||
|
front.off("change-param", onChangeParam);
|
||||||
|
|
||||||
|
ok(true, "listening to `change-param` on a dead node doesn't throw.");
|
||||||
|
yield removeTab(target.tab);
|
||||||
|
finish();
|
||||||
|
|
||||||
|
function onChangeParam (args) {
|
||||||
|
ok(false, "`change-param` should not be emitted on a node that hasn't changed params or is dead.");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test WebAudioActor `change-param` events on special types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function spawnTest () {
|
||||||
|
let [target, debuggee, front] = yield initBackend(CHANGE_PARAM_URL);
|
||||||
|
let [_, nodes] = yield Promise.all([
|
||||||
|
front.setup({ reload: true }),
|
||||||
|
getN(front, "create-node", 3)
|
||||||
|
]);
|
||||||
|
|
||||||
|
let shaper = nodes[2];
|
||||||
|
let eventCount = 0;
|
||||||
|
|
||||||
|
yield front.enableChangeParamEvents(shaper, 20);
|
||||||
|
|
||||||
|
let onChange = once(front, "change-param");
|
||||||
|
|
||||||
|
shaper.setParam("curve", null);
|
||||||
|
|
||||||
|
let { newValue, oldValue } = yield onChange;
|
||||||
|
|
||||||
|
is(oldValue.type, "object", "`oldValue` should be an object.");
|
||||||
|
is(oldValue.class, "Float32Array", "`oldValue` should be of class Float32Array.");
|
||||||
|
is(newValue.type, "null", "`newValue` should be null.");
|
||||||
|
|
||||||
|
yield removeTab(target.tab);
|
||||||
|
finish();
|
||||||
|
}
|
25
browser/devtools/webaudioeditor/test/doc_change-param.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!-- Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>Web Audio Editor test page</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.8">
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
let ctx = new AudioContext();
|
||||||
|
let osc = ctx.createOscillator();
|
||||||
|
let shaperNode = ctx.createWaveShaper();
|
||||||
|
let detuneVal = 0;
|
||||||
|
shaperNode.curve = new Float32Array(65536);
|
||||||
|
setInterval(() => osc.detune.value = ++detuneVal, 10);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -19,7 +19,9 @@ let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.j
|
|||||||
|
|
||||||
let { WebAudioFront } = devtools.require("devtools/server/actors/webaudio");
|
let { WebAudioFront } = devtools.require("devtools/server/actors/webaudio");
|
||||||
let TargetFactory = devtools.TargetFactory;
|
let TargetFactory = devtools.TargetFactory;
|
||||||
|
let mm = null;
|
||||||
|
|
||||||
|
const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js";
|
||||||
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/test/";
|
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/test/";
|
||||||
const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
|
const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
|
||||||
const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
|
const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
|
||||||
@ -30,6 +32,7 @@ const DESTROY_NODES_URL = EXAMPLE_URL + "doc_destroy-nodes.html";
|
|||||||
const CONNECT_TOGGLE_URL = EXAMPLE_URL + "doc_connect-toggle.html";
|
const CONNECT_TOGGLE_URL = EXAMPLE_URL + "doc_connect-toggle.html";
|
||||||
const CONNECT_PARAM_URL = EXAMPLE_URL + "doc_connect-param.html";
|
const CONNECT_PARAM_URL = EXAMPLE_URL + "doc_connect-param.html";
|
||||||
const CONNECT_MULTI_PARAM_URL = EXAMPLE_URL + "doc_connect-multi-param.html";
|
const CONNECT_MULTI_PARAM_URL = EXAMPLE_URL + "doc_connect-multi-param.html";
|
||||||
|
const CHANGE_PARAM_URL = EXAMPLE_URL + "doc_change-param.html";
|
||||||
|
|
||||||
// All tests are asynchronous.
|
// All tests are asynchronous.
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
@ -133,6 +136,8 @@ function initBackend(aUrl) {
|
|||||||
yield target.makeRemote();
|
yield target.makeRemote();
|
||||||
|
|
||||||
let front = new WebAudioFront(target.client, target.form);
|
let front = new WebAudioFront(target.client, target.form);
|
||||||
|
|
||||||
|
loadFrameScripts();
|
||||||
return [target, debuggee, front];
|
return [target, debuggee, front];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -150,6 +155,8 @@ function initWebAudioEditor(aUrl) {
|
|||||||
Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
|
Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
|
||||||
let toolbox = yield gDevTools.showToolbox(target, "webaudioeditor");
|
let toolbox = yield gDevTools.showToolbox(target, "webaudioeditor");
|
||||||
let panel = toolbox.getCurrentPanel();
|
let panel = toolbox.getCurrentPanel();
|
||||||
|
|
||||||
|
loadFrameScripts();
|
||||||
return [target, debuggee, panel];
|
return [target, debuggee, panel];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -387,9 +394,12 @@ function countGraphObjects (win) {
|
|||||||
* Forces cycle collection and GC, used in AudioNode destruction tests.
|
* Forces cycle collection and GC, used in AudioNode destruction tests.
|
||||||
*/
|
*/
|
||||||
function forceCC () {
|
function forceCC () {
|
||||||
SpecialPowers.DOMWindowUtils.cycleCollect();
|
mm.sendAsyncMessage("devtools:test:forceCC");
|
||||||
SpecialPowers.DOMWindowUtils.garbageCollect();
|
}
|
||||||
SpecialPowers.DOMWindowUtils.garbageCollect();
|
|
||||||
|
function loadFrameScripts () {
|
||||||
|
mm = gBrowser.selectedBrowser.messageManager;
|
||||||
|
mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,9 +20,10 @@ const STRINGS_URI = "chrome://browser/locale/devtools/webaudioeditor.properties"
|
|||||||
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||||
const Telemetry = require("devtools/shared/telemetry");
|
const Telemetry = require("devtools/shared/telemetry");
|
||||||
const telemetry = new Telemetry();
|
const telemetry = new Telemetry();
|
||||||
|
|
||||||
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
|
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
|
||||||
|
|
||||||
|
let PARAM_POLLING_FREQUENCY = 1000;
|
||||||
|
|
||||||
// The panel's window global is an EventEmitter firing the following events:
|
// The panel's window global is an EventEmitter firing the following events:
|
||||||
const EVENTS = {
|
const EVENTS = {
|
||||||
// Fired when the first AudioNode has been created, signifying
|
// Fired when the first AudioNode has been created, signifying
|
||||||
@ -173,6 +174,8 @@ let WebAudioEditorController = {
|
|||||||
telemetry.toolOpened("webaudioeditor");
|
telemetry.toolOpened("webaudioeditor");
|
||||||
this._onTabNavigated = this._onTabNavigated.bind(this);
|
this._onTabNavigated = this._onTabNavigated.bind(this);
|
||||||
this._onThemeChange = this._onThemeChange.bind(this);
|
this._onThemeChange = this._onThemeChange.bind(this);
|
||||||
|
this._onSelectNode = this._onSelectNode.bind(this);
|
||||||
|
this._onChangeParam = this._onChangeParam.bind(this);
|
||||||
gTarget.on("will-navigate", this._onTabNavigated);
|
gTarget.on("will-navigate", this._onTabNavigated);
|
||||||
gTarget.on("navigate", this._onTabNavigated);
|
gTarget.on("navigate", this._onTabNavigated);
|
||||||
gFront.on("start-context", this._onStartContext);
|
gFront.on("start-context", this._onStartContext);
|
||||||
@ -194,12 +197,15 @@ let WebAudioEditorController = {
|
|||||||
window.on(EVENTS.DISCONNECT_NODE, this._onUpdatedContext);
|
window.on(EVENTS.DISCONNECT_NODE, this._onUpdatedContext);
|
||||||
window.on(EVENTS.DESTROY_NODE, this._onUpdatedContext);
|
window.on(EVENTS.DESTROY_NODE, this._onUpdatedContext);
|
||||||
window.on(EVENTS.CONNECT_PARAM, this._onUpdatedContext);
|
window.on(EVENTS.CONNECT_PARAM, this._onUpdatedContext);
|
||||||
|
|
||||||
|
// Set up a controller for managing parameter changes per audio node
|
||||||
|
window.on(EVENTS.UI_SELECT_NODE, this._onSelectNode);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove events emitted by the current tab target.
|
* Remove events emitted by the current tab target.
|
||||||
*/
|
*/
|
||||||
destroy: function() {
|
destroy: Task.async(function* () {
|
||||||
telemetry.toolClosed("webaudioeditor");
|
telemetry.toolClosed("webaudioeditor");
|
||||||
gTarget.off("will-navigate", this._onTabNavigated);
|
gTarget.off("will-navigate", this._onTabNavigated);
|
||||||
gTarget.off("navigate", this._onTabNavigated);
|
gTarget.off("navigate", this._onTabNavigated);
|
||||||
@ -215,8 +221,11 @@ let WebAudioEditorController = {
|
|||||||
window.off(EVENTS.DISCONNECT_NODE, this._onUpdatedContext);
|
window.off(EVENTS.DISCONNECT_NODE, this._onUpdatedContext);
|
||||||
window.off(EVENTS.DESTROY_NODE, this._onUpdatedContext);
|
window.off(EVENTS.DESTROY_NODE, this._onUpdatedContext);
|
||||||
window.off(EVENTS.CONNECT_PARAM, this._onUpdatedContext);
|
window.off(EVENTS.CONNECT_PARAM, this._onUpdatedContext);
|
||||||
|
window.off(EVENTS.UI_SELECT_NODE, this._onSelectNode);
|
||||||
gDevTools.off("pref-changed", this._onThemeChange);
|
gDevTools.off("pref-changed", this._onThemeChange);
|
||||||
},
|
|
||||||
|
yield gFront.disableChangeParamEvents();
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when page is reloaded to show the reload notice and waiting
|
* Called when page is reloaded to show the reload notice and waiting
|
||||||
@ -345,9 +354,21 @@ let WebAudioEditorController = {
|
|||||||
/**
|
/**
|
||||||
* Called when a node param is changed.
|
* Called when a node param is changed.
|
||||||
*/
|
*/
|
||||||
_onChangeParam: function({ actor, param, value }) {
|
_onChangeParam: function (args) {
|
||||||
window.emit(EVENTS.CHANGE_PARAM, getViewNodeByActor(actor), param, value);
|
window.emit(EVENTS.CHANGE_PARAM, args);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on UI_SELECT_NODE, used to manage
|
||||||
|
* `change-param` events on that node.
|
||||||
|
*/
|
||||||
|
_onSelectNode: function (_, id) {
|
||||||
|
let node = getViewNodeById(id);
|
||||||
|
|
||||||
|
if (node && node.actor) {
|
||||||
|
gFront.enableChangeParamEvents(node.actor, PARAM_POLLING_FREQUENCY);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -377,6 +377,7 @@ let WebAudioInspectorView = {
|
|||||||
this._onNodeSelect = this._onNodeSelect.bind(this);
|
this._onNodeSelect = this._onNodeSelect.bind(this);
|
||||||
this._onTogglePaneClick = this._onTogglePaneClick.bind(this);
|
this._onTogglePaneClick = this._onTogglePaneClick.bind(this);
|
||||||
this._onDestroyNode = this._onDestroyNode.bind(this);
|
this._onDestroyNode = this._onDestroyNode.bind(this);
|
||||||
|
this._onChangeParam = this._onChangeParam.bind(this);
|
||||||
|
|
||||||
this._inspectorPaneToggleButton.addEventListener("mousedown", this._onTogglePaneClick, false);
|
this._inspectorPaneToggleButton.addEventListener("mousedown", this._onTogglePaneClick, false);
|
||||||
this._propsView = new VariablesView($("#properties-tabpanel-content"), GENERIC_VARIABLES_VIEW_SETTINGS);
|
this._propsView = new VariablesView($("#properties-tabpanel-content"), GENERIC_VARIABLES_VIEW_SETTINGS);
|
||||||
@ -384,6 +385,7 @@ let WebAudioInspectorView = {
|
|||||||
|
|
||||||
window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
|
window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
|
||||||
window.on(EVENTS.DESTROY_NODE, this._onDestroyNode);
|
window.on(EVENTS.DESTROY_NODE, this._onDestroyNode);
|
||||||
|
window.on(EVENTS.CHANGE_PARAM, this._onChangeParam);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -393,6 +395,7 @@ let WebAudioInspectorView = {
|
|||||||
this._inspectorPaneToggleButton.removeEventListener("mousedown", this._onTogglePaneClick);
|
this._inspectorPaneToggleButton.removeEventListener("mousedown", this._onTogglePaneClick);
|
||||||
window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
|
window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
|
||||||
window.off(EVENTS.DESTROY_NODE, this._onDestroyNode);
|
window.off(EVENTS.DESTROY_NODE, this._onDestroyNode);
|
||||||
|
window.off(EVENTS.CHANGE_PARAM, this._onChangeParam);
|
||||||
|
|
||||||
this._inspectorPane = null;
|
this._inspectorPane = null;
|
||||||
this._inspectorPaneToggleButton = null;
|
this._inspectorPaneToggleButton = null;
|
||||||
@ -612,7 +615,22 @@ let WebAudioInspectorView = {
|
|||||||
if (this._currentNode && this._currentNode.id === id) {
|
if (this._currentNode && this._currentNode.id === id) {
|
||||||
this.setCurrentAudioNode(null);
|
this.setCurrentAudioNode(null);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when `CHANGE_PARAM` is fired. We should ensure that this event is
|
||||||
|
* for the same node that is currently selected. We check the existence
|
||||||
|
* of each part of the scope to make sure that if this event was fired
|
||||||
|
* during a VariablesView rebuild, then we just ignore it.
|
||||||
|
*/
|
||||||
|
_onChangeParam: function (_, { param, newValue, oldValue, actorID }) {
|
||||||
|
if (!this._currentNode || this._currentNode.actor.actorID !== actorID) return;
|
||||||
|
let scope = this._getAudioPropertiesScope();
|
||||||
|
if (!scope) return;
|
||||||
|
let property = scope.get(param);
|
||||||
|
if (!property) return;
|
||||||
|
property.setGrip(newValue);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,7 +113,10 @@ support-files =
|
|||||||
test-console-api-stackframe.html
|
test-console-api-stackframe.html
|
||||||
test_bug_1010953_cspro.html^headers^
|
test_bug_1010953_cspro.html^headers^
|
||||||
test_bug_1010953_cspro.html
|
test_bug_1010953_cspro.html
|
||||||
|
test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
|
||||||
|
test_bug1045902_console_csp_ignore_reflected_xss_message.html
|
||||||
|
|
||||||
|
[browser_bug1045902_console_csp_ignore_reflected_xss_message.js]
|
||||||
[browser_bug664688_sandbox_update_after_navigation.js]
|
[browser_bug664688_sandbox_update_after_navigation.js]
|
||||||
[browser_bug_638949_copy_link_location.js]
|
[browser_bug_638949_copy_link_location.js]
|
||||||
[browser_bug_862916_console_dir_and_filter_off.js]
|
[browser_bug_862916_console_dir_and_filter_off.js]
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Description of the test:
|
||||||
|
* We are loading a file with the following CSP:
|
||||||
|
* 'reflected-xss filter'
|
||||||
|
* This directive is not supported, hence we confirm that
|
||||||
|
* the according message is displayed in the web console.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const EXPECTED_RESULT = "Not supporting directive 'reflected-xss'. Directive and values will be ignored.";
|
||||||
|
const TEST_FILE = "http://example.com/browser/browser/devtools/webconsole/test/" +
|
||||||
|
"test_bug1045902_console_csp_ignore_reflected_xss_message.html";
|
||||||
|
|
||||||
|
let hud = undefined;
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
addTab("data:text/html;charset=utf8,Web Console CSP ignoring reflected XSS (bug 1045902)");
|
||||||
|
browser.addEventListener("load", function _onLoad() {
|
||||||
|
browser.removeEventListener("load", _onLoad, true);
|
||||||
|
openConsole(null, loadDocument);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDocument(theHud) {
|
||||||
|
hud = theHud;
|
||||||
|
hud.jsterm.clearOutput()
|
||||||
|
browser.addEventListener("load", onLoad, true);
|
||||||
|
content.location = TEST_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLoad(aEvent) {
|
||||||
|
browser.removeEventListener("load", onLoad, true);
|
||||||
|
testViolationMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testViolationMessage() {
|
||||||
|
let aOutputNode = hud.outputNode;
|
||||||
|
|
||||||
|
waitForSuccess({
|
||||||
|
name: "Confirming that CSP logs messages to the console when 'reflected-xss' directive is used!",
|
||||||
|
validatorFn: function() {
|
||||||
|
console.log(hud.outputNode.textContent);
|
||||||
|
let success = false;
|
||||||
|
success = hud.outputNode.textContent.indexOf(EXPECTED_RESULT) > -1;
|
||||||
|
return success;
|
||||||
|
},
|
||||||
|
successFn: finishTest,
|
||||||
|
failureFn: finishTest,
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Bug 1045902 - CSP: Log console message for 'reflected-xss'</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Bug 1045902 - CSP: Log console message for 'reflected-xss'
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1 @@
|
|||||||
|
Content-Security-Policy: reflected-xss filter;
|
@ -59,7 +59,7 @@
|
|||||||
<menuitem command="cmd_newApp" accesskey="&projectMenu_newApp_accesskey;"/>
|
<menuitem command="cmd_newApp" accesskey="&projectMenu_newApp_accesskey;"/>
|
||||||
<menuitem command="cmd_importPackagedApp" accesskey="&projectMenu_importPackagedApp_accesskey;"/>
|
<menuitem command="cmd_importPackagedApp" accesskey="&projectMenu_importPackagedApp_accesskey;"/>
|
||||||
<menuitem command="cmd_importHostedApp" accesskey="&projectMenu_importHostedApp_accesskey;"/>
|
<menuitem command="cmd_importHostedApp" accesskey="&projectMenu_importHostedApp_accesskey;"/>
|
||||||
<menuitem command="cmd_showProjectPanel" key="key_showProjectPanel" label="&projectMenu_selectApp_label;" accesskey="&projectMenu_selectApp_accessley;"/>
|
<menuitem command="cmd_showProjectPanel" key="key_showProjectPanel" label="&projectMenu_selectApp_label;" accesskey="&projectMenu_selectApp_accesskey;"/>
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
<menuitem command="cmd_play" key="key_play" label="&projectMenu_play_label;" accesskey="&projectMenu_play_accesskey;"/>
|
<menuitem command="cmd_play" key="key_play" label="&projectMenu_play_label;" accesskey="&projectMenu_play_accesskey;"/>
|
||||||
<menuitem command="cmd_stop" accesskey="&projectMenu_stop_accesskey;"/>
|
<menuitem command="cmd_stop" accesskey="&projectMenu_stop_accesskey;"/>
|
||||||
|
@ -359,7 +359,7 @@ Experiments.Experiments = function (policy=new Experiments.Policy()) {
|
|||||||
// crashes. For forensics purposes, keep the last few log
|
// crashes. For forensics purposes, keep the last few log
|
||||||
// messages in memory and upload them in case of crash.
|
// messages in memory and upload them in case of crash.
|
||||||
this._forensicsLogs = [];
|
this._forensicsLogs = [];
|
||||||
this._forensicsLogs.length = 3;
|
this._forensicsLogs.length = 10;
|
||||||
this._log = Object.create(log);
|
this._log = Object.create(log);
|
||||||
this._log.log = (level, string, params) => {
|
this._log.log = (level, string, params) => {
|
||||||
this._forensicsLogs.shift();
|
this._forensicsLogs.shift();
|
||||||
|
@ -303,6 +303,7 @@
|
|||||||
@BINPATH@/components/toolkit_finalizationwitness.xpt
|
@BINPATH@/components/toolkit_finalizationwitness.xpt
|
||||||
@BINPATH@/components/toolkit_formautofill.xpt
|
@BINPATH@/components/toolkit_formautofill.xpt
|
||||||
@BINPATH@/components/toolkit_osfile.xpt
|
@BINPATH@/components/toolkit_osfile.xpt
|
||||||
|
@BINPATH@/components/toolkit_xulstore.xpt
|
||||||
@BINPATH@/components/toolkitprofile.xpt
|
@BINPATH@/components/toolkitprofile.xpt
|
||||||
#ifdef MOZ_ENABLE_XREMOTE
|
#ifdef MOZ_ENABLE_XREMOTE
|
||||||
@BINPATH@/components/toolkitremote.xpt
|
@BINPATH@/components/toolkitremote.xpt
|
||||||
@ -503,6 +504,8 @@
|
|||||||
@BINPATH@/components/cryptoComponents.manifest
|
@BINPATH@/components/cryptoComponents.manifest
|
||||||
@BINPATH@/components/TelemetryStartup.js
|
@BINPATH@/components/TelemetryStartup.js
|
||||||
@BINPATH@/components/TelemetryStartup.manifest
|
@BINPATH@/components/TelemetryStartup.manifest
|
||||||
|
@BINPATH@/components/XULStore.js
|
||||||
|
@BINPATH@/components/XULStore.manifest
|
||||||
@BINPATH@/components/messageWakeupService.js
|
@BINPATH@/components/messageWakeupService.js
|
||||||
@BINPATH@/components/messageWakeupService.manifest
|
@BINPATH@/components/messageWakeupService.manifest
|
||||||
@BINPATH@/components/SettingsManager.js
|
@BINPATH@/components/SettingsManager.js
|
||||||
|
@ -114,8 +114,6 @@
|
|||||||
- the heading of the radiobox corresponding to the theme of the developer
|
- the heading of the radiobox corresponding to the theme of the developer
|
||||||
- tools. -->
|
- tools. -->
|
||||||
<!ENTITY options.selectDevToolsTheme.label "Choose DevTools theme:">
|
<!ENTITY options.selectDevToolsTheme.label "Choose DevTools theme:">
|
||||||
<!ENTITY options.darkTheme.label "Dark theme">
|
|
||||||
<!ENTITY options.lightTheme.label "Light theme">
|
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (options.webconsole.label): This is the label for the
|
<!-- LOCALIZATION NOTE (options.webconsole.label): This is the label for the
|
||||||
- heading of the group of Web Console preferences in the options panel. -->
|
- heading of the group of Web Console preferences in the options panel. -->
|
||||||
|
@ -70,3 +70,11 @@ browserConsoleCmd.commandkey=j
|
|||||||
# LOCALIZATION NOTE (pickButton.tooltip)
|
# LOCALIZATION NOTE (pickButton.tooltip)
|
||||||
# This is the tooltip of the pick button in the toolbox toolbar
|
# This is the tooltip of the pick button in the toolbox toolbar
|
||||||
pickButton.tooltip=Pick an element from the page
|
pickButton.tooltip=Pick an element from the page
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (options.darkTheme.label)
|
||||||
|
# Used as a label for dark theme
|
||||||
|
options.darkTheme.label=Dark theme
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (options.lightTheme.label)
|
||||||
|
# Used as a label for light theme
|
||||||
|
options.lightTheme.label=Light theme
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<!ENTITY projectMenu_importHostedApp_label "Open Hosted App…">
|
<!ENTITY projectMenu_importHostedApp_label "Open Hosted App…">
|
||||||
<!ENTITY projectMenu_importHostedApp_accesskey "H">
|
<!ENTITY projectMenu_importHostedApp_accesskey "H">
|
||||||
<!ENTITY projectMenu_selectApp_label "Open App…">
|
<!ENTITY projectMenu_selectApp_label "Open App…">
|
||||||
<!ENTITY projectMenu_selectApp_accessley "S">
|
<!ENTITY projectMenu_selectApp_accesskey "O">
|
||||||
<!ENTITY projectMenu_play_label "Install and Run">
|
<!ENTITY projectMenu_play_label "Install and Run">
|
||||||
<!ENTITY projectMenu_play_accesskey "I">
|
<!ENTITY projectMenu_play_accesskey "I">
|
||||||
<!ENTITY projectMenu_stop_label "Stop App">
|
<!ENTITY projectMenu_stop_label "Stop App">
|
||||||
|
@ -40,6 +40,10 @@ error_cantFetchAddonsJSON=Can't fetch the add-on list: %S
|
|||||||
|
|
||||||
addons_stable=stable
|
addons_stable=stable
|
||||||
addons_unstable=unstable
|
addons_unstable=unstable
|
||||||
|
# LOCALIZATION NOTE (addons_simulator_label): This label is shown as the name of
|
||||||
|
# a given simulator version in the "Manage Simulators" pane. %1$S: Firefox OS
|
||||||
|
# version in the simulator, ex. 1.3. %2$S: Simulator stability label, ex.
|
||||||
|
# "stable" or "unstable".
|
||||||
addons_simulator_label=Firefox OS %1$S Simulator (%2$S)
|
addons_simulator_label=Firefox OS %1$S Simulator (%2$S)
|
||||||
addons_install_button=install
|
addons_install_button=install
|
||||||
addons_uninstall_button=uninstall
|
addons_uninstall_button=uninstall
|
||||||
|
@ -77,6 +77,10 @@ menu[disabled="true"].subviewbutton > .menu-right {
|
|||||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
.subviewbutton > .toolbarbutton-icon {
|
.subviewbutton > .toolbarbutton-icon {
|
||||||
-moz-margin-end: 5px !important;
|
-moz-margin-end: 5px !important;
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,10 @@ menu[disabled="true"].subviewbutton > .menu-right {
|
|||||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
%ifdef WINDOWS_AERO
|
%ifdef WINDOWS_AERO
|
||||||
/* Win8 and beyond. */
|
/* Win8 and beyond. */
|
||||||
@media not all and (-moz-os-version: windows-vista) {
|
@media not all and (-moz-os-version: windows-vista) {
|
||||||
|
@ -61,6 +61,10 @@ leak:gsmsdp_add_default_audio_formats_to_local_sdp
|
|||||||
leak:gsmsdp_add_default_video_formats_to_local_sdp
|
leak:gsmsdp_add_default_video_formats_to_local_sdp
|
||||||
leak:CCAPI_CallInfo_getMediaStreams
|
leak:CCAPI_CallInfo_getMediaStreams
|
||||||
|
|
||||||
|
# Intermittent Mochitest 3 WebRTC leaks, as seen in bug 1055910.
|
||||||
|
leak:sdp_build_attr_ice_attr
|
||||||
|
leak:VcmSIPCCBinding::CandidateReady
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
### Many leaks only affect some test suites. The suite annotations are not checked.
|
### Many leaks only affect some test suites. The suite annotations are not checked.
|
||||||
|
@ -784,6 +784,17 @@ nsCSPParser::directiveName()
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The directive 'reflected-xss' is part of CSP 1.1, see:
|
||||||
|
// http://www.w3.org/TR/2014/WD-CSP11-20140211/#reflected-xss
|
||||||
|
// Currently we are not supporting that directive, hence we log a
|
||||||
|
// warning to the console and ignore the directive including its values.
|
||||||
|
if (CSP_IsDirective(mCurToken, CSP_REFLECTED_XSS)) {
|
||||||
|
const char16_t* params[] = { mCurToken.get() };
|
||||||
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "notSupportingDirective",
|
||||||
|
params, ArrayLength(params));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the directive does not already exist
|
// Make sure the directive does not already exist
|
||||||
// (see http://www.w3.org/TR/CSP11/#parsing)
|
// (see http://www.w3.org/TR/CSP11/#parsing)
|
||||||
if (mPolicy->directiveExists(CSP_DirectiveToEnum(mCurToken))) {
|
if (mPolicy->directiveExists(CSP_DirectiveToEnum(mCurToken))) {
|
||||||
|
@ -70,23 +70,25 @@ enum CSPDirective {
|
|||||||
CSP_CONNECT_SRC,
|
CSP_CONNECT_SRC,
|
||||||
CSP_REPORT_URI,
|
CSP_REPORT_URI,
|
||||||
CSP_FRAME_ANCESTORS,
|
CSP_FRAME_ANCESTORS,
|
||||||
|
CSP_REFLECTED_XSS,
|
||||||
// CSP_LAST_DIRECTIVE_VALUE always needs to be the last element in the enum
|
// CSP_LAST_DIRECTIVE_VALUE always needs to be the last element in the enum
|
||||||
// because we use it to calculate the size for the char* array.
|
// because we use it to calculate the size for the char* array.
|
||||||
CSP_LAST_DIRECTIVE_VALUE
|
CSP_LAST_DIRECTIVE_VALUE
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* CSPStrDirectives[] = {
|
static const char* CSPStrDirectives[] = {
|
||||||
"default-src", // CSP_DEFAULT_SRC = 0
|
"default-src", // CSP_DEFAULT_SRC = 0
|
||||||
"script-src", // CSP_SCRIPT_SRC
|
"script-src", // CSP_SCRIPT_SRC
|
||||||
"object-src", // CSP_OBJECT_SRC
|
"object-src", // CSP_OBJECT_SRC
|
||||||
"style-src", // CSP_STYLE_SRC
|
"style-src", // CSP_STYLE_SRC
|
||||||
"img-src", // CSP_IMG_SRC
|
"img-src", // CSP_IMG_SRC
|
||||||
"media-src", // CSP_MEDIA_SRC
|
"media-src", // CSP_MEDIA_SRC
|
||||||
"frame-src", // CSP_FRAME_SRC
|
"frame-src", // CSP_FRAME_SRC
|
||||||
"font-src", // CSP_FONT_SRC
|
"font-src", // CSP_FONT_SRC
|
||||||
"connect-src", // CSP_CONNECT_SRC
|
"connect-src", // CSP_CONNECT_SRC
|
||||||
"report-uri", // CSP_REPORT_URI
|
"report-uri", // CSP_REPORT_URI
|
||||||
"frame-ancestors" // CSP_FRAME_ANCESTORS
|
"frame-ancestors", // CSP_FRAME_ANCESTORS
|
||||||
|
"reflected-xss" // CSP_REFLECTED_XSS
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const char* CSP_EnumToDirective(enum CSPDirective aDir)
|
inline const char* CSP_EnumToDirective(enum CSPDirective aDir)
|
||||||
|
@ -198,6 +198,7 @@
|
|||||||
#include "mozilla/dom/HTMLInputElement.h"
|
#include "mozilla/dom/HTMLInputElement.h"
|
||||||
#include "mozilla/dom/NodeFilterBinding.h"
|
#include "mozilla/dom/NodeFilterBinding.h"
|
||||||
#include "mozilla/dom/OwningNonNull.h"
|
#include "mozilla/dom/OwningNonNull.h"
|
||||||
|
#include "mozilla/dom/TabChild.h"
|
||||||
#include "mozilla/dom/UndoManager.h"
|
#include "mozilla/dom/UndoManager.h"
|
||||||
#include "mozilla/dom/WebComponentsBinding.h"
|
#include "mozilla/dom/WebComponentsBinding.h"
|
||||||
#include "nsFrame.h"
|
#include "nsFrame.h"
|
||||||
@ -10543,8 +10544,9 @@ nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the document is a direct child of a cross process parent
|
// Returns true if the document is a direct child of a cross process parent
|
||||||
// mozbrowser iframe. This is the case when the document has a null parent,
|
// mozbrowser iframe or TabParent. This is the case when the document has
|
||||||
// and its DocShell reports that it is a browser frame.
|
// a null parent and its DocShell reports that it is a browser frame, or
|
||||||
|
// we can get a TabChild from it.
|
||||||
static bool
|
static bool
|
||||||
HasCrossProcessParent(nsIDocument* aDocument)
|
HasCrossProcessParent(nsIDocument* aDocument)
|
||||||
{
|
{
|
||||||
@ -10562,7 +10564,12 @@ HasCrossProcessParent(nsIDocument* aDocument)
|
|||||||
if (!docShell) {
|
if (!docShell) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return docShell->GetIsBrowserOrApp();
|
TabChild* tabChild(TabChild::GetFrom(docShell));
|
||||||
|
if (!tabChild) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "prenv.h"
|
#include "prenv.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "gfx2DGlue.h"
|
#include "gfx2DGlue.h"
|
||||||
|
#include "nsPrintfCString.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -50,26 +51,32 @@ using namespace mozilla::gfx;
|
|||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||||
#define DECODER_LOG(type, msg, ...) \
|
#define DECODER_LOG(x, ...) \
|
||||||
PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, mDecoder.get(), ##__VA_ARGS__))
|
PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, ("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__))
|
||||||
#define VERBOSE_LOG(msg, ...) \
|
#define VERBOSE_LOG(x, ...) \
|
||||||
PR_BEGIN_MACRO \
|
PR_BEGIN_MACRO \
|
||||||
if (!PR_GetEnv("MOZ_QUIET")) { \
|
if (!PR_GetEnv("MOZ_QUIET")) { \
|
||||||
DECODER_LOG(PR_LOG_DEBUG, msg, ##__VA_ARGS__); \
|
DECODER_LOG(x, ##__VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
PR_END_MACRO
|
PR_END_MACRO
|
||||||
#define SAMPLE_LOG(msg, ...) \
|
#define SAMPLE_LOG(x, ...) \
|
||||||
PR_BEGIN_MACRO \
|
PR_BEGIN_MACRO \
|
||||||
if (PR_GetEnv("MEDIA_LOG_SAMPLES")) { \
|
if (PR_GetEnv("MEDIA_LOG_SAMPLES")) { \
|
||||||
DECODER_LOG(PR_LOG_DEBUG, msg, ##__VA_ARGS__); \
|
DECODER_LOG(x, ##__VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
PR_END_MACRO
|
PR_END_MACRO
|
||||||
#else
|
#else
|
||||||
#define DECODER_LOG(type, msg, ...)
|
#define DECODER_LOG(x, ...)
|
||||||
#define VERBOSE_LOG(msg, ...)
|
#define VERBOSE_LOG(x, ...)
|
||||||
#define SAMPLE_LOG(msg, ...)
|
#define SAMPLE_LOG(x, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Somehow MSVC doesn't correctly delete the comma before ##__VA_ARGS__
|
||||||
|
// when __VA_ARGS__ expands to nothing. This is a workaround for it.
|
||||||
|
#define DECODER_WARN_HELPER(a, b) NS_WARNING b
|
||||||
|
#define DECODER_WARN(x, ...) \
|
||||||
|
DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__).get()))
|
||||||
|
|
||||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||||
// GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime
|
// GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime
|
||||||
// implementation. With unified builds, putting this in headers is not enough.
|
// implementation. With unified builds, putting this in headers is not enough.
|
||||||
@ -600,7 +607,7 @@ MediaDecoderStateMachine::DecodeVideo()
|
|||||||
!HasLowUndecodedData())
|
!HasLowUndecodedData())
|
||||||
{
|
{
|
||||||
skipToNextKeyFrame = true;
|
skipToNextKeyFrame = true;
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Skipping video decode to the next keyframe");
|
DECODER_LOG("Skipping video decode to the next keyframe");
|
||||||
}
|
}
|
||||||
currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime();
|
currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime();
|
||||||
|
|
||||||
@ -923,7 +930,7 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
|
|||||||
std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS);
|
std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS);
|
||||||
mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs,
|
mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs,
|
||||||
mAmpleAudioThresholdUsecs);
|
mAmpleAudioThresholdUsecs);
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
|
DECODER_LOG("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
|
||||||
mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs);
|
mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -983,7 +990,7 @@ MediaDecoderStateMachine::CheckIfSeekComplete()
|
|||||||
if (HasVideo() && !videoSeekComplete) {
|
if (HasVideo() && !videoSeekComplete) {
|
||||||
// We haven't reached the target. Ensure we have requested another sample.
|
// We haven't reached the target. Ensure we have requested another sample.
|
||||||
if (NS_FAILED(EnsureVideoDecodeTaskQueued())) {
|
if (NS_FAILED(EnsureVideoDecodeTaskQueued())) {
|
||||||
NS_WARNING("Failed to request video during seek");
|
DECODER_WARN("Failed to request video during seek");
|
||||||
DecodeError();
|
DecodeError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -992,7 +999,7 @@ MediaDecoderStateMachine::CheckIfSeekComplete()
|
|||||||
if (HasAudio() && !audioSeekComplete) {
|
if (HasAudio() && !audioSeekComplete) {
|
||||||
// We haven't reached the target. Ensure we have requested another sample.
|
// We haven't reached the target. Ensure we have requested another sample.
|
||||||
if (NS_FAILED(EnsureAudioDecodeTaskQueued())) {
|
if (NS_FAILED(EnsureAudioDecodeTaskQueued())) {
|
||||||
NS_WARNING("Failed to request audio during seek");
|
DECODER_WARN("Failed to request audio during seek");
|
||||||
DecodeError();
|
DecodeError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1043,7 +1050,7 @@ MediaDecoderStateMachine::CheckIfDecodeComplete()
|
|||||||
DispatchDecodeTasksIfNeeded();
|
DispatchDecodeTasksIfNeeded();
|
||||||
ScheduleStateMachine();
|
ScheduleStateMachine();
|
||||||
}
|
}
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "CheckIfDecodeComplete %scompleted",
|
DECODER_LOG("CheckIfDecodeComplete %scompleted",
|
||||||
((mState == DECODER_STATE_COMPLETED) ? "" : "NOT "));
|
((mState == DECODER_STATE_COMPLETED) ? "" : "NOT "));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1087,7 +1094,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
|
|||||||
|
|
||||||
void MediaDecoderStateMachine::StopPlayback()
|
void MediaDecoderStateMachine::StopPlayback()
|
||||||
{
|
{
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "StopPlayback()");
|
DECODER_LOG("StopPlayback()");
|
||||||
|
|
||||||
AssertCurrentThreadInMonitor();
|
AssertCurrentThreadInMonitor();
|
||||||
|
|
||||||
@ -1130,13 +1137,13 @@ int64_t MediaDecoderStateMachine::GetCurrentTimeViaMediaStreamSync()
|
|||||||
|
|
||||||
void MediaDecoderStateMachine::StartPlayback()
|
void MediaDecoderStateMachine::StartPlayback()
|
||||||
{
|
{
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "StartPlayback()");
|
DECODER_LOG("StartPlayback()");
|
||||||
|
|
||||||
NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
|
NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
|
||||||
AssertCurrentThreadInMonitor();
|
AssertCurrentThreadInMonitor();
|
||||||
|
|
||||||
if (mDecoder->CheckDecoderCanOffloadAudio()) {
|
if (mDecoder->CheckDecoderCanOffloadAudio()) {
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Offloading playback");
|
DECODER_LOG("Offloading playback");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1145,7 +1152,7 @@ void MediaDecoderStateMachine::StartPlayback()
|
|||||||
|
|
||||||
NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
|
NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
|
||||||
if (NS_FAILED(StartAudioThread())) {
|
if (NS_FAILED(StartAudioThread())) {
|
||||||
NS_WARNING("Failed to create audio thread");
|
DECODER_WARN("Failed to create audio thread");
|
||||||
}
|
}
|
||||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||||
mDecoder->UpdateStreamBlockingForStateMachinePlaying();
|
mDecoder->UpdateStreamBlockingForStateMachinePlaying();
|
||||||
@ -1313,6 +1320,8 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DECODER_LOG("SetDormant=%d", aDormant);
|
||||||
|
|
||||||
if (aDormant) {
|
if (aDormant) {
|
||||||
ScheduleStateMachine();
|
ScheduleStateMachine();
|
||||||
mState = DECODER_STATE_DORMANT;
|
mState = DECODER_STATE_DORMANT;
|
||||||
@ -1335,7 +1344,7 @@ void MediaDecoderStateMachine::Shutdown()
|
|||||||
|
|
||||||
// Change state before issuing shutdown request to threads so those
|
// Change state before issuing shutdown request to threads so those
|
||||||
// threads can start exiting cleanly during the Shutdown call.
|
// threads can start exiting cleanly during the Shutdown call.
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN");
|
DECODER_LOG("Changed state to SHUTDOWN");
|
||||||
mState = DECODER_STATE_SHUTDOWN;
|
mState = DECODER_STATE_SHUTDOWN;
|
||||||
mScheduler->ScheduleAndShutdown();
|
mScheduler->ScheduleAndShutdown();
|
||||||
if (mAudioSink) {
|
if (mAudioSink) {
|
||||||
@ -1377,6 +1386,7 @@ void MediaDecoderStateMachine::StartWaitForResources()
|
|||||||
"Should be on state machine or decode thread.");
|
"Should be on state machine or decode thread.");
|
||||||
AssertCurrentThreadInMonitor();
|
AssertCurrentThreadInMonitor();
|
||||||
mState = DECODER_STATE_WAIT_FOR_RESOURCES;
|
mState = DECODER_STATE_WAIT_FOR_RESOURCES;
|
||||||
|
DECODER_LOG("StartWaitForResources");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
|
void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
|
||||||
@ -1386,6 +1396,7 @@ void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
|
|||||||
mReader->IsWaitingMediaResources()) {
|
mReader->IsWaitingMediaResources()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
DECODER_LOG("NotifyWaitingForResourcesStatusChanged");
|
||||||
// The reader is no longer waiting for resources (say a hardware decoder),
|
// The reader is no longer waiting for resources (say a hardware decoder),
|
||||||
// we can now proceed to decode metadata.
|
// we can now proceed to decode metadata.
|
||||||
mState = DECODER_STATE_DECODING_METADATA;
|
mState = DECODER_STATE_DECODING_METADATA;
|
||||||
@ -1400,7 +1411,7 @@ void MediaDecoderStateMachine::Play()
|
|||||||
// when the state machine notices the decoder's state change to PLAYING.
|
// when the state machine notices the decoder's state change to PLAYING.
|
||||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||||
if (mState == DECODER_STATE_BUFFERING) {
|
if (mState == DECODER_STATE_BUFFERING) {
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from BUFFERING to DECODING");
|
DECODER_LOG("Changed state from BUFFERING to DECODING");
|
||||||
mState = DECODER_STATE_DECODING;
|
mState = DECODER_STATE_DECODING;
|
||||||
mDecodeStartTime = TimeStamp::Now();
|
mDecodeStartTime = TimeStamp::Now();
|
||||||
}
|
}
|
||||||
@ -1461,7 +1472,7 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
|
|||||||
// We need to be able to seek both at a transport level and at a media level
|
// We need to be able to seek both at a transport level and at a media level
|
||||||
// to seek.
|
// to seek.
|
||||||
if (!mDecoder->IsMediaSeekable()) {
|
if (!mDecoder->IsMediaSeekable()) {
|
||||||
NS_WARNING("Seek() function should not be called on a non-seekable state machine");
|
DECODER_WARN("Seek() function should not be called on a non-seekable state machine");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// MediaDecoder::mPlayState should be SEEKING while we seek, and
|
// MediaDecoder::mPlayState should be SEEKING while we seek, and
|
||||||
@ -1481,7 +1492,7 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
|
|||||||
"Can only seek in range [0,duration]");
|
"Can only seek in range [0,duration]");
|
||||||
mSeekTarget = SeekTarget(seekTime, aTarget.mType);
|
mSeekTarget = SeekTarget(seekTime, aTarget.mType);
|
||||||
|
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state to SEEKING (to %lld)", mSeekTarget.mTime);
|
DECODER_LOG("Changed state to SEEKING (to %lld)", mSeekTarget.mTime);
|
||||||
mState = DECODER_STATE_SEEKING;
|
mState = DECODER_STATE_SEEKING;
|
||||||
if (mDecoder->GetDecodedStream()) {
|
if (mDecoder->GetDecodedStream()) {
|
||||||
mDecoder->RecreateDecodedStream(seekTime - mStartTime);
|
mDecoder->RecreateDecodedStream(seekTime - mStartTime);
|
||||||
@ -1503,7 +1514,7 @@ void MediaDecoderStateMachine::StopAudioThread()
|
|||||||
mStopAudioThread = true;
|
mStopAudioThread = true;
|
||||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||||
if (mAudioSink) {
|
if (mAudioSink) {
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Shutdown audio thread");
|
DECODER_LOG("Shutdown audio thread");
|
||||||
mAudioSink->PrepareToShutdown();
|
mAudioSink->PrepareToShutdown();
|
||||||
{
|
{
|
||||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||||
@ -1531,7 +1542,7 @@ MediaDecoderStateMachine::EnqueueDecodeMetadataTask()
|
|||||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeMetadata));
|
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeMetadata));
|
||||||
nsresult rv = mDecodeTaskQueue->Dispatch(task);
|
nsresult rv = mDecodeTaskQueue->Dispatch(task);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
NS_WARNING("Dispatch ReadMetadata task failed.");
|
DECODER_WARN("Dispatch ReadMetadata task failed.");
|
||||||
mDispatchedDecodeMetadataTask = false;
|
mDispatchedDecodeMetadataTask = false;
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
@ -1543,7 +1554,7 @@ MediaDecoderStateMachine::SetReaderIdle()
|
|||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
{
|
{
|
||||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "SetReaderIdle() audioQueue=%lld videoQueue=%lld",
|
DECODER_LOG("SetReaderIdle() audioQueue=%lld videoQueue=%lld",
|
||||||
GetDecodedAudioDuration(),
|
GetDecodedAudioDuration(),
|
||||||
VideoQueue().Duration());
|
VideoQueue().Duration());
|
||||||
}
|
}
|
||||||
@ -1606,7 +1617,7 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
|
|||||||
this, &MediaDecoderStateMachine::SetReaderIdle);
|
this, &MediaDecoderStateMachine::SetReaderIdle);
|
||||||
nsresult rv = mDecodeTaskQueue->Dispatch(event.forget());
|
nsresult rv = mDecodeTaskQueue->Dispatch(event.forget());
|
||||||
if (NS_FAILED(rv) && mState != DECODER_STATE_SHUTDOWN) {
|
if (NS_FAILED(rv) && mState != DECODER_STATE_SHUTDOWN) {
|
||||||
NS_WARNING("Failed to dispatch event to set decoder idle state");
|
DECODER_WARN("Failed to dispatch event to set decoder idle state");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1632,7 +1643,7 @@ MediaDecoderStateMachine::EnqueueDecodeSeekTask()
|
|||||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeSeek));
|
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeSeek));
|
||||||
nsresult rv = mDecodeTaskQueue->Dispatch(task);
|
nsresult rv = mDecodeTaskQueue->Dispatch(task);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
NS_WARNING("Dispatch DecodeSeek task failed.");
|
DECODER_WARN("Dispatch DecodeSeek task failed.");
|
||||||
mCurrentSeekTarget.Reset();
|
mCurrentSeekTarget.Reset();
|
||||||
DecodeError();
|
DecodeError();
|
||||||
}
|
}
|
||||||
@ -1676,7 +1687,7 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
|
|||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
mAudioRequestPending = true;
|
mAudioRequestPending = true;
|
||||||
} else {
|
} else {
|
||||||
NS_WARNING("Failed to dispatch task to decode audio");
|
DECODER_WARN("Failed to dispatch task to decode audio");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1721,7 +1732,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
|
|||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
mVideoRequestPending = true;
|
mVideoRequestPending = true;
|
||||||
} else {
|
} else {
|
||||||
NS_WARNING("Failed to dispatch task to decode video");
|
DECODER_WARN("Failed to dispatch task to decode video");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1746,7 +1757,7 @@ MediaDecoderStateMachine::StartAudioThread()
|
|||||||
mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel());
|
mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel());
|
||||||
nsresult rv = mAudioSink->Init();
|
nsresult rv = mAudioSink->Init();
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because audio sink initialization failed");
|
DECODER_WARN("Changed state to SHUTDOWN because audio sink initialization failed");
|
||||||
mState = DECODER_STATE_SHUTDOWN;
|
mState = DECODER_STATE_SHUTDOWN;
|
||||||
mScheduler->ScheduleAndShutdown();
|
mScheduler->ScheduleAndShutdown();
|
||||||
return rv;
|
return rv;
|
||||||
@ -1825,7 +1836,7 @@ MediaDecoderStateMachine::DecodeError()
|
|||||||
// Change state to shutdown before sending error report to MediaDecoder
|
// Change state to shutdown before sending error report to MediaDecoder
|
||||||
// and the HTMLMediaElement, so that our pipeline can start exiting
|
// and the HTMLMediaElement, so that our pipeline can start exiting
|
||||||
// cleanly during the sync dispatch below.
|
// cleanly during the sync dispatch below.
|
||||||
DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN due to error");
|
DECODER_WARN("Decode error, changed state to SHUTDOWN due to error");
|
||||||
mState = DECODER_STATE_SHUTDOWN;
|
mState = DECODER_STATE_SHUTDOWN;
|
||||||
mScheduler->ScheduleAndShutdown();
|
mScheduler->ScheduleAndShutdown();
|
||||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||||
@ -1852,7 +1863,7 @@ MediaDecoderStateMachine::CallDecodeMetadata()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (NS_FAILED(DecodeMetadata())) {
|
if (NS_FAILED(DecodeMetadata())) {
|
||||||
DECODER_LOG(PR_LOG_WARNING, "Decode metadata failed, shutting down decoder");
|
DECODER_WARN("Decode metadata failed, shutting down decoder");
|
||||||
DecodeError();
|
DecodeError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1861,7 +1872,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||||||
{
|
{
|
||||||
AssertCurrentThreadInMonitor();
|
AssertCurrentThreadInMonitor();
|
||||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Decoding Media Headers");
|
DECODER_LOG("Decoding Media Headers");
|
||||||
if (mState != DECODER_STATE_DECODING_METADATA) {
|
if (mState != DECODER_STATE_DECODING_METADATA) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
@ -1891,6 +1902,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||||||
mInfo = info;
|
mInfo = info;
|
||||||
|
|
||||||
if (NS_FAILED(res) || (!info.HasValidMedia())) {
|
if (NS_FAILED(res) || (!info.HasValidMedia())) {
|
||||||
|
DECODER_WARN("ReadMetadata failed, res=%x HasValidMedia=%d", res, info.HasValidMedia());
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
mDecoder->StartProgressUpdates();
|
mDecoder->StartProgressUpdates();
|
||||||
@ -1930,7 +1942,7 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
|
|||||||
{
|
{
|
||||||
AssertCurrentThreadInMonitor();
|
AssertCurrentThreadInMonitor();
|
||||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Decoding Media Headers");
|
DECODER_LOG("FinishDecodeMetadata");
|
||||||
|
|
||||||
if (mState == DECODER_STATE_SHUTDOWN) {
|
if (mState == DECODER_STATE_SHUTDOWN) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
@ -1946,10 +1958,8 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
|
|||||||
if (startTime == INT64_MAX) {
|
if (startTime == INT64_MAX) {
|
||||||
startTime = 0;
|
startTime = 0;
|
||||||
}
|
}
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "DecodeMetadata first video frame start %lld",
|
DECODER_LOG("DecodeMetadata first video frame start %lld", v ? v->mTime : -1);
|
||||||
v ? v->mTime : -1);
|
DECODER_LOG("DecodeMetadata first audio frame start %lld", a ? a->mTime : -1);
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "DecodeMetadata first audio frame start %lld",
|
|
||||||
a ? a->mTime : -1);
|
|
||||||
SetStartTime(startTime);
|
SetStartTime(startTime);
|
||||||
if (VideoQueue().GetSize()) {
|
if (VideoQueue().GetSize()) {
|
||||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||||
@ -1965,8 +1975,8 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
|
|||||||
MOZ_ASSERT(!(mDecoder->IsMediaSeekable() && mDecoder->IsTransportSeekable()) ||
|
MOZ_ASSERT(!(mDecoder->IsMediaSeekable() && mDecoder->IsTransportSeekable()) ||
|
||||||
GetDuration() != -1,
|
GetDuration() != -1,
|
||||||
"Seekable media should have duration");
|
"Seekable media should have duration");
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Media goes from %lld to %lld (duration %lld) "
|
DECODER_LOG("Media goes from %lld to %lld (duration %lld) "
|
||||||
"transportSeekable=%d, mediaSeekable=%d",
|
"transportSeekable=%d, mediaSeekable=%d",
|
||||||
mStartTime, mEndTime, GetDuration(),
|
mStartTime, mEndTime, GetDuration(),
|
||||||
mDecoder->IsTransportSeekable(), mDecoder->IsMediaSeekable());
|
mDecoder->IsTransportSeekable(), mDecoder->IsMediaSeekable());
|
||||||
|
|
||||||
@ -1986,7 +1996,7 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
|
|||||||
NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
|
NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
|
||||||
|
|
||||||
if (mState == DECODER_STATE_DECODING_METADATA) {
|
if (mState == DECODER_STATE_DECODING_METADATA) {
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from DECODING_METADATA to DECODING");
|
DECODER_LOG("Changed state from DECODING_METADATA to DECODING");
|
||||||
StartDecoding();
|
StartDecoding();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2051,7 +2061,7 @@ void MediaDecoderStateMachine::DecodeSeek()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!currentTimeChanged) {
|
if (!currentTimeChanged) {
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Seek !currentTimeChanged...");
|
DECODER_LOG("Seek !currentTimeChanged...");
|
||||||
mDecodeToSeekTarget = false;
|
mDecodeToSeekTarget = false;
|
||||||
nsresult rv = mDecodeTaskQueue->Dispatch(
|
nsresult rv = mDecodeTaskQueue->Dispatch(
|
||||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted));
|
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SeekCompleted));
|
||||||
@ -2150,14 +2160,14 @@ MediaDecoderStateMachine::SeekCompleted()
|
|||||||
// Seeked to end of media, move to COMPLETED state. Note we don't do
|
// Seeked to end of media, move to COMPLETED state. Note we don't do
|
||||||
// this if we're playing a live stream, since the end of media will advance
|
// this if we're playing a live stream, since the end of media will advance
|
||||||
// once we download more data!
|
// once we download more data!
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from SEEKING (to %lld) to COMPLETED", seekTime);
|
DECODER_LOG("Changed state from SEEKING (to %lld) to COMPLETED", seekTime);
|
||||||
stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStoppedAtEnd);
|
stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStoppedAtEnd);
|
||||||
// Explicitly set our state so we don't decode further, and so
|
// Explicitly set our state so we don't decode further, and so
|
||||||
// we report playback ended to the media element.
|
// we report playback ended to the media element.
|
||||||
mState = DECODER_STATE_COMPLETED;
|
mState = DECODER_STATE_COMPLETED;
|
||||||
DispatchDecodeTasksIfNeeded();
|
DispatchDecodeTasksIfNeeded();
|
||||||
} else {
|
} else {
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from SEEKING (to %lld) to DECODING", seekTime);
|
DECODER_LOG("Changed state from SEEKING (to %lld) to DECODING", seekTime);
|
||||||
stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStopped);
|
stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStopped);
|
||||||
StartDecoding();
|
StartDecoding();
|
||||||
}
|
}
|
||||||
@ -2169,7 +2179,7 @@ MediaDecoderStateMachine::SeekCompleted()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to decode another frame to detect if we're at the end...
|
// Try to decode another frame to detect if we're at the end...
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime);
|
DECODER_LOG("Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime);
|
||||||
|
|
||||||
// Prevent changes in playback position before 'seeked' is fired for we
|
// Prevent changes in playback position before 'seeked' is fired for we
|
||||||
// expect currentTime equals seek target in 'seeked' callback.
|
// expect currentTime equals seek target in 'seeked' callback.
|
||||||
@ -2293,6 +2303,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
|||||||
GetStateMachineThread()->Dispatch(
|
GetStateMachineThread()->Dispatch(
|
||||||
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
|
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
|
||||||
|
|
||||||
|
DECODER_LOG("SHUTDOWN OK");
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2362,14 +2373,14 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
|||||||
!mDecoder->IsDataCachedToEndOfResource() &&
|
!mDecoder->IsDataCachedToEndOfResource() &&
|
||||||
!resource->IsSuspended())
|
!resource->IsSuspended())
|
||||||
{
|
{
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Buffering: wait %ds, timeout in %.3lfs %s",
|
DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
|
||||||
mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
|
mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
|
||||||
(mQuickBuffering ? "(quick exit)" : ""));
|
(mQuickBuffering ? "(quick exit)" : ""));
|
||||||
ScheduleStateMachine(USECS_PER_S);
|
ScheduleStateMachine(USECS_PER_S);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
} else {
|
} else {
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from BUFFERING to DECODING");
|
DECODER_LOG("Changed state from BUFFERING to DECODING");
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
|
DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
|
||||||
StartDecoding();
|
StartDecoding();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2698,8 +2709,7 @@ MediaDecoderStateMachine::DropVideoUpToSeekTarget(VideoData* aSample)
|
|||||||
{
|
{
|
||||||
nsAutoPtr<VideoData> video(aSample);
|
nsAutoPtr<VideoData> video(aSample);
|
||||||
MOZ_ASSERT(video);
|
MOZ_ASSERT(video);
|
||||||
DECODER_LOG(PR_LOG_DEBUG,
|
DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld] dup=%d",
|
||||||
"DropVideoUpToSeekTarget() frame [%lld, %lld] dup=%d",
|
|
||||||
video->mTime, video->GetEndTime(), video->mDuplicate);
|
video->mTime, video->GetEndTime(), video->mDuplicate);
|
||||||
const int64_t target = mCurrentSeekTarget.mTime;
|
const int64_t target = mCurrentSeekTarget.mTime;
|
||||||
|
|
||||||
@ -2722,8 +2732,7 @@ MediaDecoderStateMachine::DropVideoUpToSeekTarget(VideoData* aSample)
|
|||||||
// If the frame end time is less than the seek target, we won't want
|
// If the frame end time is less than the seek target, we won't want
|
||||||
// to display this frame after the seek, so discard it.
|
// to display this frame after the seek, so discard it.
|
||||||
if (target >= video->GetEndTime()) {
|
if (target >= video->GetEndTime()) {
|
||||||
DECODER_LOG(PR_LOG_DEBUG,
|
DECODER_LOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
|
||||||
"DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
|
|
||||||
video->mTime, video->GetEndTime(), target);
|
video->mTime, video->GetEndTime(), target);
|
||||||
mFirstVideoFrameAfterSeek = video;
|
mFirstVideoFrameAfterSeek = video;
|
||||||
} else {
|
} else {
|
||||||
@ -2736,8 +2745,7 @@ MediaDecoderStateMachine::DropVideoUpToSeekTarget(VideoData* aSample)
|
|||||||
}
|
}
|
||||||
mFirstVideoFrameAfterSeek = nullptr;
|
mFirstVideoFrameAfterSeek = nullptr;
|
||||||
|
|
||||||
DECODER_LOG(PR_LOG_DEBUG,
|
DECODER_LOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
|
||||||
"DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
|
|
||||||
video->mTime, video->GetEndTime(), target);
|
video->mTime, video->GetEndTime(), target);
|
||||||
|
|
||||||
VideoQueue().PushFront(video.forget());
|
VideoQueue().PushFront(video.forget());
|
||||||
@ -2774,7 +2782,7 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(AudioData* aSample)
|
|||||||
// abort the audio decode-to-target, the state machine will play
|
// abort the audio decode-to-target, the state machine will play
|
||||||
// silence to cover the gap. Typically this happens in poorly muxed
|
// silence to cover the gap. Typically this happens in poorly muxed
|
||||||
// files.
|
// files.
|
||||||
NS_WARNING("Audio not synced after seek, maybe a poorly muxed file?");
|
DECODER_WARN("Audio not synced after seek, maybe a poorly muxed file?");
|
||||||
AudioQueue().Push(audio.forget());
|
AudioQueue().Push(audio.forget());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -2791,7 +2799,7 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(AudioData* aSample)
|
|||||||
if (framesToPrune > audio->mFrames) {
|
if (framesToPrune > audio->mFrames) {
|
||||||
// We've messed up somehow. Don't try to trim frames, the |frames|
|
// We've messed up somehow. Don't try to trim frames, the |frames|
|
||||||
// variable below will overflow.
|
// variable below will overflow.
|
||||||
NS_WARNING("Can't prune more frames that we have!");
|
DECODER_WARN("Can't prune more frames that we have!");
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune);
|
uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune);
|
||||||
@ -2819,7 +2827,7 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(AudioData* aSample)
|
|||||||
void MediaDecoderStateMachine::SetStartTime(int64_t aStartTimeUsecs)
|
void MediaDecoderStateMachine::SetStartTime(int64_t aStartTimeUsecs)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "SetStartTime(%lld)", aStartTimeUsecs);
|
DECODER_LOG("SetStartTime(%lld)", aStartTimeUsecs);
|
||||||
mStartTime = 0;
|
mStartTime = 0;
|
||||||
if (aStartTimeUsecs != 0) {
|
if (aStartTimeUsecs != 0) {
|
||||||
mStartTime = aStartTimeUsecs;
|
mStartTime = aStartTimeUsecs;
|
||||||
@ -2836,7 +2844,7 @@ void MediaDecoderStateMachine::SetStartTime(int64_t aStartTimeUsecs)
|
|||||||
// first actual audio frame we have, we'll inject silence during playback
|
// first actual audio frame we have, we'll inject silence during playback
|
||||||
// to ensure the audio starts at the correct time.
|
// to ensure the audio starts at the correct time.
|
||||||
mAudioStartTime = mStartTime;
|
mAudioStartTime = mStartTime;
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Set media start time to %lld", mStartTime);
|
DECODER_LOG("Set media start time to %lld", mStartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaDecoderStateMachine::UpdateReadyState() {
|
void MediaDecoderStateMachine::UpdateReadyState() {
|
||||||
@ -2905,11 +2913,11 @@ void MediaDecoderStateMachine::StartBuffering()
|
|||||||
// the element we're buffering or not.
|
// the element we're buffering or not.
|
||||||
UpdateReadyState();
|
UpdateReadyState();
|
||||||
mState = DECODER_STATE_BUFFERING;
|
mState = DECODER_STATE_BUFFERING;
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from DECODING to BUFFERING, decoded for %.3lfs",
|
DECODER_LOG("Changed state from DECODING to BUFFERING, decoded for %.3lfs",
|
||||||
decodeDuration.ToSeconds());
|
decodeDuration.ToSeconds());
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
MediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
MediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
||||||
DECODER_LOG(PR_LOG_DEBUG, "Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
|
DECODER_LOG("Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
|
||||||
stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
|
stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
|
||||||
stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
|
stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
|
||||||
#endif
|
#endif
|
||||||
@ -3080,3 +3088,5 @@ void MediaDecoderStateMachine::OnAudioSinkComplete()
|
|||||||
// avoid redefined macro in unified build
|
// avoid redefined macro in unified build
|
||||||
#undef DECODER_LOG
|
#undef DECODER_LOG
|
||||||
#undef VERBOSE_LOG
|
#undef VERBOSE_LOG
|
||||||
|
#undef DECODER_WARN
|
||||||
|
#undef DECODER_WARN_HELPER
|
||||||
|
@ -570,7 +570,7 @@ MP4Reader::Decode(TrackType aTrack)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.mMonitor.AssertCurrentThreadOwns();
|
data.mMonitor.AssertCurrentThreadOwns();
|
||||||
bool rv = !(data.mEOS || data.mError);
|
bool rv = !(data.mDrainComplete || data.mError);
|
||||||
data.mMonitor.Unlock();
|
data.mMonitor.Unlock();
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -49,19 +49,19 @@ GMPChild::~GMPChild()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
GetPluginBinaryPath(const std::string& aPluginPath,
|
GetPluginBinaryFile(const std::string& aPluginPath,
|
||||||
nsCString &aPluginBinaryPath)
|
nsCOMPtr<nsIFile>& aLibFile)
|
||||||
{
|
{
|
||||||
nsDependentCString pluginPath(aPluginPath.c_str());
|
nsDependentCString pluginPath(aPluginPath.c_str());
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> libFile;
|
nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(pluginPath),
|
||||||
nsresult rv = NS_NewNativeLocalFile(pluginPath, true, getter_AddRefs(libFile));
|
true, getter_AddRefs(aLibFile));
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoString leafName;
|
nsAutoString leafName;
|
||||||
if (NS_FAILED(libFile->GetLeafName(leafName))) {
|
if (NS_FAILED(aLibFile->GetLeafName(leafName))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
nsAutoString baseName(Substring(leafName, 4, leafName.Length() - 1));
|
nsAutoString baseName(Substring(leafName, 4, leafName.Length() - 1));
|
||||||
@ -75,13 +75,24 @@ GetPluginBinaryPath(const std::string& aPluginPath,
|
|||||||
#else
|
#else
|
||||||
#error not defined
|
#error not defined
|
||||||
#endif
|
#endif
|
||||||
libFile->AppendRelativePath(binaryName);
|
aLibFile->AppendRelativePath(binaryName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
|
||||||
|
static bool
|
||||||
|
GetPluginBinaryPath(const std::string& aPluginPath,
|
||||||
|
nsCString &aPluginBinaryPath)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIFile> libFile;
|
||||||
|
if (!GetPluginBinaryFile(aPluginPath, libFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
libFile->GetNativePath(aPluginBinaryPath);
|
libFile->GetNativePath(aPluginBinaryPath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
|
|
||||||
void
|
void
|
||||||
GMPChild::OnChannelConnected(int32_t aPid)
|
GMPChild::OnChannelConnected(int32_t aPid)
|
||||||
{
|
{
|
||||||
@ -147,23 +158,29 @@ GMPChild::Init(const std::string& aPluginPath,
|
|||||||
bool
|
bool
|
||||||
GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
|
GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
|
||||||
{
|
{
|
||||||
nsAutoCString nativePath;
|
|
||||||
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
|
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
|
||||||
|
nsAutoCString nativePath;
|
||||||
nativePath.Assign(mPluginBinaryPath);
|
nativePath.Assign(mPluginBinaryPath);
|
||||||
|
|
||||||
|
mLib = PR_LoadLibrary(nativePath.get());
|
||||||
#else
|
#else
|
||||||
if (!GetPluginBinaryPath(aPluginPath, nativePath)) {
|
nsCOMPtr<nsIFile> libFile;
|
||||||
|
if (!GetPluginBinaryFile(aPluginPath, libFile)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
||||||
|
nsAutoCString nativePath;
|
||||||
|
libFile->GetNativePath(nativePath);
|
||||||
|
|
||||||
// Enable sandboxing here -- we know the plugin file's path, but
|
// Enable sandboxing here -- we know the plugin file's path, but
|
||||||
// this process's execution hasn't been affected by its content yet.
|
// this process's execution hasn't been affected by its content yet.
|
||||||
MOZ_ASSERT(mozilla::CanSandboxMediaPlugin());
|
MOZ_ASSERT(mozilla::CanSandboxMediaPlugin());
|
||||||
mozilla::SetMediaPluginSandbox(nativePath.get());
|
mozilla::SetMediaPluginSandbox(nativePath.get());
|
||||||
#endif
|
#endif // XP_LINUX && MOZ_GMP_SANDBOX
|
||||||
|
|
||||||
|
libFile->Load(&mLib);
|
||||||
|
#endif // XP_MACOSX && MOZ_GMP_SANDBOX
|
||||||
|
|
||||||
mLib = PR_LoadLibrary(nativePath.get());
|
|
||||||
if (!mLib) {
|
if (!mLib) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -119,14 +119,14 @@ GMPParent::LoadProcess()
|
|||||||
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
||||||
MOZ_ASSERT(mState == GMPStateNotLoaded);
|
MOZ_ASSERT(mState == GMPStateNotLoaded);
|
||||||
|
|
||||||
nsAutoCString path;
|
nsAutoString path;
|
||||||
if (NS_FAILED(mDirectory->GetNativePath(path))) {
|
if (NS_FAILED(mDirectory->GetPath(path))) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, path.get()));
|
LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, path.get()));
|
||||||
|
|
||||||
if (!mProcess) {
|
if (!mProcess) {
|
||||||
mProcess = new GMPProcessParent(path.get());
|
mProcess = new GMPProcessParent(NS_ConvertUTF16toUTF8(path).get());
|
||||||
if (!mProcess->Launch(30 * 1000)) {
|
if (!mProcess->Launch(30 * 1000)) {
|
||||||
mProcess->Delete();
|
mProcess->Delete();
|
||||||
mProcess = nullptr;
|
mProcess = nullptr;
|
||||||
|
@ -29,6 +29,8 @@ struct MediaCodec;
|
|||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
class MediaTaskQueue;
|
||||||
|
|
||||||
class MediaCodecReader : public MediaOmxCommonReader
|
class MediaCodecReader : public MediaOmxCommonReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -53,17 +55,15 @@ public:
|
|||||||
// irreversible, whereas ReleaseMediaResources() is reversible.
|
// irreversible, whereas ReleaseMediaResources() is reversible.
|
||||||
virtual void Shutdown();
|
virtual void Shutdown();
|
||||||
|
|
||||||
// Decodes an unspecified amount of audio data, enqueuing the audio data
|
// Flush the MediaTaskQueue, flush MediaCodec and raise the mDiscontinuity.
|
||||||
// in mAudioQueue. Returns true when there's more audio to decode,
|
virtual nsresult ResetDecode() MOZ_OVERRIDE;
|
||||||
// false if the audio is finished, end of file has been reached,
|
|
||||||
// or an un-recoverable read error has occured.
|
|
||||||
virtual bool DecodeAudioData();
|
|
||||||
|
|
||||||
// Reads and decodes one video frame. Packets with a timestamp less
|
// Disptach a DecodeVideoFrameTask to decode video data.
|
||||||
// than aTimeThreshold will be decoded (unless they're not keyframes
|
virtual void RequestVideoData(bool aSkipToNextKeyframe,
|
||||||
// and aKeyframeSkip is true), but will not be added to the queue.
|
int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
|
||||||
int64_t aTimeThreshold);
|
// Disptach a DecodeAduioDataTask to decode video data.
|
||||||
|
virtual void RequestAudioData() MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual bool HasAudio();
|
virtual bool HasAudio();
|
||||||
virtual bool HasVideo();
|
virtual bool HasVideo();
|
||||||
@ -90,7 +90,8 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
struct TrackInputCopier
|
struct TrackInputCopier
|
||||||
{
|
{
|
||||||
virtual bool Copy(android::MediaBuffer* aSourceBuffer, android::sp<android::ABuffer> aCodecBuffer);
|
virtual bool Copy(android::MediaBuffer* aSourceBuffer,
|
||||||
|
android::sp<android::ABuffer> aCodecBuffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Track
|
struct Track
|
||||||
@ -111,14 +112,20 @@ protected:
|
|||||||
|
|
||||||
// playback parameters
|
// playback parameters
|
||||||
CheckedUint32 mInputIndex;
|
CheckedUint32 mInputIndex;
|
||||||
|
// mDiscontinuity, mFlushed, mInputEndOfStream, mInputEndOfStream,
|
||||||
|
// mSeekTimeUs don't be protected by a lock because the
|
||||||
|
// mTaskQueue->Flush() will flush all tasks.
|
||||||
bool mInputEndOfStream;
|
bool mInputEndOfStream;
|
||||||
|
bool mOutputEndOfStream;
|
||||||
int64_t mSeekTimeUs;
|
int64_t mSeekTimeUs;
|
||||||
bool mFlushed; // meaningless when mSeekTimeUs is invalid.
|
bool mFlushed; // meaningless when mSeekTimeUs is invalid.
|
||||||
|
bool mDiscontinuity;
|
||||||
|
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Receive a message from MessageHandler.
|
// Receive a message from MessageHandler.
|
||||||
// Called on MediaCodecReader::mLooper thread.
|
// Called on MediaCodecReader::mLooper thread.
|
||||||
void onMessageReceived(const android::sp<android::AMessage> &aMessage);
|
void onMessageReceived(const android::sp<android::AMessage>& aMessage);
|
||||||
|
|
||||||
// Receive a notify from ResourceListener.
|
// Receive a notify from ResourceListener.
|
||||||
// Called on Binder thread.
|
// Called on Binder thread.
|
||||||
@ -131,16 +138,16 @@ private:
|
|||||||
class MessageHandler : public android::AHandler
|
class MessageHandler : public android::AHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MessageHandler(MediaCodecReader *aReader);
|
MessageHandler(MediaCodecReader* aReader);
|
||||||
~MessageHandler();
|
~MessageHandler();
|
||||||
|
|
||||||
virtual void onMessageReceived(const android::sp<android::AMessage> &aMessage);
|
virtual void onMessageReceived(const android::sp<android::AMessage>& aMessage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Forbidden
|
// Forbidden
|
||||||
MessageHandler() MOZ_DELETE;
|
MessageHandler() MOZ_DELETE;
|
||||||
MessageHandler(const MessageHandler &rhs) MOZ_DELETE;
|
MessageHandler(const MessageHandler& rhs) MOZ_DELETE;
|
||||||
const MessageHandler &operator=(const MessageHandler &rhs) MOZ_DELETE;
|
const MessageHandler& operator=(const MessageHandler& rhs) MOZ_DELETE;
|
||||||
|
|
||||||
MediaCodecReader *mReader;
|
MediaCodecReader *mReader;
|
||||||
};
|
};
|
||||||
@ -151,7 +158,7 @@ private:
|
|||||||
class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
|
class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VideoResourceListener(MediaCodecReader *aReader);
|
VideoResourceListener(MediaCodecReader* aReader);
|
||||||
~VideoResourceListener();
|
~VideoResourceListener();
|
||||||
|
|
||||||
virtual void codecReserved();
|
virtual void codecReserved();
|
||||||
@ -160,16 +167,17 @@ private:
|
|||||||
private:
|
private:
|
||||||
// Forbidden
|
// Forbidden
|
||||||
VideoResourceListener() MOZ_DELETE;
|
VideoResourceListener() MOZ_DELETE;
|
||||||
VideoResourceListener(const VideoResourceListener &rhs) MOZ_DELETE;
|
VideoResourceListener(const VideoResourceListener& rhs) MOZ_DELETE;
|
||||||
const VideoResourceListener &operator=(const VideoResourceListener &rhs) MOZ_DELETE;
|
const VideoResourceListener& operator=(const VideoResourceListener& rhs) MOZ_DELETE;
|
||||||
|
|
||||||
MediaCodecReader *mReader;
|
MediaCodecReader* mReader;
|
||||||
};
|
};
|
||||||
friend class VideoResourceListener;
|
friend class VideoResourceListener;
|
||||||
|
|
||||||
class VorbisInputCopier : public TrackInputCopier
|
class VorbisInputCopier : public TrackInputCopier
|
||||||
{
|
{
|
||||||
virtual bool Copy(android::MediaBuffer* aSourceBuffer, android::sp<android::ABuffer> aCodecBuffer);
|
virtual bool Copy(android::MediaBuffer* aSourceBuffer,
|
||||||
|
android::sp<android::ABuffer> aCodecBuffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudioTrack : public Track
|
struct AudioTrack : public Track
|
||||||
@ -206,7 +214,7 @@ private:
|
|||||||
|
|
||||||
// Forbidden
|
// Forbidden
|
||||||
MediaCodecReader() MOZ_DELETE;
|
MediaCodecReader() MOZ_DELETE;
|
||||||
const MediaCodecReader &operator=(const MediaCodecReader &rhs) MOZ_DELETE;
|
const MediaCodecReader& operator=(const MediaCodecReader& rhs) MOZ_DELETE;
|
||||||
|
|
||||||
bool ReallocateResources();
|
bool ReallocateResources();
|
||||||
void ReleaseCriticalResources();
|
void ReleaseCriticalResources();
|
||||||
@ -222,27 +230,45 @@ private:
|
|||||||
void DestroyMediaSources();
|
void DestroyMediaSources();
|
||||||
|
|
||||||
bool CreateMediaCodecs();
|
bool CreateMediaCodecs();
|
||||||
static bool CreateMediaCodec(android::sp<android::ALooper> &aLooper,
|
static bool CreateMediaCodec(android::sp<android::ALooper>& aLooper,
|
||||||
Track &aTrack,
|
Track& aTrack,
|
||||||
bool aAsync,
|
bool aAsync,
|
||||||
android::wp<android::MediaCodecProxy::CodecResourceListener> aListener);
|
android::wp<android::MediaCodecProxy::CodecResourceListener> aListener);
|
||||||
static bool ConfigureMediaCodec(Track &aTrack);
|
static bool ConfigureMediaCodec(Track& aTrack);
|
||||||
void DestroyMediaCodecs();
|
void DestroyMediaCodecs();
|
||||||
static void DestroyMediaCodecs(Track &aTrack);
|
static void DestroyMediaCodecs(Track& aTrack);
|
||||||
|
|
||||||
|
bool CreateTaskQueues();
|
||||||
|
void ShutdownTaskQueues();
|
||||||
|
bool DecodeVideoFrameTask(int64_t aTimeThreshold);
|
||||||
|
bool DecodeVideoFrameSync(int64_t aTimeThreshold);
|
||||||
|
bool DecodeAudioDataTask();
|
||||||
|
bool DecodeAudioDataSync();
|
||||||
|
void DispatchVideoTask(int64_t aTimeThreshold);
|
||||||
|
void DispatchAudioTask();
|
||||||
|
inline bool CheckVideoResources() {
|
||||||
|
return (HasVideo() && mVideoTrack.mSource != nullptr &&
|
||||||
|
mVideoTrack.mTaskQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CheckAudioResources() {
|
||||||
|
return (HasAudio() && mAudioTrack.mSource != nullptr &&
|
||||||
|
mAudioTrack.mTaskQueue);
|
||||||
|
}
|
||||||
|
|
||||||
bool UpdateDuration();
|
bool UpdateDuration();
|
||||||
bool UpdateAudioInfo();
|
bool UpdateAudioInfo();
|
||||||
bool UpdateVideoInfo();
|
bool UpdateVideoInfo();
|
||||||
|
|
||||||
static android::status_t FlushCodecData(Track &aTrack);
|
static android::status_t FlushCodecData(Track& aTrack);
|
||||||
static android::status_t FillCodecInputData(Track &aTrack);
|
static android::status_t FillCodecInputData(Track& aTrack);
|
||||||
static android::status_t GetCodecOutputData(Track &aTrack,
|
static android::status_t GetCodecOutputData(Track& aTrack,
|
||||||
CodecBufferInfo &aBuffer,
|
CodecBufferInfo& aBuffer,
|
||||||
int64_t aThreshold,
|
int64_t aThreshold,
|
||||||
const TimeStamp &aTimeout);
|
const TimeStamp& aTimeout);
|
||||||
static bool EnsureCodecFormatParsed(Track &aTrack);
|
static bool EnsureCodecFormatParsed(Track& aTrack);
|
||||||
|
|
||||||
uint8_t *GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
|
uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
|
||||||
void ClearColorConverterBuffer();
|
void ClearColorConverterBuffer();
|
||||||
|
|
||||||
android::sp<MessageHandler> mHandler;
|
android::sp<MessageHandler> mHandler;
|
||||||
|
@ -33,9 +33,6 @@
|
|||||||
#include "nsViewManager.h"
|
#include "nsViewManager.h"
|
||||||
#include "nsIContentViewer.h"
|
#include "nsIContentViewer.h"
|
||||||
#include "nsIDOMXULElement.h"
|
#include "nsIDOMXULElement.h"
|
||||||
#include "nsIRDFNode.h"
|
|
||||||
#include "nsIRDFRemoteDataSource.h"
|
|
||||||
#include "nsIRDFService.h"
|
|
||||||
#include "nsIStreamListener.h"
|
#include "nsIStreamListener.h"
|
||||||
#include "nsITimer.h"
|
#include "nsITimer.h"
|
||||||
#include "nsDocShell.h"
|
#include "nsDocShell.h"
|
||||||
@ -44,11 +41,10 @@
|
|||||||
#include "nsXULContentSink.h"
|
#include "nsXULContentSink.h"
|
||||||
#include "nsXULContentUtils.h"
|
#include "nsXULContentUtils.h"
|
||||||
#include "nsIXULOverlayProvider.h"
|
#include "nsIXULOverlayProvider.h"
|
||||||
|
#include "nsIStringEnumerator.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsParserCIID.h"
|
#include "nsParserCIID.h"
|
||||||
#include "nsPIBoxObject.h"
|
#include "nsPIBoxObject.h"
|
||||||
#include "nsRDFCID.h"
|
|
||||||
#include "nsILocalStore.h"
|
|
||||||
#include "nsXPIDLString.h"
|
#include "nsXPIDLString.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "nsPIWindowRoot.h"
|
#include "nsPIWindowRoot.h"
|
||||||
@ -137,11 +133,6 @@ const uint32_t kMaxAttributeLength = 4096;
|
|||||||
|
|
||||||
int32_t XULDocument::gRefCnt = 0;
|
int32_t XULDocument::gRefCnt = 0;
|
||||||
|
|
||||||
nsIRDFService* XULDocument::gRDFService;
|
|
||||||
nsIRDFResource* XULDocument::kNC_persist;
|
|
||||||
nsIRDFResource* XULDocument::kNC_attribute;
|
|
||||||
nsIRDFResource* XULDocument::kNC_value;
|
|
||||||
|
|
||||||
PRLogModuleInfo* XULDocument::gXULLog;
|
PRLogModuleInfo* XULDocument::gXULLog;
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
@ -230,26 +221,11 @@ XULDocument::~XULDocument()
|
|||||||
PL_DHashTableDestroy(mBroadcasterMap);
|
PL_DHashTableDestroy(mBroadcasterMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mLocalStore) {
|
|
||||||
nsCOMPtr<nsIRDFRemoteDataSource> remote =
|
|
||||||
do_QueryInterface(mLocalStore);
|
|
||||||
if (remote)
|
|
||||||
remote->Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete mTemplateBuilderTable;
|
delete mTemplateBuilderTable;
|
||||||
|
|
||||||
Preferences::UnregisterCallback(XULDocument::DirectionChanged,
|
Preferences::UnregisterCallback(XULDocument::DirectionChanged,
|
||||||
"intl.uidirection.", this);
|
"intl.uidirection.", this);
|
||||||
|
|
||||||
if (--gRefCnt == 0) {
|
|
||||||
NS_IF_RELEASE(gRDFService);
|
|
||||||
|
|
||||||
NS_IF_RELEASE(kNC_persist);
|
|
||||||
NS_IF_RELEASE(kNC_attribute);
|
|
||||||
NS_IF_RELEASE(kNC_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mOffThreadCompileStringBuf) {
|
if (mOffThreadCompileStringBuf) {
|
||||||
js_free(mOffThreadCompileStringBuf);
|
js_free(mOffThreadCompileStringBuf);
|
||||||
}
|
}
|
||||||
@ -349,6 +325,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
|
|||||||
tmp->mTemplateBuilderTable = nullptr;
|
tmp->mTemplateBuilderTable = nullptr;
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore)
|
||||||
//XXX We should probably unlink all the objects we traverse.
|
//XXX We should probably unlink all the objects we traverse.
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
|
|
||||||
@ -1331,10 +1308,7 @@ XULDocument::Persist(const nsAString& aID,
|
|||||||
nameSpaceID = kNameSpaceID_None;
|
nameSpaceID = kNameSpaceID_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = Persist(element, nameSpaceID, tag);
|
return Persist(element, nameSpaceID, tag);
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -1345,102 +1319,39 @@ XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID,
|
|||||||
if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
|
if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
|
||||||
// First make sure we _have_ a local store to stuff the persisted
|
if (!mLocalStore) {
|
||||||
// information into. (We might not have one if profile information
|
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
|
||||||
// hasn't been loaded yet...)
|
if (NS_WARN_IF(!mLocalStore)) {
|
||||||
if (!mLocalStore)
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
return NS_OK;
|
}
|
||||||
|
|
||||||
nsresult rv;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFResource> element;
|
|
||||||
rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element));
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
// No ID, so nothing to persist.
|
|
||||||
if (! element)
|
|
||||||
return NS_OK;
|
|
||||||
|
|
||||||
// Ick. Construct a property from the attribute. Punt on
|
|
||||||
// namespaces for now.
|
|
||||||
// Don't bother with unreasonable attributes. We clamp long values,
|
|
||||||
// but truncating attribute names turns it into a different attribute
|
|
||||||
// so there's no point in persisting anything at all
|
|
||||||
nsAtomCString attrstr(aAttribute);
|
|
||||||
if (attrstr.Length() > kMaxAttrNameLength) {
|
|
||||||
NS_WARNING("Can't persist, Attribute name too long");
|
|
||||||
return NS_ERROR_ILLEGAL_VALUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFResource> attr;
|
nsAutoString id;
|
||||||
rv = gRDFService->GetResource(attrstr,
|
|
||||||
getter_AddRefs(attr));
|
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
|
||||||
if (NS_FAILED(rv)) return rv;
|
nsAtomString attrstr(aAttribute);
|
||||||
|
|
||||||
// Turn the value into a literal
|
|
||||||
nsAutoString valuestr;
|
nsAutoString valuestr;
|
||||||
aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
|
aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
|
||||||
|
|
||||||
// prevent over-long attributes that choke the parser (bug 319846)
|
nsAutoCString utf8uri;
|
||||||
// (can't simply Truncate without testing, it's implemented
|
nsresult rv = mDocumentURI->GetSpec(utf8uri);
|
||||||
// using SetLength and will grow a short string)
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
if (valuestr.Length() > kMaxAttributeLength) {
|
return rv;
|
||||||
NS_WARNING("Truncating persisted attribute value");
|
}
|
||||||
valuestr.Truncate(kMaxAttributeLength);
|
NS_ConvertUTF8toUTF16 uri(utf8uri);
|
||||||
|
|
||||||
|
bool hasAttr;
|
||||||
|
rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if there was an old value...
|
if (hasAttr && valuestr.IsEmpty()) {
|
||||||
nsCOMPtr<nsIRDFNode> oldvalue;
|
return mLocalStore->RemoveValue(uri, id, attrstr);
|
||||||
rv = mLocalStore->GetTarget(element, attr, true, getter_AddRefs(oldvalue));
|
} else {
|
||||||
if (NS_FAILED(rv)) return rv;
|
return mLocalStore->SetValue(uri, id, attrstr, valuestr);
|
||||||
|
|
||||||
if (oldvalue && valuestr.IsEmpty()) {
|
|
||||||
// ...there was an oldvalue, and they've removed it. XXXThis
|
|
||||||
// handling isn't quite right...
|
|
||||||
rv = mLocalStore->Unassert(element, attr, oldvalue);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Now either 'change' or 'assert' based on whether there was
|
|
||||||
// an old value.
|
|
||||||
nsCOMPtr<nsIRDFLiteral> newvalue;
|
|
||||||
rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue));
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
if (oldvalue) {
|
|
||||||
if (oldvalue != newvalue)
|
|
||||||
rv = mLocalStore->Change(element, attr, oldvalue, newvalue);
|
|
||||||
else
|
|
||||||
rv = NS_OK;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rv = mLocalStore->Assert(element, attr, newvalue, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
// Add it to the persisted set for this document (if it's not
|
|
||||||
// there already).
|
|
||||||
{
|
|
||||||
nsAutoCString docurl;
|
|
||||||
rv = mDocumentURI->GetSpec(docurl);
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFResource> doc;
|
|
||||||
rv = gRDFService->GetResource(docurl, getter_AddRefs(doc));
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
bool hasAssertion;
|
|
||||||
rv = mLocalStore->HasAssertion(doc, kNC_persist, element, true, &hasAssertion);
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
if (! hasAssertion) {
|
|
||||||
rv = mLocalStore->Assert(doc, kNC_persist, element, true);
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1993,25 +1904,7 @@ XULDocument::Init()
|
|||||||
mCommandDispatcher = new nsXULCommandDispatcher(this);
|
mCommandDispatcher = new nsXULCommandDispatcher(this);
|
||||||
NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
// this _could_ fail; e.g., if we've tried to grab the local store
|
|
||||||
// before profiles have initialized. If so, no big deal; nothing
|
|
||||||
// will persist.
|
|
||||||
mLocalStore = do_GetService(NS_LOCALSTORE_CONTRACTID);
|
|
||||||
|
|
||||||
if (gRefCnt++ == 0) {
|
if (gRefCnt++ == 0) {
|
||||||
// Keep the RDF service cached in a member variable to make using
|
|
||||||
// it a bit less painful
|
|
||||||
rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService);
|
|
||||||
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service");
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"),
|
|
||||||
&kNC_persist);
|
|
||||||
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"),
|
|
||||||
&kNC_attribute);
|
|
||||||
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"),
|
|
||||||
&kNC_value);
|
|
||||||
|
|
||||||
// ensure that the XUL prototype cache is instantiated successfully,
|
// ensure that the XUL prototype cache is instantiated successfully,
|
||||||
// so that we can use nsXULPrototypeCache::GetInstance() without
|
// so that we can use nsXULPrototypeCache::GetInstance() without
|
||||||
// null-checks in the rest of the class.
|
// null-checks in the rest of the class.
|
||||||
@ -2175,8 +2068,12 @@ XULDocument::ApplyPersistentAttributes()
|
|||||||
|
|
||||||
// Add all of the 'persisted' attributes into the content
|
// Add all of the 'persisted' attributes into the content
|
||||||
// model.
|
// model.
|
||||||
if (!mLocalStore)
|
if (!mLocalStore) {
|
||||||
return NS_OK;
|
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
|
||||||
|
if (NS_WARN_IF(!mLocalStore)) {
|
||||||
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mApplyingPersistedAttrs = true;
|
mApplyingPersistedAttrs = true;
|
||||||
ApplyPersistentAttributesInternal();
|
ApplyPersistentAttributesInternal();
|
||||||
@ -2191,56 +2088,49 @@ XULDocument::ApplyPersistentAttributes()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
XULDocument::ApplyPersistentAttributesInternal()
|
XULDocument::ApplyPersistentAttributesInternal()
|
||||||
{
|
{
|
||||||
nsCOMArray<nsIContent> elements;
|
nsCOMArray<nsIContent> elements;
|
||||||
|
|
||||||
nsAutoCString docurl;
|
nsAutoCString utf8uri;
|
||||||
mDocumentURI->GetSpec(docurl);
|
nsresult rv = mDocumentURI->GetSpec(utf8uri);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF8toUTF16 uri(utf8uri);
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFResource> doc;
|
// Get a list of element IDs for which persisted values are available
|
||||||
gRDFService->GetResource(docurl, getter_AddRefs(doc));
|
nsCOMPtr<nsIStringEnumerator> ids;
|
||||||
|
rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
|
||||||
nsCOMPtr<nsISimpleEnumerator> persisted;
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
mLocalStore->GetTargets(doc, kNC_persist, true, getter_AddRefs(persisted));
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
bool hasmore = false;
|
bool hasmore = false;
|
||||||
persisted->HasMoreElements(&hasmore);
|
ids->HasMore(&hasmore);
|
||||||
if (! hasmore)
|
if (!hasmore) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsISupports> isupports;
|
nsAutoString id;
|
||||||
persisted->GetNext(getter_AddRefs(isupports));
|
ids->GetNext(id);
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
|
if (mRestrictPersistence && !mPersistenceIds.Contains(id)) {
|
||||||
if (! resource) {
|
|
||||||
NS_WARNING("expected element to be a resource");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *uri;
|
|
||||||
resource->GetValueConst(&uri);
|
|
||||||
if (! uri)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
nsAutoString id;
|
|
||||||
nsXULContentUtils::MakeElementID(this, nsDependentCString(uri), id);
|
|
||||||
|
|
||||||
if (id.IsEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mRestrictPersistence && !mPersistenceIds.Contains(id))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// This will clear the array if there are no elements.
|
// This will clear the array if there are no elements.
|
||||||
GetElementsForID(id, elements);
|
GetElementsForID(id, elements);
|
||||||
|
if (!elements.Count()) {
|
||||||
if (!elements.Count())
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ApplyPersistentAttributesToElements(resource, elements);
|
rv = ApplyPersistentAttributesToElements(id, elements);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -2248,71 +2138,53 @@ XULDocument::ApplyPersistentAttributesInternal()
|
|||||||
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
XULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
|
XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
|
||||||
nsCOMArray<nsIContent>& aElements)
|
nsCOMArray<nsIContent>& aElements)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsAutoCString utf8uri;
|
||||||
|
nsresult rv = mDocumentURI->GetSpec(utf8uri);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF8toUTF16 uri(utf8uri);
|
||||||
|
|
||||||
nsCOMPtr<nsISimpleEnumerator> attrs;
|
// Get a list of attributes for which persisted values are available
|
||||||
rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs));
|
nsCOMPtr<nsIStringEnumerator> attrs;
|
||||||
if (NS_FAILED(rv)) return rv;
|
rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
bool hasmore;
|
bool hasmore = PR_FALSE;
|
||||||
rv = attrs->HasMoreElements(&hasmore);
|
attrs->HasMore(&hasmore);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (!hasmore) {
|
||||||
|
|
||||||
if (! hasmore)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
nsCOMPtr<nsISupports> isupports;
|
|
||||||
rv = attrs->GetNext(getter_AddRefs(isupports));
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
|
|
||||||
if (! property) {
|
|
||||||
NS_WARNING("expected a resource");
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* attrname;
|
nsAutoString attrstr;
|
||||||
rv = property->GetValueConst(&attrname);
|
attrs->GetNext(attrstr);
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname);
|
nsAutoString value;
|
||||||
if (! attr)
|
rv = mLocalStore->GetValue(uri, aID, attrstr, value);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIAtom> attr = do_GetAtom(attrstr);
|
||||||
|
if (NS_WARN_IF(!attr)) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
// XXX could hang namespace off here, as well...
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFNode> node;
|
|
||||||
rv = mLocalStore->GetTarget(aResource, property, true,
|
|
||||||
getter_AddRefs(node));
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node);
|
|
||||||
if (! literal) {
|
|
||||||
NS_WARNING("expected a literal");
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char16_t* value;
|
|
||||||
rv = literal->GetValueConst(&value);
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
nsDependentString wrapper(value);
|
|
||||||
|
|
||||||
uint32_t cnt = aElements.Count();
|
uint32_t cnt = aElements.Count();
|
||||||
|
|
||||||
for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
|
for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
|
||||||
nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i);
|
nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i);
|
||||||
if (!element)
|
if (!element) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
rv = element->SetAttr(/* XXX */ kNameSpaceID_None,
|
rv = element->SetAttr(kNameSpaceID_None, attr, value, PR_TRUE);
|
||||||
attr,
|
|
||||||
wrapper,
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "nsScriptLoader.h"
|
#include "nsScriptLoader.h"
|
||||||
#include "nsIStreamListener.h"
|
#include "nsIStreamListener.h"
|
||||||
#include "nsICSSLoaderObserver.h"
|
#include "nsICSSLoaderObserver.h"
|
||||||
|
#include "nsIXULStore.h"
|
||||||
|
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
|
||||||
@ -259,7 +260,7 @@ protected:
|
|||||||
|
|
||||||
nsresult ApplyPersistentAttributes();
|
nsresult ApplyPersistentAttributes();
|
||||||
nsresult ApplyPersistentAttributesInternal();
|
nsresult ApplyPersistentAttributesInternal();
|
||||||
nsresult ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
|
nsresult ApplyPersistentAttributesToElements(const nsAString &aID,
|
||||||
nsCOMArray<nsIContent>& aElements);
|
nsCOMArray<nsIContent>& aElements);
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -314,10 +315,10 @@ protected:
|
|||||||
// Tracks elements with a 'ref' attribute, or an 'id' attribute where
|
// Tracks elements with a 'ref' attribute, or an 'id' attribute where
|
||||||
// the element's namespace has no registered ID attribute name.
|
// the element's namespace has no registered ID attribute name.
|
||||||
nsTHashtable<nsRefMapEntry> mRefMap;
|
nsTHashtable<nsRefMapEntry> mRefMap;
|
||||||
nsCOMPtr<nsIRDFDataSource> mLocalStore;
|
nsCOMPtr<nsIXULStore> mLocalStore;
|
||||||
bool mApplyingPersistedAttrs;
|
bool mApplyingPersistedAttrs;
|
||||||
bool mIsWritingFastLoad;
|
bool mIsWritingFastLoad;
|
||||||
bool mDocumentLoaded;
|
bool mDocumentLoaded;
|
||||||
/**
|
/**
|
||||||
* Since ResumeWalk is interruptible, it's possible that last
|
* Since ResumeWalk is interruptible, it's possible that last
|
||||||
* stylesheet finishes loading while the PD walk is still in
|
* stylesheet finishes loading while the PD walk is still in
|
||||||
|
@ -186,36 +186,6 @@ nsXULContentUtils::FindChildByTag(nsIContent* aElement,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsXULContentUtils::GetElementResource(nsIContent* aElement, nsIRDFResource** aResult)
|
|
||||||
{
|
|
||||||
// Perform a reverse mapping from an element in the content model
|
|
||||||
// to an RDF resource.
|
|
||||||
nsresult rv;
|
|
||||||
|
|
||||||
char16_t buf[128];
|
|
||||||
nsFixedString id(buf, ArrayLength(buf), 0);
|
|
||||||
|
|
||||||
// Whoa. Why the "id" attribute? What if it's not even a XUL
|
|
||||||
// element? This is totally bogus!
|
|
||||||
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
|
|
||||||
if (id.IsEmpty())
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
|
|
||||||
// Since the element will store its ID attribute as a document-relative value,
|
|
||||||
// we may need to qualify it first...
|
|
||||||
nsCOMPtr<nsIDocument> doc = aElement->GetDocument();
|
|
||||||
NS_ASSERTION(doc, "element is not in any document");
|
|
||||||
if (! doc)
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
|
|
||||||
rv = nsXULContentUtils::MakeElementResource(doc, id, aResult);
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note: this routine is similar, yet distinctly different from, nsBookmarksService::GetTextForNode
|
Note: this routine is similar, yet distinctly different from, nsBookmarksService::GetTextForNode
|
||||||
*/
|
*/
|
||||||
@ -287,78 +257,6 @@ nsXULContentUtils::GetTextForNode(nsIRDFNode* aNode, nsAString& aResult)
|
|||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsXULContentUtils::MakeElementURI(nsIDocument* aDocument,
|
|
||||||
const nsAString& aElementID,
|
|
||||||
nsCString& aURI)
|
|
||||||
{
|
|
||||||
// Convert an element's ID to a URI that can be used to refer to
|
|
||||||
// the element in the XUL graph.
|
|
||||||
|
|
||||||
nsIURI *docURI = aDocument->GetDocumentURI();
|
|
||||||
NS_ENSURE_TRUE(docURI, NS_ERROR_UNEXPECTED);
|
|
||||||
|
|
||||||
nsRefPtr<nsIURI> docURIClone;
|
|
||||||
nsresult rv = docURI->Clone(getter_AddRefs(docURIClone));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
rv = docURIClone->SetRef(NS_ConvertUTF16toUTF8(aElementID));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
return docURIClone->GetSpec(aURI);
|
|
||||||
}
|
|
||||||
|
|
||||||
// docURIClone is apparently immutable. Fine - we can append ref manually.
|
|
||||||
rv = docURI->GetSpec(aURI);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsAutoCString ref;
|
|
||||||
NS_EscapeURL(NS_ConvertUTF16toUTF8(aElementID), esc_FilePath | esc_AlwaysCopy, ref);
|
|
||||||
|
|
||||||
aURI.Append('#');
|
|
||||||
aURI.Append(ref);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsXULContentUtils::MakeElementResource(nsIDocument* aDocument, const nsAString& aID, nsIRDFResource** aResult)
|
|
||||||
{
|
|
||||||
nsresult rv;
|
|
||||||
|
|
||||||
char buf[256];
|
|
||||||
nsFixedCString uri(buf, sizeof(buf), 0);
|
|
||||||
rv = MakeElementURI(aDocument, aID, uri);
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
rv = gRDF->GetResource(uri, aResult);
|
|
||||||
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create resource");
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsXULContentUtils::MakeElementID(nsIDocument* aDocument,
|
|
||||||
const nsACString& aURI,
|
|
||||||
nsAString& aElementID)
|
|
||||||
{
|
|
||||||
// Convert a URI into an element ID that can be accessed from the
|
|
||||||
// DOM APIs.
|
|
||||||
nsCOMPtr<nsIURI> uri;
|
|
||||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
|
|
||||||
aDocument->GetDocumentCharacterSet().get());
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsAutoCString ref;
|
|
||||||
uri->GetRef(ref);
|
|
||||||
CopyUTF8toUTF16(ref, aElementID);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsXULContentUtils::GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult)
|
nsXULContentUtils::GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult)
|
||||||
{
|
{
|
||||||
|
@ -115,31 +115,9 @@ public:
|
|||||||
nsIRDFResource* aResource,
|
nsIRDFResource* aResource,
|
||||||
nsIContent** aResult);
|
nsIContent** aResult);
|
||||||
|
|
||||||
static nsresult
|
|
||||||
GetElementResource(nsIContent* aElement, nsIRDFResource** aResult);
|
|
||||||
|
|
||||||
static nsresult
|
static nsresult
|
||||||
GetTextForNode(nsIRDFNode* aNode, nsAString& aResult);
|
GetTextForNode(nsIRDFNode* aNode, nsAString& aResult);
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a URI from the element ID given. This uses aElement as the
|
|
||||||
* ref and aDocument's document URI as the base. If aDocument's document
|
|
||||||
* URI does not support refs, this will throw NS_ERROR_NOT_AVAILABLE.
|
|
||||||
*/
|
|
||||||
static nsresult
|
|
||||||
MakeElementURI(nsIDocument* aDocument, const nsAString& aElementID, nsCString& aURI);
|
|
||||||
|
|
||||||
static nsresult
|
|
||||||
MakeElementResource(nsIDocument* aDocument, const nsAString& aElementID, nsIRDFResource** aResult);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract the element ID from aURI. Note that aURI must be an absolute
|
|
||||||
* URI string in UTF8; the element ID is the ref from the URI. If the
|
|
||||||
* scheme does not support refs, then the ID will be empty.
|
|
||||||
*/
|
|
||||||
static nsresult
|
|
||||||
MakeElementID(nsIDocument* aDocument, const nsACString& aURI, nsAString& aElementID);
|
|
||||||
|
|
||||||
static nsresult
|
static nsresult
|
||||||
GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult);
|
GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult);
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#include "nsIContent.h"
|
#include "nsIContent.h"
|
||||||
#include "mozilla/dom/NodeInfo.h"
|
#include "mozilla/dom/NodeInfo.h"
|
||||||
#include "nsIDOMElement.h"
|
#include "nsIDOMElement.h"
|
||||||
#include "nsILocalStore.h"
|
|
||||||
#include "nsIBoxObject.h"
|
#include "nsIBoxObject.h"
|
||||||
#include "nsITreeBoxObject.h"
|
#include "nsITreeBoxObject.h"
|
||||||
#include "nsITreeSelection.h"
|
#include "nsITreeSelection.h"
|
||||||
@ -31,6 +30,7 @@
|
|||||||
#include "nsDOMClassInfoID.h"
|
#include "nsDOMClassInfoID.h"
|
||||||
#include "nsWhitespaceTokenizer.h"
|
#include "nsWhitespaceTokenizer.h"
|
||||||
#include "nsTreeContentView.h"
|
#include "nsTreeContentView.h"
|
||||||
|
#include "nsIXULStore.h"
|
||||||
|
|
||||||
// For security check
|
// For security check
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
@ -139,13 +139,10 @@ protected:
|
|||||||
RemoveMatchesFor(nsTreeRows::Subtree& subtree);
|
RemoveMatchesFor(nsTreeRows::Subtree& subtree);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper methods that determine if the specified container is open.
|
* Helper method that determines if the specified container is open.
|
||||||
*/
|
*/
|
||||||
nsresult
|
bool
|
||||||
IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen);
|
IsContainerOpen(nsIXULTemplateResult* aResource);
|
||||||
|
|
||||||
nsresult
|
|
||||||
IsContainerOpen(nsIRDFResource* aResource, bool* aOpen);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A sorting callback for NS_QuickSort().
|
* A sorting callback for NS_QuickSort().
|
||||||
@ -242,6 +239,11 @@ protected:
|
|||||||
* The builder observers.
|
* The builder observers.
|
||||||
*/
|
*/
|
||||||
nsCOMArray<nsIXULTreeBuilderObserver> mObservers;
|
nsCOMArray<nsIXULTreeBuilderObserver> mObservers;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XUL store for holding open container state
|
||||||
|
*/
|
||||||
|
nsCOMPtr<nsIXULStore> mLocalStore;
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
@ -278,6 +280,7 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder,
|
|||||||
mBoxObject,
|
mBoxObject,
|
||||||
mSelection,
|
mSelection,
|
||||||
mPersistStateStore,
|
mPersistStateStore,
|
||||||
|
mLocalStore,
|
||||||
mObservers)
|
mObservers)
|
||||||
|
|
||||||
DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
|
DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
|
||||||
@ -528,8 +531,7 @@ nsXULTreeBuilder::IsContainerOpen(int32_t aIndex, bool* aOpen)
|
|||||||
nsTreeRows::iterator iter = mRows[aIndex];
|
nsTreeRows::iterator iter = mRows[aIndex];
|
||||||
|
|
||||||
if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) {
|
if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) {
|
||||||
bool isOpen;
|
bool isOpen = IsContainerOpen(iter->mMatch->mResult);
|
||||||
IsContainerOpen(iter->mMatch->mResult, &isOpen);
|
|
||||||
|
|
||||||
iter->mContainerState = isOpen
|
iter->mContainerState = isOpen
|
||||||
? nsTreeRows::eContainerState_Open
|
? nsTreeRows::eContainerState_Open
|
||||||
@ -757,42 +759,16 @@ nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree)
|
|||||||
}
|
}
|
||||||
NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
// Is our root's principal trusted?
|
// Only use the XUL store if the root's principal is trusted.
|
||||||
bool isTrusted = false;
|
bool isTrusted = false;
|
||||||
nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted);
|
nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted);
|
||||||
if (NS_SUCCEEDED(rv) && isTrusted) {
|
if (NS_SUCCEEDED(rv) && isTrusted) {
|
||||||
// Get the datasource we intend to use to remember open state.
|
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
|
||||||
nsAutoString datasourceStr;
|
if(NS_WARN_IF(!mLocalStore)){
|
||||||
mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::statedatasource, datasourceStr);
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
|
|
||||||
// since we are trusted, use the user specified datasource
|
|
||||||
// if non specified, use localstore, which gives us
|
|
||||||
// persistence across sessions
|
|
||||||
if (! datasourceStr.IsEmpty()) {
|
|
||||||
gRDFService->GetDataSource(NS_ConvertUTF16toUTF8(datasourceStr).get(),
|
|
||||||
getter_AddRefs(mPersistStateStore));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gRDFService->GetDataSource("rdf:local-store",
|
|
||||||
getter_AddRefs(mPersistStateStore));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either no specific datasource was specified, or we failed
|
|
||||||
// to get one because we are not trusted.
|
|
||||||
//
|
|
||||||
// XXX if it were possible to ``write an arbitrary datasource
|
|
||||||
// back'', then we could also allow an untrusted document to
|
|
||||||
// use a statedatasource from the same codebase.
|
|
||||||
if (! mPersistStateStore) {
|
|
||||||
mPersistStateStore =
|
|
||||||
do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource");
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_ASSERTION(mPersistStateStore, "failed to get a persistent state store");
|
|
||||||
if (! mPersistStateStore)
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
|
|
||||||
Rebuild();
|
Rebuild();
|
||||||
|
|
||||||
EnsureSortVariables();
|
EnsureSortVariables();
|
||||||
@ -830,34 +806,36 @@ nsXULTreeBuilder::ToggleOpenState(int32_t aIndex)
|
|||||||
observer->OnToggleOpenState(aIndex);
|
observer->OnToggleOpenState(aIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPersistStateStore) {
|
if (mLocalStore && mRoot) {
|
||||||
bool isOpen;
|
bool isOpen;
|
||||||
IsContainerOpen(aIndex, &isOpen);
|
IsContainerOpen(aIndex, &isOpen);
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFResource> container;
|
nsIDocument* doc = mRoot->GetDocument();
|
||||||
GetResourceFor(aIndex, getter_AddRefs(container));
|
if (!doc) {
|
||||||
if (! container)
|
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasProperty;
|
nsIURI* docURI = doc->GetDocumentURI();
|
||||||
IsContainerOpen(container, &hasProperty);
|
nsTreeRows::Row& row = *(mRows[aIndex]);
|
||||||
|
nsAutoString nodeid;
|
||||||
|
nsresult rv = row.mMatch->mResult->GetId(nodeid);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString utf8uri;
|
||||||
|
rv = docURI->GetSpec(utf8uri);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF8toUTF16 uri(utf8uri);
|
||||||
|
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
if (hasProperty) {
|
mLocalStore->RemoveValue(uri, nodeid, NS_LITERAL_STRING("open"));
|
||||||
mPersistStateStore->Unassert(container,
|
|
||||||
nsXULContentUtils::NC_open,
|
|
||||||
nsXULContentUtils::true_);
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseContainer(aIndex);
|
CloseContainer(aIndex);
|
||||||
}
|
} else {
|
||||||
else {
|
mLocalStore->SetValue(uri, nodeid, NS_LITERAL_STRING("open"),
|
||||||
if (! hasProperty) {
|
NS_LITERAL_STRING("true"));
|
||||||
mPersistStateStore->Assert(container,
|
|
||||||
nsXULContentUtils::NC_open,
|
|
||||||
nsXULContentUtils::true_,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenContainer(aIndex, result);
|
OpenContainer(aIndex, result);
|
||||||
}
|
}
|
||||||
@ -1225,10 +1203,9 @@ nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
|
|||||||
if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK;
|
if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open;
|
if (IsContainerOpen(result)) {
|
||||||
IsContainerOpen(result, &open);
|
|
||||||
if (open)
|
|
||||||
OpenContainer(iter.GetRowIndex(), result);
|
OpenContainer(iter.GetRowIndex(), result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1636,9 +1613,7 @@ nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
|
|||||||
|
|
||||||
// If this is open, then remember it so we can recursively add
|
// If this is open, then remember it so we can recursively add
|
||||||
// *its* rows to the tree.
|
// *its* rows to the tree.
|
||||||
bool isOpen = false;
|
if (IsContainerOpen(nextresult)) {
|
||||||
IsContainerOpen(nextresult, &isOpen);
|
|
||||||
if (isOpen) {
|
|
||||||
if (open.AppendElement(count) == nullptr)
|
if (open.AppendElement(count) == nullptr)
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
@ -1722,36 +1697,42 @@ nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree& subtree)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen)
|
bool
|
||||||
|
nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult)
|
||||||
{
|
{
|
||||||
// items are never open if recursion is disabled
|
// items are never open if recursion is disabled
|
||||||
if ((mFlags & eDontRecurse) && aResult != mRootResult) {
|
if ((mFlags & eDontRecurse) && aResult != mRootResult) {
|
||||||
*aOpen = false;
|
return false;
|
||||||
return NS_OK;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRDFResource> id;
|
if (!mLocalStore) {
|
||||||
nsresult rv = GetResultResource(aResult, getter_AddRefs(id));
|
return false;
|
||||||
if (NS_FAILED(rv))
|
}
|
||||||
return rv;
|
|
||||||
|
|
||||||
return IsContainerOpen(id, aOpen);
|
nsIDocument* doc = mRoot->GetDocument();
|
||||||
}
|
if (!doc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsIURI* docURI = doc->GetDocumentURI();
|
||||||
nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aResource, bool* aOpen)
|
|
||||||
{
|
|
||||||
if (mPersistStateStore)
|
|
||||||
mPersistStateStore->HasAssertion(aResource,
|
|
||||||
nsXULContentUtils::NC_open,
|
|
||||||
nsXULContentUtils::true_,
|
|
||||||
true,
|
|
||||||
aOpen);
|
|
||||||
else
|
|
||||||
*aOpen = false;
|
|
||||||
|
|
||||||
return NS_OK;
|
nsAutoString nodeid;
|
||||||
|
nsresult rv = aResult->GetId(nodeid);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString utf8uri;
|
||||||
|
rv = docURI->GetSpec(utf8uri);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF8toUTF16 uri(utf8uri);
|
||||||
|
|
||||||
|
nsAutoString val;
|
||||||
|
mLocalStore->GetValue(uri, nodeid, NS_LITERAL_STRING("open"), val);
|
||||||
|
return val.EqualsLiteral("true");
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -10752,12 +10752,6 @@ class CGDescriptor(CGThing):
|
|||||||
if props.isCrossOriginGetter:
|
if props.isCrossOriginGetter:
|
||||||
crossOriginGetters.add(m.identifier.name)
|
crossOriginGetters.add(m.identifier.name)
|
||||||
if not m.readonly:
|
if not m.readonly:
|
||||||
for extAttr in ["PutForwards", "Replaceable"]:
|
|
||||||
if m.getExtendedAttribute(extAttr):
|
|
||||||
raise TypeError("Writable attributes should not "
|
|
||||||
"have %s specified.\n"
|
|
||||||
"%s" %
|
|
||||||
(extAttr, m.location))
|
|
||||||
if m.isStatic():
|
if m.isStatic():
|
||||||
assert descriptor.interface.hasInterfaceObject()
|
assert descriptor.interface.hasInterfaceObject()
|
||||||
cgThings.append(CGStaticSetter(descriptor, m))
|
cgThings.append(CGStaticSetter(descriptor, m))
|
||||||
|
@ -957,10 +957,15 @@ class IDLInterface(IDLObjectWithScope):
|
|||||||
list(i.location for i in
|
list(i.location for i in
|
||||||
self.interfacesBasedOnSelf if i.parent == self))
|
self.interfacesBasedOnSelf if i.parent == self))
|
||||||
|
|
||||||
|
|
||||||
for member in self.members:
|
for member in self.members:
|
||||||
member.validate()
|
member.validate()
|
||||||
|
|
||||||
|
if self.isCallback() and member.getExtendedAttribute("Replaceable"):
|
||||||
|
raise WebIDLError("[Replaceable] used on an attribute on "
|
||||||
|
"interface %s which is a callback interface" %
|
||||||
|
self.identifier.name,
|
||||||
|
[self.location, member.location])
|
||||||
|
|
||||||
# Check that PutForwards refers to another attribute and that no
|
# Check that PutForwards refers to another attribute and that no
|
||||||
# cycles exist in forwarded assignments.
|
# cycles exist in forwarded assignments.
|
||||||
if member.isAttr():
|
if member.isAttr():
|
||||||
@ -3236,6 +3241,15 @@ class IDLAttribute(IDLInterfaceMember):
|
|||||||
raise WebIDLError("[PutForwards] takes an identifier",
|
raise WebIDLError("[PutForwards] takes an identifier",
|
||||||
[attr.location, self.location])
|
[attr.location, self.location])
|
||||||
elif identifier == "Replaceable":
|
elif identifier == "Replaceable":
|
||||||
|
if not attr.noArguments():
|
||||||
|
raise WebIDLError("[Replaceable] must take no arguments",
|
||||||
|
[attr.location])
|
||||||
|
if not self.readonly:
|
||||||
|
raise WebIDLError("[Replaceable] is only allowed on readonly "
|
||||||
|
"attributes", [attr.location, self.location])
|
||||||
|
if self.isStatic():
|
||||||
|
raise WebIDLError("[Replaceable] is only allowed on non-static "
|
||||||
|
"attributes", [attr.location, self.location])
|
||||||
if self.getExtendedAttribute("PutForwards") is not None:
|
if self.getExtendedAttribute("PutForwards") is not None:
|
||||||
raise WebIDLError("[PutForwards] and [Replaceable] can't both "
|
raise WebIDLError("[PutForwards] and [Replaceable] can't both "
|
||||||
"appear on the same attribute",
|
"appear on the same attribute",
|
||||||
|
58
dom/bindings/parser/tests/test_replaceable.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# 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/.
|
||||||
|
|
||||||
|
def should_throw(parser, harness, message, code):
|
||||||
|
parser = parser.reset();
|
||||||
|
threw = False
|
||||||
|
try:
|
||||||
|
parser.parse(code)
|
||||||
|
parser.finish()
|
||||||
|
except:
|
||||||
|
threw = True
|
||||||
|
|
||||||
|
harness.ok(threw, "Should have thrown: %s" % message)
|
||||||
|
|
||||||
|
|
||||||
|
def WebIDLTest(parser, harness):
|
||||||
|
# The [Replaceable] extended attribute MUST take no arguments.
|
||||||
|
should_throw(parser, harness, "no arguments", """
|
||||||
|
interface I {
|
||||||
|
[Replaceable=X] readonly attribute long A;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
|
# An attribute with the [Replaceable] extended attribute MUST NOT also be
|
||||||
|
# declared with the [PutForwards] extended attribute.
|
||||||
|
should_throw(parser, harness, "PutForwards", """
|
||||||
|
interface I {
|
||||||
|
[PutForwards=B, Replaceable] readonly attribute J A;
|
||||||
|
};
|
||||||
|
interface J {
|
||||||
|
attribute long B;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
|
# The [Replaceable] extended attribute MUST NOT be used on an attribute
|
||||||
|
# that is not read only.
|
||||||
|
should_throw(parser, harness, "writable attribute", """
|
||||||
|
interface I {
|
||||||
|
[Replaceable] attribute long A;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
|
# The [Replaceable] extended attribute MUST NOT be used on a static
|
||||||
|
# attribute.
|
||||||
|
should_throw(parser, harness, "static attribute", """
|
||||||
|
interface I {
|
||||||
|
[Replaceable] static readonly attribute long A;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
|
# The [Replaceable] extended attribute MUST NOT be used on an attribute
|
||||||
|
# declared on a callback interface.
|
||||||
|
should_throw(parser, harness, "callback interface", """
|
||||||
|
callback interface I {
|
||||||
|
[Replaceable] readonly attribute long A;
|
||||||
|
};
|
||||||
|
""")
|
@ -68,6 +68,8 @@ this.BrowserElementParentBuilder = {
|
|||||||
function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
|
function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
|
||||||
debug("Creating new BrowserElementParent object for " + frameLoader);
|
debug("Creating new BrowserElementParent object for " + frameLoader);
|
||||||
this._domRequestCounter = 0;
|
this._domRequestCounter = 0;
|
||||||
|
this._domRequestReady = false;
|
||||||
|
this._pendingAPICalls = [];
|
||||||
this._pendingDOMRequests = {};
|
this._pendingDOMRequests = {};
|
||||||
this._pendingSetInputMethodActive = [];
|
this._pendingSetInputMethodActive = [];
|
||||||
this._hasRemoteFrame = hasRemoteFrame;
|
this._hasRemoteFrame = hasRemoteFrame;
|
||||||
@ -94,7 +96,7 @@ function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
|
|||||||
|
|
||||||
let defineNoReturnMethod = function(name, fn) {
|
let defineNoReturnMethod = function(name, fn) {
|
||||||
XPCNativeWrapper.unwrap(self._frameElement)[name] = function method() {
|
XPCNativeWrapper.unwrap(self._frameElement)[name] = function method() {
|
||||||
if (!self._mm) {
|
if (!self._domRequestReady) {
|
||||||
// Remote browser haven't been created, we just queue the API call.
|
// Remote browser haven't been created, we just queue the API call.
|
||||||
let args = Array.slice(arguments);
|
let args = Array.slice(arguments);
|
||||||
args.unshift(self);
|
args.unshift(self);
|
||||||
@ -181,7 +183,6 @@ function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
|
|||||||
} else {
|
} else {
|
||||||
// if we are a pending frame, we setup message manager after
|
// if we are a pending frame, we setup message manager after
|
||||||
// observing remote-browser-frame-shown
|
// observing remote-browser-frame-shown
|
||||||
this._pendingAPICalls = [];
|
|
||||||
Services.obs.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true);
|
Services.obs.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,6 +371,13 @@ BrowserElementParent.prototype = {
|
|||||||
this._ownerVisibilityChange();
|
this._ownerVisibilityChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this._domRequestReady) {
|
||||||
|
// At least, one message listener such as for hello is registered.
|
||||||
|
// So we can use sendAsyncMessage now.
|
||||||
|
this._domRequestReady = true;
|
||||||
|
this._runPendingAPICall();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: this._frameElement.getAttribute('name'),
|
name: this._frameElement.getAttribute('name'),
|
||||||
fullscreenAllowed:
|
fullscreenAllowed:
|
||||||
@ -531,7 +539,7 @@ BrowserElementParent.prototype = {
|
|||||||
Services.DOMRequest.fireErrorAsync(req, "fail");
|
Services.DOMRequest.fireErrorAsync(req, "fail");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (this._mm) {
|
if (this._domRequestReady) {
|
||||||
send();
|
send();
|
||||||
} else {
|
} else {
|
||||||
// Child haven't been loaded.
|
// Child haven't been loaded.
|
||||||
@ -797,7 +805,7 @@ BrowserElementParent.prototype = {
|
|||||||
if (self._nextPaintListeners.push(listener) == 1)
|
if (self._nextPaintListeners.push(listener) == 1)
|
||||||
self._sendAsyncMsg('activate-next-paint-listener');
|
self._sendAsyncMsg('activate-next-paint-listener');
|
||||||
};
|
};
|
||||||
if (!this._mm) {
|
if (!this._domRequestReady) {
|
||||||
this._pendingAPICalls.push(run);
|
this._pendingAPICalls.push(run);
|
||||||
} else {
|
} else {
|
||||||
run();
|
run();
|
||||||
@ -820,7 +828,7 @@ BrowserElementParent.prototype = {
|
|||||||
if (self._nextPaintListeners.length == 0)
|
if (self._nextPaintListeners.length == 0)
|
||||||
self._sendAsyncMsg('deactivate-next-paint-listener');
|
self._sendAsyncMsg('deactivate-next-paint-listener');
|
||||||
};
|
};
|
||||||
if (!this._mm) {
|
if (!this._domRequestReady) {
|
||||||
this._pendingAPICalls.push(run);
|
this._pendingAPICalls.push(run);
|
||||||
} else {
|
} else {
|
||||||
run();
|
run();
|
||||||
@ -908,7 +916,6 @@ BrowserElementParent.prototype = {
|
|||||||
if (!this._mm) {
|
if (!this._mm) {
|
||||||
this._setupMessageListener();
|
this._setupMessageListener();
|
||||||
this._registerAppManifest();
|
this._registerAppManifest();
|
||||||
this._runPendingAPICall();
|
|
||||||
}
|
}
|
||||||
Services.obs.removeObserver(this, 'remote-browser-frame-shown');
|
Services.obs.removeObserver(this, 'remote-browser-frame-shown');
|
||||||
}
|
}
|
||||||
|
@ -51,11 +51,16 @@ function runTest() {
|
|||||||
// should have 'backgroundLRU' equals 1
|
// should have 'backgroundLRU' equals 1
|
||||||
var p = expectPriorityWithBackgroundLRUSet(childID, '1');
|
var p = expectPriorityWithBackgroundLRUSet(childID, '1');
|
||||||
iframe2.setVisible(false);
|
iframe2.setVisible(false);
|
||||||
document.body.removeChild(iframe2);
|
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
|
|
||||||
}).then(SimpleTest.finish);
|
}).then(function() {
|
||||||
|
// Don't call removeChild immediately after calling setVisible.
|
||||||
|
// setVisible on remote browser is async method, so we should wait
|
||||||
|
// until it sends to the child process.
|
||||||
|
document.body.removeChild(iframe2);
|
||||||
|
SimpleTest.finish();
|
||||||
|
});
|
||||||
|
|
||||||
document.body.appendChild(iframe1);
|
document.body.appendChild(iframe1);
|
||||||
}
|
}
|
||||||
|
@ -2021,6 +2021,9 @@ EventStateManager::DoScrollZoom(nsIFrame* aTargetFrame,
|
|||||||
} else {
|
} else {
|
||||||
ChangeTextSize(change);
|
ChangeTextSize(change);
|
||||||
}
|
}
|
||||||
|
nsContentUtils::DispatchChromeEvent(mDocument, static_cast<nsIDocument*>(mDocument),
|
||||||
|
NS_LITERAL_STRING("ZoomChangeUsingMouseWheel"),
|
||||||
|
true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +65,9 @@ scriptFromStringBlocked = An attempt to call JavaScript from a string (by callin
|
|||||||
# LOCALIZATION NOTE (hostNameMightBeKeyword):
|
# LOCALIZATION NOTE (hostNameMightBeKeyword):
|
||||||
# %1$S is the hostname in question and %2$S is the keyword
|
# %1$S is the hostname in question and %2$S is the keyword
|
||||||
hostNameMightBeKeyword = Interpreting %1$S as a hostname, not a keyword. If you intended this to be a keyword, use '%2$S' (wrapped in single quotes).
|
hostNameMightBeKeyword = Interpreting %1$S as a hostname, not a keyword. If you intended this to be a keyword, use '%2$S' (wrapped in single quotes).
|
||||||
|
# LOCALIZATION NOTE (notSupportingDirective):
|
||||||
|
# directive is not supported (e.g. 'reflected-xss')
|
||||||
|
notSupportingDirective = Not supporting directive '%1$S'. Directive and values will be ignored.
|
||||||
|
|
||||||
# CSP Errors:
|
# CSP Errors:
|
||||||
policyURINotAlone = policy-uri directive can only appear alone
|
policyURINotAlone = policy-uri directive can only appear alone
|
||||||
|
@ -59,7 +59,4 @@ RESOURCE_FILES += [
|
|||||||
'res/text_caret_tilt_right@1.5x.png',
|
'res/text_caret_tilt_right@1.5x.png',
|
||||||
'res/text_caret_tilt_right@2.25x.png',
|
'res/text_caret_tilt_right@2.25x.png',
|
||||||
'res/text_caret_tilt_right@2x.png',
|
'res/text_caret_tilt_right@2x.png',
|
||||||
'res/text_selection_handle.png',
|
|
||||||
'res/text_selection_handle@1.5.png',
|
|
||||||
'res/text_selection_handle@2.png',
|
|
||||||
]
|
]
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.3 KiB |
@ -3,11 +3,17 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "CreateElementTxn.h"
|
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
|
#include "mozilla/dom/Selection.h"
|
||||||
|
|
||||||
|
#include "mozilla/Casting.h"
|
||||||
|
|
||||||
|
#include "CreateElementTxn.h"
|
||||||
#include "nsAlgorithm.h"
|
#include "nsAlgorithm.h"
|
||||||
|
#include "nsAString.h"
|
||||||
#include "nsDebug.h"
|
#include "nsDebug.h"
|
||||||
#include "nsEditor.h"
|
#include "nsEditor.h"
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
@ -21,14 +27,19 @@
|
|||||||
#include "nsReadableUtils.h"
|
#include "nsReadableUtils.h"
|
||||||
#include "nsStringFwd.h"
|
#include "nsStringFwd.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsAString.h"
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
CreateElementTxn::CreateElementTxn()
|
CreateElementTxn::CreateElementTxn(nsEditor& aEditor,
|
||||||
|
nsIAtom& aTag,
|
||||||
|
nsINode& aParent,
|
||||||
|
int32_t aOffsetInParent)
|
||||||
: EditTxn()
|
: EditTxn()
|
||||||
|
, mEditor(&aEditor)
|
||||||
|
, mTag(&aTag)
|
||||||
|
, mParent(&aParent)
|
||||||
|
, mOffsetInParent(aOffsetInParent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,114 +56,86 @@ NS_IMPL_ADDREF_INHERITED(CreateElementTxn, EditTxn)
|
|||||||
NS_IMPL_RELEASE_INHERITED(CreateElementTxn, EditTxn)
|
NS_IMPL_RELEASE_INHERITED(CreateElementTxn, EditTxn)
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CreateElementTxn)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CreateElementTxn)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(EditTxn)
|
NS_INTERFACE_MAP_END_INHERITING(EditTxn)
|
||||||
NS_IMETHODIMP CreateElementTxn::Init(nsEditor *aEditor,
|
|
||||||
const nsAString &aTag,
|
|
||||||
nsIDOMNode *aParent,
|
NS_IMETHODIMP
|
||||||
uint32_t aOffsetInParent)
|
CreateElementTxn::DoTransaction()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aEditor&&aParent, "null args");
|
MOZ_ASSERT(mEditor && mTag && mParent);
|
||||||
if (!aEditor || !aParent) { return NS_ERROR_NULL_POINTER; }
|
|
||||||
|
|
||||||
mEditor = aEditor;
|
mNewNode = mEditor->CreateHTMLContent(mTag);
|
||||||
mTag = aTag;
|
NS_ENSURE_STATE(mNewNode);
|
||||||
mParent = do_QueryInterface(aParent);
|
|
||||||
mOffsetInParent = aOffsetInParent;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NS_IMETHODIMP CreateElementTxn::DoTransaction(void)
|
|
||||||
{
|
|
||||||
NS_ASSERTION(mEditor && mParent, "bad state");
|
|
||||||
NS_ENSURE_TRUE(mEditor && mParent, NS_ERROR_NOT_INITIALIZED);
|
|
||||||
|
|
||||||
nsCOMPtr<Element> newContent =
|
|
||||||
mEditor->CreateHTMLContent(nsCOMPtr<nsIAtom>(do_GetAtom(mTag)));
|
|
||||||
NS_ENSURE_STATE(newContent);
|
|
||||||
|
|
||||||
mNewNode = newContent->AsDOMNode();
|
|
||||||
// Try to insert formatting whitespace for the new node:
|
// Try to insert formatting whitespace for the new node:
|
||||||
mEditor->MarkNodeDirty(mNewNode);
|
mEditor->MarkNodeDirty(GetAsDOMNode(mNewNode));
|
||||||
|
|
||||||
// insert the new node
|
// Insert the new node
|
||||||
if (CreateElementTxn::eAppend == int32_t(mOffsetInParent)) {
|
ErrorResult rv;
|
||||||
nsCOMPtr<nsIDOMNode> resultNode;
|
if (mOffsetInParent == -1) {
|
||||||
return mParent->AppendChild(mNewNode, getter_AddRefs(resultNode));
|
mParent->AppendChild(*mNewNode, rv);
|
||||||
|
return rv.ErrorCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsINode> parent = do_QueryInterface(mParent);
|
mOffsetInParent = std::min(mOffsetInParent,
|
||||||
NS_ENSURE_STATE(parent);
|
static_cast<int32_t>(mParent->GetChildCount()));
|
||||||
|
|
||||||
mOffsetInParent = std::min(mOffsetInParent, parent->GetChildCount());
|
// Note, it's ok for mRefNode to be null. That means append
|
||||||
|
mRefNode = mParent->GetChildAt(mOffsetInParent);
|
||||||
|
|
||||||
// note, it's ok for mRefNode to be null. that means append
|
mParent->InsertBefore(*mNewNode, mRefNode, rv);
|
||||||
nsIContent* refNode = parent->GetChildAt(mOffsetInParent);
|
NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
|
||||||
mRefNode = refNode ? refNode->AsDOMNode() : nullptr;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMNode> resultNode;
|
// Only set selection to insertion point if editor gives permission
|
||||||
nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode));
|
if (!mEditor->GetShouldTxnSetSelection()) {
|
||||||
NS_ENSURE_SUCCESS(result, result);
|
// Do nothing - DOM range gravity will adjust selection
|
||||||
|
|
||||||
// only set selection to insertion point if editor gives permission
|
|
||||||
bool bAdjustSelection;
|
|
||||||
mEditor->ShouldTxnSetSelection(&bAdjustSelection);
|
|
||||||
if (!bAdjustSelection) {
|
|
||||||
// do nothing - dom range gravity will adjust selection
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsISelection> selection;
|
nsRefPtr<Selection> selection = mEditor->GetSelection();
|
||||||
result = mEditor->GetSelection(getter_AddRefs(selection));
|
|
||||||
NS_ENSURE_SUCCESS(result, result);
|
|
||||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||||
|
|
||||||
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(mParent);
|
rv = selection->CollapseNative(mParent, mParent->IndexOf(mNewNode) + 1);
|
||||||
NS_ENSURE_STATE(parentContent);
|
NS_ASSERTION(!rv.Failed(),
|
||||||
|
"selection could not be collapsed after insert");
|
||||||
result = selection->CollapseNative(parentContent,
|
return NS_OK;
|
||||||
parentContent->IndexOf(newContent) + 1);
|
|
||||||
NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert.");
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP CreateElementTxn::UndoTransaction(void)
|
NS_IMETHODIMP
|
||||||
|
CreateElementTxn::UndoTransaction()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mEditor && mParent, "bad state");
|
MOZ_ASSERT(mEditor && mParent);
|
||||||
NS_ENSURE_TRUE(mEditor && mParent, NS_ERROR_NOT_INITIALIZED);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMNode> resultNode;
|
ErrorResult rv;
|
||||||
return mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode));
|
mParent->RemoveChild(*mNewNode, rv);
|
||||||
|
|
||||||
|
return rv.ErrorCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP CreateElementTxn::RedoTransaction(void)
|
NS_IMETHODIMP
|
||||||
|
CreateElementTxn::RedoTransaction()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mEditor && mParent, "bad state");
|
MOZ_ASSERT(mEditor && mParent);
|
||||||
NS_ENSURE_TRUE(mEditor && mParent, NS_ERROR_NOT_INITIALIZED);
|
|
||||||
|
|
||||||
// first, reset mNewNode so it has no attributes or content
|
// First, reset mNewNode so it has no attributes or content
|
||||||
nsCOMPtr<nsIDOMCharacterData>nodeAsText = do_QueryInterface(mNewNode);
|
// XXX We never actually did this, we only cleared mNewNode's contents if it
|
||||||
if (nodeAsText)
|
// was a CharacterData node (which it's not, it's an Element)
|
||||||
{
|
|
||||||
nodeAsText->SetData(EmptyString());
|
// Now, reinsert mNewNode
|
||||||
}
|
ErrorResult rv;
|
||||||
|
mParent->InsertBefore(*mNewNode, mRefNode, rv);
|
||||||
// now, reinsert mNewNode
|
return rv.ErrorCode();
|
||||||
nsCOMPtr<nsIDOMNode> resultNode;
|
|
||||||
return mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP CreateElementTxn::GetTxnDescription(nsAString& aString)
|
NS_IMETHODIMP
|
||||||
|
CreateElementTxn::GetTxnDescription(nsAString& aString)
|
||||||
{
|
{
|
||||||
aString.AssignLiteral("CreateElementTxn: ");
|
aString.AssignLiteral("CreateElementTxn: ");
|
||||||
aString += mTag;
|
aString += nsDependentAtomString(mTag);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP CreateElementTxn::GetNewNode(nsIDOMNode **aNewNode)
|
already_AddRefed<Element>
|
||||||
|
CreateElementTxn::GetNewNode()
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(aNewNode, NS_ERROR_NULL_POINTER);
|
return nsCOMPtr<Element>(mNewNode).forget();
|
||||||
NS_ENSURE_TRUE(mNewNode, NS_ERROR_NOT_INITIALIZED);
|
|
||||||
*aNewNode = mNewNode;
|
|
||||||
NS_ADDREF(*aNewNode);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
@ -9,21 +9,24 @@
|
|||||||
#include "EditTxn.h"
|
#include "EditTxn.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
#include "nsIDOMNode.h"
|
|
||||||
#include "nsISupportsImpl.h"
|
#include "nsISupportsImpl.h"
|
||||||
#include "nsString.h"
|
|
||||||
#include "nscore.h"
|
|
||||||
|
|
||||||
class nsEditor;
|
class nsEditor;
|
||||||
|
class nsIAtom;
|
||||||
|
class nsIContent;
|
||||||
|
class nsINode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transaction that creates a new node in the content tree.
|
* A transaction that creates a new node in the content tree.
|
||||||
*/
|
*/
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class Element;
|
||||||
|
|
||||||
class CreateElementTxn : public EditTxn
|
class CreateElementTxn : public EditTxn
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum { eAppend=-1 };
|
|
||||||
|
|
||||||
/** Initialize the transaction.
|
/** Initialize the transaction.
|
||||||
* @param aEditor the provider of basic editing functionality
|
* @param aEditor the provider of basic editing functionality
|
||||||
* @param aTag the tag (P, HR, TABLE, etc.) for the new element
|
* @param aTag the tag (P, HR, TABLE, etc.) for the new element
|
||||||
@ -31,12 +34,10 @@ public:
|
|||||||
* @param aOffsetInParent the location in aParent to insert the new element
|
* @param aOffsetInParent the location in aParent to insert the new element
|
||||||
* if eAppend, the new element is appended as the last child
|
* if eAppend, the new element is appended as the last child
|
||||||
*/
|
*/
|
||||||
NS_IMETHOD Init(nsEditor *aEditor,
|
CreateElementTxn(nsEditor& aEditor,
|
||||||
const nsAString& aTag,
|
nsIAtom& aTag,
|
||||||
nsIDOMNode *aParent,
|
nsINode& aParent,
|
||||||
uint32_t aOffsetInParent);
|
int32_t aOffsetInParent);
|
||||||
|
|
||||||
CreateElementTxn();
|
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS_INHERITED
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CreateElementTxn, EditTxn)
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CreateElementTxn, EditTxn)
|
||||||
@ -45,28 +46,31 @@ public:
|
|||||||
|
|
||||||
NS_IMETHOD RedoTransaction();
|
NS_IMETHOD RedoTransaction();
|
||||||
|
|
||||||
NS_IMETHOD GetNewNode(nsIDOMNode **aNewNode);
|
already_AddRefed<Element> GetNewNode();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~CreateElementTxn();
|
virtual ~CreateElementTxn();
|
||||||
|
|
||||||
/** the document into which the new node will be inserted */
|
/** the document into which the new node will be inserted */
|
||||||
nsEditor* mEditor;
|
nsEditor* mEditor;
|
||||||
|
|
||||||
/** the tag (mapping to object type) for the new element */
|
/** the tag (mapping to object type) for the new element */
|
||||||
nsString mTag;
|
nsCOMPtr<nsIAtom> mTag;
|
||||||
|
|
||||||
/** the node into which the new node will be inserted */
|
/** the node into which the new node will be inserted */
|
||||||
nsCOMPtr<nsIDOMNode> mParent;
|
nsCOMPtr<nsINode> mParent;
|
||||||
|
|
||||||
/** the index in mParent for the new node */
|
/** the index in mParent for the new node */
|
||||||
uint32_t mOffsetInParent;
|
int32_t mOffsetInParent;
|
||||||
|
|
||||||
/** the new node to insert */
|
/** the new node to insert */
|
||||||
nsCOMPtr<nsIDOMNode> mNewNode;
|
nsCOMPtr<Element> mNewNode;
|
||||||
|
|
||||||
/** the node we will insert mNewNode before. We compute this ourselves. */
|
/** the node we will insert mNewNode before. We compute this ourselves. */
|
||||||
nsCOMPtr<nsIDOMNode> mRefNode;
|
nsCOMPtr<nsIContent> mRefNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -139,8 +139,6 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsINode* aNode,
|
|||||||
// see what kind of node we have
|
// see what kind of node we have
|
||||||
if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
|
if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
|
||||||
// if the node is a chardata node, then delete chardata content
|
// if the node is a chardata node, then delete chardata content
|
||||||
nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
|
|
||||||
|
|
||||||
int32_t numToDel;
|
int32_t numToDel;
|
||||||
if (aStartOffset == aEndOffset) {
|
if (aStartOffset == aEndOffset) {
|
||||||
numToDel = 1;
|
numToDel = 1;
|
||||||
@ -148,9 +146,14 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsINode* aNode,
|
|||||||
numToDel = aEndOffset - aStartOffset;
|
numToDel = aEndOffset - aStartOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(aNode);
|
nsRefPtr<nsGenericDOMDataNode> charDataNode =
|
||||||
nsresult res = txn->Init(mEditor, charDataNode, aStartOffset, numToDel,
|
static_cast<nsGenericDOMDataNode*>(aNode);
|
||||||
mRangeUpdater);
|
|
||||||
|
nsRefPtr<DeleteTextTxn> txn =
|
||||||
|
new DeleteTextTxn(*mEditor, *charDataNode, aStartOffset, numToDel,
|
||||||
|
mRangeUpdater);
|
||||||
|
|
||||||
|
nsresult res = txn->Init();
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
|
||||||
AppendChild(txn);
|
AppendChild(txn);
|
||||||
@ -193,11 +196,12 @@ DeleteRangeTxn::CreateTxnsToDeleteContent(nsINode* aNode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (numToDelete) {
|
if (numToDelete) {
|
||||||
nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
|
nsRefPtr<nsGenericDOMDataNode> dataNode =
|
||||||
|
static_cast<nsGenericDOMDataNode*>(aNode);
|
||||||
|
nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn(*mEditor, *dataNode,
|
||||||
|
start, numToDelete, mRangeUpdater);
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(aNode);
|
nsresult res = txn->Init();
|
||||||
nsresult res = txn->Init(mEditor, charDataNode, start, numToDelete,
|
|
||||||
mRangeUpdater);
|
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
|
||||||
AppendChild(txn);
|
AppendChild(txn);
|
||||||
|
@ -19,14 +19,19 @@
|
|||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
DeleteTextTxn::DeleteTextTxn() :
|
DeleteTextTxn::DeleteTextTxn(nsEditor& aEditor,
|
||||||
EditTxn(),
|
nsGenericDOMDataNode& aCharData, uint32_t aOffset,
|
||||||
mEditor(nullptr),
|
uint32_t aNumCharsToDelete,
|
||||||
mCharData(),
|
nsRangeUpdater* aRangeUpdater)
|
||||||
mOffset(0),
|
: EditTxn()
|
||||||
mNumCharsToDelete(0),
|
, mEditor(aEditor)
|
||||||
mRangeUpdater(nullptr)
|
, mCharData(&aCharData)
|
||||||
|
, mOffset(aOffset)
|
||||||
|
, mNumCharsToDelete(aNumCharsToDelete)
|
||||||
|
, mRangeUpdater(aRangeUpdater)
|
||||||
{
|
{
|
||||||
|
NS_ASSERTION(mCharData->Length() >= aOffset + aNumCharsToDelete,
|
||||||
|
"Trying to delete more characters than in node");
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTxn, EditTxn,
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTxn, EditTxn,
|
||||||
@ -35,42 +40,23 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTxn, EditTxn,
|
|||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTxn)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTxn)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(EditTxn)
|
NS_INTERFACE_MAP_END_INHERITING(EditTxn)
|
||||||
|
|
||||||
NS_IMETHODIMP
|
nsresult
|
||||||
DeleteTextTxn::Init(nsEditor* aEditor,
|
DeleteTextTxn::Init()
|
||||||
nsIDOMCharacterData* aCharData,
|
|
||||||
uint32_t aOffset,
|
|
||||||
uint32_t aNumCharsToDelete,
|
|
||||||
nsRangeUpdater* aRangeUpdater)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aEditor && aCharData);
|
// Do nothing if the node is read-only
|
||||||
|
if (!mEditor.IsModifiableNode(mCharData)) {
|
||||||
mEditor = aEditor;
|
|
||||||
mCharData = aCharData;
|
|
||||||
|
|
||||||
// do nothing if the node is read-only
|
|
||||||
if (!mEditor->IsModifiableNode(mCharData)) {
|
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
mOffset = aOffset;
|
|
||||||
mNumCharsToDelete = aNumCharsToDelete;
|
|
||||||
#ifdef DEBUG
|
|
||||||
uint32_t length;
|
|
||||||
mCharData->GetLength(&length);
|
|
||||||
NS_ASSERTION(length >= aOffset + aNumCharsToDelete,
|
|
||||||
"Trying to delete more characters than in node");
|
|
||||||
#endif
|
|
||||||
mDeletedText.Truncate();
|
|
||||||
mRangeUpdater = aRangeUpdater;
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
DeleteTextTxn::DoTransaction()
|
DeleteTextTxn::DoTransaction()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mEditor && mCharData);
|
MOZ_ASSERT(mCharData);
|
||||||
|
|
||||||
// get the text that we're about to delete
|
// Get the text that we're about to delete
|
||||||
nsresult res = mCharData->SubstringData(mOffset, mNumCharsToDelete,
|
nsresult res = mCharData->SubstringData(mOffset, mNumCharsToDelete,
|
||||||
mDeletedText);
|
mDeletedText);
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(res));
|
MOZ_ASSERT(NS_SUCCEEDED(res));
|
||||||
@ -81,27 +67,25 @@ DeleteTextTxn::DoTransaction()
|
|||||||
mRangeUpdater->SelAdjDeleteText(mCharData, mOffset, mNumCharsToDelete);
|
mRangeUpdater->SelAdjDeleteText(mCharData, mOffset, mNumCharsToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
// only set selection to deletion point if editor gives permission
|
// Only set selection to deletion point if editor gives permission
|
||||||
bool bAdjustSelection;
|
if (mEditor.GetShouldTxnSetSelection()) {
|
||||||
mEditor->ShouldTxnSetSelection(&bAdjustSelection);
|
nsRefPtr<Selection> selection = mEditor.GetSelection();
|
||||||
if (bAdjustSelection) {
|
|
||||||
nsRefPtr<Selection> selection = mEditor->GetSelection();
|
|
||||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||||
res = selection->Collapse(mCharData, mOffset);
|
res = selection->Collapse(mCharData, mOffset);
|
||||||
NS_ASSERTION(NS_SUCCEEDED(res),
|
NS_ASSERTION(NS_SUCCEEDED(res),
|
||||||
"selection could not be collapsed after undo of deletetext.");
|
"Selection could not be collapsed after undo of deletetext");
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
}
|
}
|
||||||
// else do nothing - dom range gravity will adjust selection
|
// Else do nothing - DOM Range gravity will adjust selection
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
//XXX: we may want to store the selection state and restore it properly
|
//XXX: We may want to store the selection state and restore it properly. Was
|
||||||
// was it an insertion point or an extended selection?
|
// it an insertion point or an extended selection?
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
DeleteTextTxn::UndoTransaction()
|
DeleteTextTxn::UndoTransaction()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mEditor && mCharData);
|
MOZ_ASSERT(mCharData);
|
||||||
|
|
||||||
return mCharData->InsertData(mOffset, mDeletedText);
|
return mCharData->InsertData(mOffset, mDeletedText);
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,17 @@
|
|||||||
#include "EditTxn.h"
|
#include "EditTxn.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
|
#include "nsGenericDOMDataNode.h"
|
||||||
#include "nsID.h"
|
#include "nsID.h"
|
||||||
#include "nsIDOMCharacterData.h"
|
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nscore.h"
|
#include "nscore.h"
|
||||||
|
|
||||||
class nsEditor;
|
class nsEditor;
|
||||||
class nsRangeUpdater;
|
class nsRangeUpdater;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transaction that removes text from a content node.
|
* A transaction that removes text from a content node.
|
||||||
*/
|
*/
|
||||||
@ -29,13 +32,13 @@ public:
|
|||||||
* @param aOffset the location in aElement to begin the deletion
|
* @param aOffset the location in aElement to begin the deletion
|
||||||
* @param aNumCharsToDelete the number of characters to delete. Not the number of bytes!
|
* @param aNumCharsToDelete the number of characters to delete. Not the number of bytes!
|
||||||
*/
|
*/
|
||||||
NS_IMETHOD Init(nsEditor* aEditor,
|
DeleteTextTxn(nsEditor& aEditor,
|
||||||
nsIDOMCharacterData* aCharData,
|
nsGenericDOMDataNode& aCharData,
|
||||||
uint32_t aOffset,
|
uint32_t aOffset,
|
||||||
uint32_t aNumCharsToDelete,
|
uint32_t aNumCharsToDelete,
|
||||||
nsRangeUpdater* aRangeUpdater);
|
nsRangeUpdater* aRangeUpdater);
|
||||||
|
|
||||||
DeleteTextTxn();
|
nsresult Init();
|
||||||
|
|
||||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DeleteTextTxn, EditTxn)
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DeleteTextTxn, EditTxn)
|
||||||
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
|
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
|
||||||
@ -49,10 +52,10 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
|
|
||||||
/** the provider of basic editing operations */
|
/** the provider of basic editing operations */
|
||||||
nsEditor* mEditor;
|
nsEditor& mEditor;
|
||||||
|
|
||||||
/** the CharacterData node to operate upon */
|
/** the CharacterData node to operate upon */
|
||||||
nsCOMPtr<nsIDOMCharacterData> mCharData;
|
nsRefPtr<nsGenericDOMDataNode> mCharData;
|
||||||
|
|
||||||
/** the offset into mCharData where the deletion is to take place */
|
/** the offset into mCharData where the deletion is to take place */
|
||||||
uint32_t mOffset;
|
uint32_t mOffset;
|
||||||
@ -67,4 +70,7 @@ protected:
|
|||||||
nsRangeUpdater* mRangeUpdater;
|
nsRangeUpdater* mRangeUpdater;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
#include <stdio.h> // for printf
|
|
||||||
|
|
||||||
#include "InsertElementTxn.h"
|
|
||||||
#include "nsAString.h"
|
|
||||||
#include "nsDebug.h" // for NS_ENSURE_TRUE, etc
|
|
||||||
#include "nsEditor.h" // for nsEditor
|
|
||||||
#include "nsError.h" // for NS_ERROR_NULL_POINTER, etc
|
|
||||||
#include "nsIContent.h" // for nsIContent
|
|
||||||
#include "nsINode.h" // for nsINode
|
|
||||||
#include "nsISelection.h" // for nsISelection
|
|
||||||
#include "nsMemory.h" // for nsMemory
|
|
||||||
#include "nsReadableUtils.h" // for ToNewCString
|
|
||||||
#include "nsString.h" // for nsString
|
|
||||||
|
|
||||||
using namespace mozilla;
|
|
||||||
|
|
||||||
InsertElementTxn::InsertElementTxn()
|
|
||||||
: EditTxn()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertElementTxn::~InsertElementTxn()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertElementTxn, EditTxn,
|
|
||||||
mNode,
|
|
||||||
mParent)
|
|
||||||
|
|
||||||
NS_IMPL_ADDREF_INHERITED(InsertElementTxn, EditTxn)
|
|
||||||
NS_IMPL_RELEASE_INHERITED(InsertElementTxn, EditTxn)
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertElementTxn)
|
|
||||||
NS_INTERFACE_MAP_END_INHERITING(EditTxn)
|
|
||||||
|
|
||||||
NS_IMETHODIMP InsertElementTxn::Init(nsIDOMNode *aNode,
|
|
||||||
nsIDOMNode *aParent,
|
|
||||||
int32_t aOffset,
|
|
||||||
nsIEditor *aEditor)
|
|
||||||
{
|
|
||||||
NS_ASSERTION(aNode && aParent && aEditor, "bad arg");
|
|
||||||
NS_ENSURE_TRUE(aNode && aParent && aEditor, NS_ERROR_NULL_POINTER);
|
|
||||||
|
|
||||||
mNode = do_QueryInterface(aNode);
|
|
||||||
mParent = do_QueryInterface(aParent);
|
|
||||||
mOffset = aOffset;
|
|
||||||
mEditor = aEditor;
|
|
||||||
NS_ENSURE_TRUE(mNode && mParent && mEditor, NS_ERROR_INVALID_ARG);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NS_IMETHODIMP InsertElementTxn::DoTransaction(void)
|
|
||||||
{
|
|
||||||
NS_ENSURE_TRUE(mNode && mParent, NS_ERROR_NOT_INITIALIZED);
|
|
||||||
|
|
||||||
nsCOMPtr<nsINode> parent = do_QueryInterface(mParent);
|
|
||||||
NS_ENSURE_STATE(parent);
|
|
||||||
|
|
||||||
uint32_t count = parent->GetChildCount();
|
|
||||||
if (mOffset > int32_t(count) || mOffset == -1) {
|
|
||||||
// -1 is sentinel value meaning "append at end"
|
|
||||||
mOffset = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// note, it's ok for refContent to be null. that means append
|
|
||||||
nsCOMPtr<nsIContent> refContent = parent->GetChildAt(mOffset);
|
|
||||||
nsCOMPtr<nsIDOMNode> refNode = refContent ? refContent->AsDOMNode() : nullptr;
|
|
||||||
|
|
||||||
mEditor->MarkNodeDirty(mNode);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMNode> resultNode;
|
|
||||||
nsresult result = mParent->InsertBefore(mNode, refNode, getter_AddRefs(resultNode));
|
|
||||||
NS_ENSURE_SUCCESS(result, result);
|
|
||||||
NS_ENSURE_TRUE(resultNode, NS_ERROR_NULL_POINTER);
|
|
||||||
|
|
||||||
// only set selection to insertion point if editor gives permission
|
|
||||||
bool bAdjustSelection;
|
|
||||||
mEditor->ShouldTxnSetSelection(&bAdjustSelection);
|
|
||||||
if (bAdjustSelection)
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsISelection> selection;
|
|
||||||
result = mEditor->GetSelection(getter_AddRefs(selection));
|
|
||||||
NS_ENSURE_SUCCESS(result, result);
|
|
||||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
|
||||||
// place the selection just after the inserted element
|
|
||||||
selection->Collapse(mParent, mOffset+1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// do nothing - dom range gravity will adjust selection
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP InsertElementTxn::UndoTransaction(void)
|
|
||||||
{
|
|
||||||
NS_ENSURE_TRUE(mNode && mParent, NS_ERROR_NOT_INITIALIZED);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMNode> resultNode;
|
|
||||||
return mParent->RemoveChild(mNode, getter_AddRefs(resultNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP InsertElementTxn::GetTxnDescription(nsAString& aString)
|
|
||||||
{
|
|
||||||
aString.AssignLiteral("InsertElementTxn");
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
93
editor/libeditor/InsertNodeTxn.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#include <stdio.h> // for printf
|
||||||
|
|
||||||
|
#include "mozilla/dom/Selection.h" // for Selection
|
||||||
|
|
||||||
|
#include "InsertNodeTxn.h"
|
||||||
|
#include "nsAString.h"
|
||||||
|
#include "nsDebug.h" // for NS_ENSURE_TRUE, etc
|
||||||
|
#include "nsEditor.h" // for nsEditor
|
||||||
|
#include "nsError.h" // for NS_ERROR_NULL_POINTER, etc
|
||||||
|
#include "nsIContent.h" // for nsIContent
|
||||||
|
#include "nsMemory.h" // for nsMemory
|
||||||
|
#include "nsReadableUtils.h" // for ToNewCString
|
||||||
|
#include "nsString.h" // for nsString
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
|
InsertNodeTxn::InsertNodeTxn(nsIContent& aNode, nsINode& aParent,
|
||||||
|
int32_t aOffset, nsEditor& aEditor)
|
||||||
|
: EditTxn()
|
||||||
|
, mNode(&aNode)
|
||||||
|
, mParent(&aParent)
|
||||||
|
, mOffset(aOffset)
|
||||||
|
, mEditor(aEditor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertNodeTxn::~InsertNodeTxn()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTxn, EditTxn,
|
||||||
|
mNode,
|
||||||
|
mParent)
|
||||||
|
|
||||||
|
NS_IMPL_ADDREF_INHERITED(InsertNodeTxn, EditTxn)
|
||||||
|
NS_IMPL_RELEASE_INHERITED(InsertNodeTxn, EditTxn)
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertNodeTxn)
|
||||||
|
NS_INTERFACE_MAP_END_INHERITING(EditTxn)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
InsertNodeTxn::DoTransaction()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mNode && mParent);
|
||||||
|
|
||||||
|
uint32_t count = mParent->GetChildCount();
|
||||||
|
if (mOffset > static_cast<int32_t>(count) || mOffset == -1) {
|
||||||
|
// -1 is sentinel value meaning "append at end"
|
||||||
|
mOffset = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note, it's ok for ref to be null. That means append.
|
||||||
|
nsCOMPtr<nsIContent> ref = mParent->GetChildAt(mOffset);
|
||||||
|
|
||||||
|
mEditor.MarkNodeDirty(GetAsDOMNode(mNode));
|
||||||
|
|
||||||
|
ErrorResult rv;
|
||||||
|
mParent->InsertBefore(*mNode, ref, rv);
|
||||||
|
NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
|
||||||
|
|
||||||
|
// Only set selection to insertion point if editor gives permission
|
||||||
|
if (mEditor.GetShouldTxnSetSelection()) {
|
||||||
|
nsRefPtr<Selection> selection = mEditor.GetSelection();
|
||||||
|
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||||
|
// Place the selection just after the inserted element
|
||||||
|
selection->Collapse(mParent, mOffset + 1);
|
||||||
|
} else {
|
||||||
|
// Do nothing - DOM Range gravity will adjust selection
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
InsertNodeTxn::UndoTransaction()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mNode && mParent);
|
||||||
|
|
||||||
|
ErrorResult rv;
|
||||||
|
mParent->RemoveChild(*mNode, rv);
|
||||||
|
return rv.ErrorCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
InsertNodeTxn::GetTxnDescription(nsAString& aString)
|
||||||
|
{
|
||||||
|
aString.AssignLiteral("InsertNodeTxn");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
@ -3,22 +3,24 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#ifndef InsertElementTxn_h__
|
#ifndef InsertNodeTxn_h__
|
||||||
#define InsertElementTxn_h__
|
#define InsertNodeTxn_h__
|
||||||
|
|
||||||
#include "EditTxn.h" // for EditTxn, NS_DECL_EDITTXN
|
#include "EditTxn.h" // for EditTxn, NS_DECL_EDITTXN
|
||||||
#include "nsCOMPtr.h" // for nsCOMPtr
|
#include "nsCOMPtr.h" // for nsCOMPtr
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
#include "nsIDOMNode.h" // for nsIDOMNode
|
#include "nsIContent.h" // for nsIContent
|
||||||
#include "nsISupportsImpl.h" // for NS_DECL_ISUPPORTS_INHERITED
|
#include "nsISupportsImpl.h" // for NS_DECL_ISUPPORTS_INHERITED
|
||||||
#include "nscore.h" // for NS_IMETHOD
|
|
||||||
|
|
||||||
class nsIEditor;
|
class nsEditor;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transaction that inserts a single element
|
* A transaction that inserts a single element
|
||||||
*/
|
*/
|
||||||
class InsertElementTxn : public EditTxn
|
class InsertNodeTxn : public EditTxn
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** initialize the transaction.
|
/** initialize the transaction.
|
||||||
@ -26,32 +28,31 @@ public:
|
|||||||
* @param aParent the node to insert into
|
* @param aParent the node to insert into
|
||||||
* @param aOffset the offset in aParent to insert aNode
|
* @param aOffset the offset in aParent to insert aNode
|
||||||
*/
|
*/
|
||||||
NS_IMETHOD Init(nsIDOMNode *aNode,
|
InsertNodeTxn(nsIContent& aNode, nsINode& aParent, int32_t aOffset,
|
||||||
nsIDOMNode *aParent,
|
nsEditor& aEditor);
|
||||||
int32_t aOffset,
|
|
||||||
nsIEditor *aEditor);
|
|
||||||
|
|
||||||
InsertElementTxn();
|
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS_INHERITED
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InsertElementTxn, EditTxn)
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InsertNodeTxn, EditTxn)
|
||||||
|
|
||||||
NS_DECL_EDITTXN
|
NS_DECL_EDITTXN
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~InsertElementTxn();
|
virtual ~InsertNodeTxn();
|
||||||
|
|
||||||
/** the element to insert */
|
/** the element to insert */
|
||||||
nsCOMPtr<nsIDOMNode> mNode;
|
nsCOMPtr<nsIContent> mNode;
|
||||||
|
|
||||||
/** the node into which the new node will be inserted */
|
/** the node into which the new node will be inserted */
|
||||||
nsCOMPtr<nsIDOMNode> mParent;
|
nsCOMPtr<nsINode> mParent;
|
||||||
|
|
||||||
/** the editor for this transaction */
|
|
||||||
nsIEditor* mEditor;
|
|
||||||
|
|
||||||
/** the index in mParent for the new node */
|
/** the index in mParent for the new node */
|
||||||
int32_t mOffset;
|
int32_t mOffset;
|
||||||
|
|
||||||
|
/** the editor for this transaction */
|
||||||
|
nsEditor& mEditor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -16,7 +16,7 @@ UNIFIED_SOURCES += [
|
|||||||
'EditAggregateTxn.cpp',
|
'EditAggregateTxn.cpp',
|
||||||
'EditTxn.cpp',
|
'EditTxn.cpp',
|
||||||
'IMETextTxn.cpp',
|
'IMETextTxn.cpp',
|
||||||
'InsertElementTxn.cpp',
|
'InsertNodeTxn.cpp',
|
||||||
'InsertTextTxn.cpp',
|
'InsertTextTxn.cpp',
|
||||||
'JoinElementTxn.cpp',
|
'JoinElementTxn.cpp',
|
||||||
'nsEditor.cpp',
|
'nsEditor.cpp',
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#include "EditAggregateTxn.h" // for EditAggregateTxn
|
#include "EditAggregateTxn.h" // for EditAggregateTxn
|
||||||
#include "EditTxn.h" // for EditTxn
|
#include "EditTxn.h" // for EditTxn
|
||||||
#include "IMETextTxn.h" // for IMETextTxn
|
#include "IMETextTxn.h" // for IMETextTxn
|
||||||
#include "InsertElementTxn.h" // for InsertElementTxn
|
#include "InsertNodeTxn.h" // for InsertNodeTxn
|
||||||
#include "InsertTextTxn.h" // for InsertTextTxn
|
#include "InsertTextTxn.h" // for InsertTextTxn
|
||||||
#include "JoinElementTxn.h" // for JoinElementTxn
|
#include "JoinElementTxn.h" // for JoinElementTxn
|
||||||
#include "PlaceholderTxn.h" // for PlaceholderTxn
|
#include "PlaceholderTxn.h" // for PlaceholderTxn
|
||||||
@ -1335,72 +1335,89 @@ NS_IMETHODIMP nsEditor::SetSpellcheckUserOverride(bool enable)
|
|||||||
return SyncRealTimeSpell();
|
return SyncRealTimeSpell();
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsEditor::CreateNode(const nsAString& aTag,
|
NS_IMETHODIMP
|
||||||
nsIDOMNode * aParent,
|
nsEditor::CreateNode(const nsAString& aTag,
|
||||||
int32_t aPosition,
|
nsIDOMNode* aParent,
|
||||||
nsIDOMNode ** aNewNode)
|
int32_t aPosition,
|
||||||
|
nsIDOMNode** aNewNode)
|
||||||
{
|
{
|
||||||
int32_t i;
|
nsCOMPtr<nsIAtom> tag = do_GetAtom(aTag);
|
||||||
|
nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
|
||||||
|
NS_ENSURE_STATE(parent);
|
||||||
|
*aNewNode = GetAsDOMNode(CreateNode(tag, parent, aPosition).take());
|
||||||
|
NS_ENSURE_STATE(*aNewNode);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<Element>
|
||||||
|
nsEditor::CreateNode(nsIAtom* aTag,
|
||||||
|
nsINode* aParent,
|
||||||
|
int32_t aPosition)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aTag && aParent);
|
||||||
|
|
||||||
nsAutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
|
nsAutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
|
||||||
|
|
||||||
for (i = 0; i < mActionListeners.Count(); i++)
|
for (int32_t i = 0; i < mActionListeners.Count(); i++) {
|
||||||
mActionListeners[i]->WillCreateNode(aTag, aParent, aPosition);
|
mActionListeners[i]->WillCreateNode(nsDependentAtomString(aTag),
|
||||||
|
GetAsDOMNode(aParent), aPosition);
|
||||||
|
}
|
||||||
|
|
||||||
nsRefPtr<CreateElementTxn> txn;
|
nsCOMPtr<Element> ret;
|
||||||
nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition,
|
|
||||||
getter_AddRefs(txn));
|
nsRefPtr<CreateElementTxn> txn =
|
||||||
if (NS_SUCCEEDED(result))
|
CreateTxnForCreateElement(*aTag, *aParent, aPosition);
|
||||||
{
|
nsresult res = DoTransaction(txn);
|
||||||
result = DoTransaction(txn);
|
if (NS_SUCCEEDED(res)) {
|
||||||
if (NS_SUCCEEDED(result))
|
ret = txn->GetNewNode();
|
||||||
{
|
MOZ_ASSERT(ret);
|
||||||
result = txn->GetNewNode(aNewNode);
|
|
||||||
NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::DoTransaction succeeded.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
|
mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
|
||||||
|
|
||||||
for (i = 0; i < mActionListeners.Count(); i++)
|
for (int32_t i = 0; i < mActionListeners.Count(); i++) {
|
||||||
mActionListeners[i]->DidCreateNode(aTag, *aNewNode, aParent, aPosition, result);
|
mActionListeners[i]->DidCreateNode(nsDependentAtomString(aTag),
|
||||||
|
GetAsDOMNode(ret),
|
||||||
return result;
|
GetAsDOMNode(aParent), aPosition,
|
||||||
}
|
res);
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsEditor::InsertNode(nsIContent* aContent, nsINode* aParent, int32_t aPosition)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aContent && aParent);
|
|
||||||
return InsertNode(GetAsDOMNode(aContent), GetAsDOMNode(aParent), aPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
|
|
||||||
nsIDOMNode * aParent,
|
|
||||||
int32_t aPosition)
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
|
|
||||||
|
|
||||||
for (i = 0; i < mActionListeners.Count(); i++)
|
|
||||||
mActionListeners[i]->WillInsertNode(aNode, aParent, aPosition);
|
|
||||||
|
|
||||||
nsRefPtr<InsertElementTxn> txn;
|
|
||||||
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
|
|
||||||
nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
|
|
||||||
nsresult result = CreateTxnForInsertElement(node->AsDOMNode(), parent->AsDOMNode(),
|
|
||||||
aPosition, getter_AddRefs(txn));
|
|
||||||
if (NS_SUCCEEDED(result)) {
|
|
||||||
result = DoTransaction(txn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mRangeUpdater.SelAdjInsertNode(aParent, aPosition);
|
return ret.forget();
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < mActionListeners.Count(); i++)
|
|
||||||
mActionListeners[i]->DidInsertNode(aNode, aParent, aPosition, result);
|
|
||||||
|
|
||||||
return result;
|
NS_IMETHODIMP
|
||||||
|
nsEditor::InsertNode(nsIDOMNode* aNode, nsIDOMNode* aParent, int32_t aPosition)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIContent> node = do_QueryInterface(aNode);
|
||||||
|
nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
|
||||||
|
NS_ENSURE_TRUE(node && parent, NS_ERROR_NULL_POINTER);
|
||||||
|
|
||||||
|
return InsertNode(*node, *parent, aPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsEditor::InsertNode(nsIContent& aNode, nsINode& aParent, int32_t aPosition)
|
||||||
|
{
|
||||||
|
nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < mActionListeners.Count(); i++) {
|
||||||
|
mActionListeners[i]->WillInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(),
|
||||||
|
aPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<InsertNodeTxn> txn = CreateTxnForInsertNode(aNode, aParent,
|
||||||
|
aPosition);
|
||||||
|
nsresult res = DoTransaction(txn);
|
||||||
|
|
||||||
|
mRangeUpdater.SelAdjInsertNode(aParent.AsDOMNode(), aPosition);
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < mActionListeners.Count(); i++) {
|
||||||
|
mActionListeners[i]->DidInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(),
|
||||||
|
aPosition, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1555,8 +1572,8 @@ nsEditor::ReplaceContainer(Element* aOldContainer,
|
|||||||
// notify our internal selection state listener
|
// notify our internal selection state listener
|
||||||
// (Note: A nsAutoSelectionReset object must be created
|
// (Note: A nsAutoSelectionReset object must be created
|
||||||
// before calling this to initialize mRangeUpdater)
|
// before calling this to initialize mRangeUpdater)
|
||||||
nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater,
|
AutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, aOldContainer,
|
||||||
aOldContainer->AsDOMNode(), ret->AsDOMNode());
|
ret);
|
||||||
{
|
{
|
||||||
nsAutoTxnsConserveSelection conserveSelection(this);
|
nsAutoTxnsConserveSelection conserveSelection(this);
|
||||||
while (aOldContainer->HasChildren()) {
|
while (aOldContainer->HasChildren()) {
|
||||||
@ -1565,13 +1582,13 @@ nsEditor::ReplaceContainer(Element* aOldContainer,
|
|||||||
res = DeleteNode(child);
|
res = DeleteNode(child);
|
||||||
NS_ENSURE_SUCCESS(res, nullptr);
|
NS_ENSURE_SUCCESS(res, nullptr);
|
||||||
|
|
||||||
res = InsertNode(child, ret, -1);
|
res = InsertNode(*child, *ret, -1);
|
||||||
NS_ENSURE_SUCCESS(res, nullptr);
|
NS_ENSURE_SUCCESS(res, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert new container into tree
|
// insert new container into tree
|
||||||
res = InsertNode(ret, parent, offset);
|
res = InsertNode(*ret, *parent, offset);
|
||||||
NS_ENSURE_SUCCESS(res, nullptr);
|
NS_ENSURE_SUCCESS(res, nullptr);
|
||||||
|
|
||||||
// delete old container
|
// delete old container
|
||||||
@ -1581,171 +1598,129 @@ nsEditor::ReplaceContainer(Element* aOldContainer,
|
|||||||
return ret.forget();
|
return ret.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// RemoveContainer: remove inNode, reparenting its children into their
|
// RemoveContainer: remove inNode, reparenting its children (if any) into the
|
||||||
// the parent of inNode
|
// parent of inNode
|
||||||
//
|
//
|
||||||
nsresult
|
nsresult
|
||||||
nsEditor::RemoveContainer(nsIDOMNode* aNode)
|
nsEditor::RemoveContainer(nsIContent* aNode)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
|
MOZ_ASSERT(aNode);
|
||||||
return RemoveContainer(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsEditor::RemoveContainer(nsINode* aNode)
|
|
||||||
{
|
|
||||||
NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
|
|
||||||
|
|
||||||
nsCOMPtr<nsINode> parent = aNode->GetParentNode();
|
nsCOMPtr<nsINode> parent = aNode->GetParentNode();
|
||||||
NS_ENSURE_STATE(parent);
|
NS_ENSURE_STATE(parent);
|
||||||
|
|
||||||
int32_t offset = parent->IndexOf(aNode);
|
int32_t offset = parent->IndexOf(aNode);
|
||||||
|
|
||||||
// loop through the child nodes of inNode and promote them
|
// Loop through the children of inNode and promote them into inNode's parent
|
||||||
// into inNode's parent.
|
|
||||||
uint32_t nodeOrigLen = aNode->GetChildCount();
|
uint32_t nodeOrigLen = aNode->GetChildCount();
|
||||||
|
|
||||||
// notify our internal selection state listener
|
// notify our internal selection state listener
|
||||||
nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode, parent, offset, nodeOrigLen);
|
nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode, parent,
|
||||||
|
offset, nodeOrigLen);
|
||||||
|
|
||||||
while (aNode->HasChildren()) {
|
while (aNode->HasChildren()) {
|
||||||
nsCOMPtr<nsIContent> child = aNode->GetLastChild();
|
nsCOMPtr<nsIContent> child = aNode->GetLastChild();
|
||||||
nsresult rv = DeleteNode(child->AsDOMNode());
|
nsresult rv = DeleteNode(child);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = InsertNode(child->AsDOMNode(), parent->AsDOMNode(), offset);
|
rv = InsertNode(*child, *parent, offset);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DeleteNode(aNode->AsDOMNode());
|
return DeleteNode(aNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// InsertContainerAbove: insert a new parent for inNode, returned in outNode,
|
// InsertContainerAbove: Insert a new parent for inNode, which is contructed to
|
||||||
// which is contructed to be of type aNodeType. outNode becomes
|
// be of type aNodeType. outNode becomes a child of
|
||||||
// a child of inNode's earlier parent.
|
// inNode's earlier parent. Caller's responsibility to
|
||||||
// Callers responsibility to make sure inNode's can be child
|
// make sure inNode's can be child of outNode, and
|
||||||
// of outNode, and outNode can be child of old parent.
|
// outNode can be child of old parent.
|
||||||
nsresult
|
already_AddRefed<Element>
|
||||||
nsEditor::InsertContainerAbove( nsIDOMNode *inNode,
|
|
||||||
nsCOMPtr<nsIDOMNode> *outNode,
|
|
||||||
const nsAString &aNodeType,
|
|
||||||
const nsAString *aAttribute,
|
|
||||||
const nsAString *aValue)
|
|
||||||
{
|
|
||||||
NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIContent> node = do_QueryInterface(inNode);
|
|
||||||
NS_ENSURE_STATE(node);
|
|
||||||
|
|
||||||
nsCOMPtr<dom::Element> element;
|
|
||||||
nsresult rv = InsertContainerAbove(node, getter_AddRefs(element), aNodeType,
|
|
||||||
aAttribute, aValue);
|
|
||||||
*outNode = element ? element->AsDOMNode() : nullptr;
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsEditor::InsertContainerAbove(nsIContent* aNode,
|
nsEditor::InsertContainerAbove(nsIContent* aNode,
|
||||||
dom::Element** aOutNode,
|
nsIAtom* aNodeType,
|
||||||
const nsAString& aNodeType,
|
nsIAtom* aAttribute,
|
||||||
const nsAString* aAttribute,
|
|
||||||
const nsAString* aValue)
|
const nsAString* aValue)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aNode);
|
MOZ_ASSERT(aNode && aNodeType);
|
||||||
|
|
||||||
nsCOMPtr<nsIContent> parent = aNode->GetParent();
|
nsCOMPtr<nsIContent> parent = aNode->GetParent();
|
||||||
NS_ENSURE_STATE(parent);
|
NS_ENSURE_TRUE(parent, nullptr);
|
||||||
int32_t offset = parent->IndexOf(aNode);
|
int32_t offset = parent->IndexOf(aNode);
|
||||||
|
|
||||||
// create new container
|
// Create new container
|
||||||
nsCOMPtr<Element> newContent =
|
nsCOMPtr<Element> newContent = CreateHTMLContent(aNodeType);
|
||||||
CreateHTMLContent(nsCOMPtr<nsIAtom>(do_GetAtom(aNodeType)));
|
NS_ENSURE_TRUE(newContent, nullptr);
|
||||||
NS_ENSURE_STATE(newContent);
|
|
||||||
|
|
||||||
// set attribute if needed
|
// Set attribute if needed
|
||||||
nsresult res;
|
nsresult res;
|
||||||
if (aAttribute && aValue && !aAttribute->IsEmpty()) {
|
if (aAttribute && aValue && aAttribute != nsGkAtoms::_empty) {
|
||||||
nsIDOMNode* elem = newContent->AsDOMNode();
|
res = newContent->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
|
||||||
res = static_cast<nsIDOMElement*>(elem)->SetAttribute(*aAttribute, *aValue);
|
NS_ENSURE_SUCCESS(res, nullptr);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify our internal selection state listener
|
// Notify our internal selection state listener
|
||||||
nsAutoInsertContainerSelNotify selNotify(mRangeUpdater);
|
nsAutoInsertContainerSelNotify selNotify(mRangeUpdater);
|
||||||
|
|
||||||
// put inNode in new parent, outNode
|
// Put inNode in new parent, outNode
|
||||||
res = DeleteNode(aNode->AsDOMNode());
|
res = DeleteNode(aNode);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, nullptr);
|
||||||
|
|
||||||
{
|
{
|
||||||
nsAutoTxnsConserveSelection conserveSelection(this);
|
nsAutoTxnsConserveSelection conserveSelection(this);
|
||||||
res = InsertNode(aNode->AsDOMNode(), newContent->AsDOMNode(), 0);
|
res = InsertNode(*aNode, *newContent, 0);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// put new parent in doc
|
// Put new parent in doc
|
||||||
res = InsertNode(newContent->AsDOMNode(), parent->AsDOMNode(), offset);
|
res = InsertNode(*newContent, *parent, offset);
|
||||||
newContent.forget(aOutNode);
|
NS_ENSURE_SUCCESS(res, nullptr);
|
||||||
return res;
|
|
||||||
|
return newContent.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// MoveNode: move aNode to {aParent,aOffset}
|
// MoveNode: move aNode to {aParent,aOffset}
|
||||||
nsresult
|
nsresult
|
||||||
nsEditor::MoveNode(nsIDOMNode* aNode, nsIDOMNode* aParent, int32_t aOffset)
|
nsEditor::MoveNode(nsIContent* aNode, nsINode* aParent, int32_t aOffset)
|
||||||
{
|
|
||||||
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
|
|
||||||
NS_ENSURE_STATE(node);
|
|
||||||
|
|
||||||
nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
|
|
||||||
NS_ENSURE_STATE(parent);
|
|
||||||
|
|
||||||
return MoveNode(node, parent, aOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsEditor::MoveNode(nsINode* aNode, nsINode* aParent, int32_t aOffset)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aNode);
|
MOZ_ASSERT(aNode);
|
||||||
MOZ_ASSERT(aParent);
|
MOZ_ASSERT(aParent);
|
||||||
MOZ_ASSERT(aOffset == -1 ||
|
MOZ_ASSERT(aOffset == -1 ||
|
||||||
(0 <= aOffset && SafeCast<uint32_t>(aOffset) <= aParent->Length()));
|
(0 <= aOffset && SafeCast<uint32_t>(aOffset) <= aParent->Length()));
|
||||||
|
|
||||||
int32_t oldOffset;
|
nsCOMPtr<nsINode> oldParent = aNode->GetParentNode();
|
||||||
nsCOMPtr<nsINode> oldParent = GetNodeLocation(aNode, &oldOffset);
|
int32_t oldOffset = oldParent ? oldParent->IndexOf(aNode) : -1;
|
||||||
|
|
||||||
if (aOffset == -1) {
|
if (aOffset == -1) {
|
||||||
// Magic value meaning "move to end of aParent".
|
// Magic value meaning "move to end of aParent"
|
||||||
aOffset = SafeCast<int32_t>(aParent->Length());
|
aOffset = SafeCast<int32_t>(aParent->Length());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't do anything if it's already in right place.
|
// Don't do anything if it's already in right place
|
||||||
if (aParent == oldParent && aOffset == oldOffset) {
|
if (aParent == oldParent && aOffset == oldOffset) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify our internal selection state listener.
|
// Notify our internal selection state listener
|
||||||
nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset,
|
nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset,
|
||||||
aParent, aOffset);
|
aParent, aOffset);
|
||||||
|
|
||||||
// Need to adjust aOffset if we are moving aNode further along in its current
|
// Need to adjust aOffset if we're moving aNode later in its current parent
|
||||||
// parent.
|
|
||||||
if (aParent == oldParent && oldOffset < aOffset) {
|
if (aParent == oldParent && oldOffset < aOffset) {
|
||||||
// This is because when we delete aNode, it will make the offsets after it
|
// When we delete aNode, it will make the offsets after it off by one
|
||||||
// off by one.
|
|
||||||
aOffset--;
|
aOffset--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hold a reference so aNode doesn't go away when we remove it (bug 772282).
|
// Hold a reference so aNode doesn't go away when we remove it (bug 772282)
|
||||||
nsCOMPtr<nsINode> kungFuDeathGrip = aNode;
|
nsCOMPtr<nsINode> kungFuDeathGrip = aNode;
|
||||||
|
|
||||||
nsresult rv = DeleteNode(aNode);
|
nsresult rv = DeleteNode(aNode);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return InsertNode(aNode->AsDOMNode(), aParent->AsDOMNode(), aOffset);
|
return InsertNode(*aNode, *aParent, aOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2592,46 +2567,47 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsAString & aStringToInsert
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
|
nsresult
|
||||||
uint32_t aOffset,
|
nsEditor::DeleteText(nsGenericDOMDataNode& aCharData, uint32_t aOffset,
|
||||||
uint32_t aLength)
|
uint32_t aLength)
|
||||||
{
|
{
|
||||||
nsRefPtr<DeleteTextTxn> txn;
|
nsRefPtr<DeleteTextTxn> txn =
|
||||||
nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength,
|
CreateTxnForDeleteText(aCharData, aOffset, aLength);
|
||||||
getter_AddRefs(txn));
|
NS_ENSURE_STATE(txn);
|
||||||
|
|
||||||
nsAutoRules beginRulesSniffing(this, EditAction::deleteText, nsIEditor::ePrevious);
|
nsAutoRules beginRulesSniffing(this, EditAction::deleteText, nsIEditor::ePrevious);
|
||||||
if (NS_SUCCEEDED(result))
|
|
||||||
{
|
// Let listeners know what's up
|
||||||
// let listeners know what's up
|
for (int32_t i = 0; i < mActionListeners.Count(); i++) {
|
||||||
int32_t i;
|
mActionListeners[i]->WillDeleteText(
|
||||||
for (i = 0; i < mActionListeners.Count(); i++)
|
static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset,
|
||||||
mActionListeners[i]->WillDeleteText(aElement, aOffset, aLength);
|
aLength);
|
||||||
|
|
||||||
result = DoTransaction(txn);
|
|
||||||
|
|
||||||
// let listeners know what happened
|
|
||||||
for (i = 0; i < mActionListeners.Count(); i++)
|
|
||||||
mActionListeners[i]->DidDeleteText(aElement, aOffset, aLength, result);
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
|
nsresult res = DoTransaction(txn);
|
||||||
|
|
||||||
|
// Let listeners know what happened
|
||||||
|
for (int32_t i = 0; i < mActionListeners.Count(); i++) {
|
||||||
|
mActionListeners[i]->DidDeleteText(
|
||||||
|
static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset,
|
||||||
|
aLength, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
already_AddRefed<DeleteTextTxn>
|
||||||
nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData* aElement,
|
nsEditor::CreateTxnForDeleteText(nsGenericDOMDataNode& aCharData,
|
||||||
uint32_t aOffset,
|
uint32_t aOffset, uint32_t aLength)
|
||||||
uint32_t aLength,
|
|
||||||
DeleteTextTxn** aTxn)
|
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
|
nsRefPtr<DeleteTextTxn> txn =
|
||||||
|
new DeleteTextTxn(*this, aCharData, aOffset, aLength, &mRangeUpdater);
|
||||||
|
|
||||||
nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
|
nsresult res = txn->Init();
|
||||||
|
NS_ENSURE_SUCCESS(res, nullptr);
|
||||||
|
|
||||||
nsresult res = txn->Init(this, aElement, aOffset, aLength, &mRangeUpdater);
|
return txn.forget();
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
|
||||||
|
|
||||||
txn.forget(aTxn);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4146,6 +4122,8 @@ NS_IMETHODIMP
|
|||||||
nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
|
nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
|
||||||
nsIDOMNode ** aNewNode)
|
nsIDOMNode ** aNewNode)
|
||||||
{
|
{
|
||||||
|
nsCOMPtr<nsIAtom> tag = do_GetAtom(aTag);
|
||||||
|
|
||||||
nsresult result = DeleteSelectionAndPrepareToCreateNode();
|
nsresult result = DeleteSelectionAndPrepareToCreateNode();
|
||||||
NS_ENSURE_SUCCESS(result, result);
|
NS_ENSURE_SUCCESS(result, result);
|
||||||
|
|
||||||
@ -4156,12 +4134,9 @@ nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
|
|||||||
uint32_t offset = selection->AnchorOffset();
|
uint32_t offset = selection->AnchorOffset();
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMNode> newNode;
|
nsCOMPtr<nsIDOMNode> newNode;
|
||||||
result = CreateNode(aTag, node->AsDOMNode(), offset,
|
*aNewNode = GetAsDOMNode(CreateNode(tag, node, offset).take());
|
||||||
getter_AddRefs(newNode));
|
|
||||||
// XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly
|
// XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly
|
||||||
// in success/failure cases
|
// in success/failure cases
|
||||||
*aNewNode = newNode;
|
|
||||||
NS_IF_ADDREF(*aNewNode);
|
|
||||||
|
|
||||||
// we want the selection to be just after the new node
|
// we want the selection to be just after the new node
|
||||||
return selection->Collapse(node, offset + 1);
|
return selection->Collapse(node, offset + 1);
|
||||||
@ -4317,41 +4292,26 @@ nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsAString& aTag,
|
already_AddRefed<CreateElementTxn>
|
||||||
nsIDOMNode *aParent,
|
nsEditor::CreateTxnForCreateElement(nsIAtom& aTag,
|
||||||
int32_t aPosition,
|
nsINode& aParent,
|
||||||
CreateElementTxn ** aTxn)
|
int32_t aPosition)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER);
|
nsRefPtr<CreateElementTxn> txn =
|
||||||
|
new CreateElementTxn(*this, aTag, aParent, aPosition);
|
||||||
|
|
||||||
nsRefPtr<CreateElementTxn> txn = new CreateElementTxn();
|
return txn.forget();
|
||||||
|
|
||||||
nsresult rv = txn->Init(this, aTag, aParent, aPosition);
|
|
||||||
if (NS_SUCCEEDED(rv))
|
|
||||||
{
|
|
||||||
txn.forget(aTxn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode,
|
already_AddRefed<InsertNodeTxn>
|
||||||
nsIDOMNode * aParent,
|
nsEditor::CreateTxnForInsertNode(nsIContent& aNode,
|
||||||
int32_t aPosition,
|
nsINode& aParent,
|
||||||
InsertElementTxn ** aTxn)
|
int32_t aPosition)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(aNode && aParent, NS_ERROR_NULL_POINTER);
|
nsRefPtr<InsertNodeTxn> txn = new InsertNodeTxn(aNode, aParent, aPosition,
|
||||||
|
*this);
|
||||||
nsRefPtr<InsertElementTxn> txn = new InsertElementTxn();
|
return txn.forget();
|
||||||
|
|
||||||
nsresult rv = txn->Init(aNode, aParent, aPosition, this);
|
|
||||||
if (NS_SUCCEEDED(rv))
|
|
||||||
{
|
|
||||||
txn.forget(aTxn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -4466,25 +4426,23 @@ nsEditor::CreateTxnForDeleteSelection(EDirection aAction,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
already_AddRefed<DeleteTextTxn>
|
||||||
nsEditor::CreateTxnForDeleteCharacter(nsIDOMCharacterData* aData,
|
nsEditor::CreateTxnForDeleteCharacter(nsGenericDOMDataNode& aData,
|
||||||
uint32_t aOffset,
|
uint32_t aOffset, EDirection aDirection)
|
||||||
EDirection aDirection,
|
|
||||||
DeleteTextTxn** aTxn)
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aDirection == eNext || aDirection == ePrevious,
|
NS_ASSERTION(aDirection == eNext || aDirection == ePrevious,
|
||||||
"invalid direction");
|
"Invalid direction");
|
||||||
nsAutoString data;
|
nsAutoString data;
|
||||||
aData->GetData(data);
|
aData.GetData(data);
|
||||||
NS_ASSERTION(data.Length(), "Trying to delete from a zero-length node");
|
NS_ASSERTION(data.Length(), "Trying to delete from a zero-length node");
|
||||||
NS_ENSURE_STATE(data.Length());
|
NS_ENSURE_TRUE(data.Length(), nullptr);
|
||||||
|
|
||||||
uint32_t segOffset = aOffset, segLength = 1;
|
uint32_t segOffset = aOffset, segLength = 1;
|
||||||
if (aDirection == eNext) {
|
if (aDirection == eNext) {
|
||||||
if (segOffset + 1 < data.Length() &&
|
if (segOffset + 1 < data.Length() &&
|
||||||
NS_IS_HIGH_SURROGATE(data[segOffset]) &&
|
NS_IS_HIGH_SURROGATE(data[segOffset]) &&
|
||||||
NS_IS_LOW_SURROGATE(data[segOffset+1])) {
|
NS_IS_LOW_SURROGATE(data[segOffset+1])) {
|
||||||
// delete both halves of the surrogate pair
|
// Delete both halves of the surrogate pair
|
||||||
++segLength;
|
++segLength;
|
||||||
}
|
}
|
||||||
} else if (aOffset > 0) {
|
} else if (aOffset > 0) {
|
||||||
@ -4496,9 +4454,9 @@ nsEditor::CreateTxnForDeleteCharacter(nsIDOMCharacterData* aData,
|
|||||||
--segOffset;
|
--segOffset;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return NS_ERROR_FAILURE;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return CreateTxnForDeleteText(aData, segOffset, segLength, aTxn);
|
return CreateTxnForDeleteText(aData, segOffset, segLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
|
//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
|
||||||
@ -4523,7 +4481,6 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsRange* aRange,
|
|||||||
|
|
||||||
// determine if the insertion point is at the beginning, middle, or end of
|
// determine if the insertion point is at the beginning, middle, or end of
|
||||||
// the node
|
// the node
|
||||||
nsCOMPtr<nsIDOMCharacterData> nodeAsCharData = do_QueryInterface(node);
|
|
||||||
|
|
||||||
uint32_t count = node->Length();
|
uint32_t count = node->Length();
|
||||||
|
|
||||||
@ -4543,16 +4500,15 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsRange* aRange,
|
|||||||
|
|
||||||
// there is a priorNode, so delete its last child (if chardata, delete the
|
// there is a priorNode, so delete its last child (if chardata, delete the
|
||||||
// last char). if it has no children, delete it
|
// last char). if it has no children, delete it
|
||||||
nsCOMPtr<nsIDOMCharacterData> priorNodeAsCharData =
|
if (priorNode->IsNodeOfType(nsINode::eDATA_NODE)) {
|
||||||
do_QueryInterface(priorNode);
|
nsRefPtr<nsGenericDOMDataNode> priorNodeAsCharData =
|
||||||
if (priorNodeAsCharData) {
|
static_cast<nsGenericDOMDataNode*>(priorNode.get());
|
||||||
uint32_t length = priorNode->Length();
|
uint32_t length = priorNode->Length();
|
||||||
// Bail out for empty chardata XXX: Do we want to do something else?
|
// Bail out for empty chardata XXX: Do we want to do something else?
|
||||||
NS_ENSURE_STATE(length);
|
NS_ENSURE_STATE(length);
|
||||||
nsRefPtr<DeleteTextTxn> txn;
|
nsRefPtr<DeleteTextTxn> txn =
|
||||||
res = CreateTxnForDeleteCharacter(priorNodeAsCharData, length,
|
CreateTxnForDeleteCharacter(*priorNodeAsCharData, length, ePrevious);
|
||||||
ePrevious, getter_AddRefs(txn));
|
NS_ENSURE_STATE(txn);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
|
||||||
|
|
||||||
*aOffset = txn->GetOffset();
|
*aOffset = txn->GetOffset();
|
||||||
*aLength = txn->GetNumCharsToDelete();
|
*aLength = txn->GetNumCharsToDelete();
|
||||||
@ -4579,16 +4535,15 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsRange* aRange,
|
|||||||
|
|
||||||
// there is a nextNode, so delete its first child (if chardata, delete the
|
// there is a nextNode, so delete its first child (if chardata, delete the
|
||||||
// first char). if it has no children, delete it
|
// first char). if it has no children, delete it
|
||||||
nsCOMPtr<nsIDOMCharacterData> nextNodeAsCharData =
|
if (nextNode->IsNodeOfType(nsINode::eDATA_NODE)) {
|
||||||
do_QueryInterface(nextNode);
|
nsRefPtr<nsGenericDOMDataNode> nextNodeAsCharData =
|
||||||
if (nextNodeAsCharData) {
|
static_cast<nsGenericDOMDataNode*>(nextNode.get());
|
||||||
uint32_t length = nextNode->Length();
|
uint32_t length = nextNode->Length();
|
||||||
// Bail out for empty chardata XXX: Do we want to do something else?
|
// Bail out for empty chardata XXX: Do we want to do something else?
|
||||||
NS_ENSURE_STATE(length);
|
NS_ENSURE_STATE(length);
|
||||||
nsRefPtr<DeleteTextTxn> txn;
|
nsRefPtr<DeleteTextTxn> txn =
|
||||||
res = CreateTxnForDeleteCharacter(nextNodeAsCharData, 0, eNext,
|
CreateTxnForDeleteCharacter(*nextNodeAsCharData, 0, eNext);
|
||||||
getter_AddRefs(txn));
|
NS_ENSURE_STATE(txn);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
|
||||||
|
|
||||||
*aOffset = txn->GetOffset();
|
*aOffset = txn->GetOffset();
|
||||||
*aLength = txn->GetNumCharsToDelete();
|
*aLength = txn->GetNumCharsToDelete();
|
||||||
@ -4606,12 +4561,13 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsRange* aRange,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeAsCharData) {
|
if (node->IsNodeOfType(nsINode::eDATA_NODE)) {
|
||||||
|
nsRefPtr<nsGenericDOMDataNode> nodeAsCharData =
|
||||||
|
static_cast<nsGenericDOMDataNode*>(node.get());
|
||||||
// we have chardata, so delete a char at the proper offset
|
// we have chardata, so delete a char at the proper offset
|
||||||
nsRefPtr<DeleteTextTxn> txn;
|
nsRefPtr<DeleteTextTxn> txn = CreateTxnForDeleteCharacter(*nodeAsCharData,
|
||||||
res = CreateTxnForDeleteCharacter(nodeAsCharData, offset, aAction,
|
offset, aAction);
|
||||||
getter_AddRefs(txn));
|
NS_ENSURE_STATE(txn);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
|
||||||
|
|
||||||
aTxn->AppendChild(txn);
|
aTxn->AppendChild(txn);
|
||||||
NS_ADDREF(*aNode = node);
|
NS_ADDREF(*aNode = node);
|
||||||
@ -4639,18 +4595,17 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsRange* aRange,
|
|||||||
}
|
}
|
||||||
NS_ENSURE_STATE(selectedNode);
|
NS_ENSURE_STATE(selectedNode);
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMCharacterData> selectedNodeAsCharData =
|
if (selectedNode->IsNodeOfType(nsINode::eDATA_NODE)) {
|
||||||
do_QueryInterface(selectedNode);
|
nsRefPtr<nsGenericDOMDataNode> selectedNodeAsCharData =
|
||||||
if (selectedNodeAsCharData) {
|
static_cast<nsGenericDOMDataNode*>(selectedNode.get());
|
||||||
// we are deleting from a chardata node, so do a character deletion
|
// we are deleting from a chardata node, so do a character deletion
|
||||||
uint32_t position = 0;
|
uint32_t position = 0;
|
||||||
if (aAction == ePrevious) {
|
if (aAction == ePrevious) {
|
||||||
position = selectedNode->Length();
|
position = selectedNode->Length();
|
||||||
}
|
}
|
||||||
nsRefPtr<DeleteTextTxn> delTextTxn;
|
nsRefPtr<DeleteTextTxn> delTextTxn =
|
||||||
res = CreateTxnForDeleteCharacter(selectedNodeAsCharData, position,
|
CreateTxnForDeleteCharacter(*selectedNodeAsCharData, position,
|
||||||
aAction, getter_AddRefs(delTextTxn));
|
aAction);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
|
||||||
NS_ENSURE_TRUE(delTextTxn, NS_ERROR_NULL_POINTER);
|
NS_ENSURE_TRUE(delTextTxn, NS_ERROR_NULL_POINTER);
|
||||||
|
|
||||||
aTxn->AppendChild(delTextTxn);
|
aTxn->AppendChild(delTextTxn);
|
||||||
|
@ -29,12 +29,9 @@
|
|||||||
|
|
||||||
class AddStyleSheetTxn;
|
class AddStyleSheetTxn;
|
||||||
class ChangeAttributeTxn;
|
class ChangeAttributeTxn;
|
||||||
class CreateElementTxn;
|
|
||||||
class DeleteNodeTxn;
|
class DeleteNodeTxn;
|
||||||
class DeleteTextTxn;
|
|
||||||
class EditAggregateTxn;
|
class EditAggregateTxn;
|
||||||
class IMETextTxn;
|
class IMETextTxn;
|
||||||
class InsertElementTxn;
|
|
||||||
class InsertTextTxn;
|
class InsertTextTxn;
|
||||||
class JoinElementTxn;
|
class JoinElementTxn;
|
||||||
class RemoveStyleSheetTxn;
|
class RemoveStyleSheetTxn;
|
||||||
@ -72,9 +69,12 @@ class ErrorResult;
|
|||||||
class TextComposition;
|
class TextComposition;
|
||||||
|
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
class CreateElementTxn;
|
||||||
class DataTransfer;
|
class DataTransfer;
|
||||||
|
class DeleteTextTxn;
|
||||||
class Element;
|
class Element;
|
||||||
class EventTarget;
|
class EventTarget;
|
||||||
|
class InsertNodeTxn;
|
||||||
class Selection;
|
class Selection;
|
||||||
class Text;
|
class Text;
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
@ -200,7 +200,6 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
nsresult MarkNodeDirty(nsINode* aNode);
|
|
||||||
virtual bool IsModifiableNode(nsINode *aNode);
|
virtual bool IsModifiableNode(nsINode *aNode);
|
||||||
|
|
||||||
NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert,
|
NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert,
|
||||||
@ -222,8 +221,7 @@ public:
|
|||||||
|
|
||||||
/* helper routines for node/parent manipulations */
|
/* helper routines for node/parent manipulations */
|
||||||
nsresult DeleteNode(nsINode* aNode);
|
nsresult DeleteNode(nsINode* aNode);
|
||||||
nsresult InsertNode(nsIContent* aContent, nsINode* aParent,
|
nsresult InsertNode(nsIContent& aNode, nsINode& aParent, int32_t aPosition);
|
||||||
int32_t aPosition);
|
|
||||||
enum ECloneAttributes { eDontCloneAttributes, eCloneAttributes };
|
enum ECloneAttributes { eDontCloneAttributes, eCloneAttributes };
|
||||||
already_AddRefed<mozilla::dom::Element> ReplaceContainer(
|
already_AddRefed<mozilla::dom::Element> ReplaceContainer(
|
||||||
mozilla::dom::Element* aOldContainer,
|
mozilla::dom::Element* aOldContainer,
|
||||||
@ -234,21 +232,14 @@ public:
|
|||||||
void CloneAttributes(mozilla::dom::Element* aDest,
|
void CloneAttributes(mozilla::dom::Element* aDest,
|
||||||
mozilla::dom::Element* aSource);
|
mozilla::dom::Element* aSource);
|
||||||
|
|
||||||
nsresult RemoveContainer(nsINode* aNode);
|
nsresult RemoveContainer(nsIContent* aNode);
|
||||||
nsresult RemoveContainer(nsIDOMNode *inNode);
|
already_AddRefed<mozilla::dom::Element> InsertContainerAbove(
|
||||||
nsresult InsertContainerAbove(nsIContent* aNode,
|
nsIContent* aNode,
|
||||||
mozilla::dom::Element** aOutNode,
|
nsIAtom* aNodeType,
|
||||||
const nsAString& aNodeType,
|
nsIAtom* aAttribute = nullptr,
|
||||||
const nsAString* aAttribute = nullptr,
|
|
||||||
const nsAString* aValue = nullptr);
|
const nsAString* aValue = nullptr);
|
||||||
nsresult InsertContainerAbove(nsIDOMNode *inNode,
|
|
||||||
nsCOMPtr<nsIDOMNode> *outNode,
|
|
||||||
const nsAString &aNodeType,
|
|
||||||
const nsAString *aAttribute = nullptr,
|
|
||||||
const nsAString *aValue = nullptr);
|
|
||||||
nsresult JoinNodes(nsINode* aNodeToKeep, nsIContent* aNodeToMove);
|
nsresult JoinNodes(nsINode* aNodeToKeep, nsIContent* aNodeToMove);
|
||||||
nsresult MoveNode(nsINode* aNode, nsINode* aParent, int32_t aOffset);
|
nsresult MoveNode(nsIContent* aNode, nsINode* aParent, int32_t aOffset);
|
||||||
nsresult MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aOffset);
|
|
||||||
|
|
||||||
/* Method to replace certain CreateElementNS() calls.
|
/* Method to replace certain CreateElementNS() calls.
|
||||||
Arguments:
|
Arguments:
|
||||||
@ -282,17 +273,19 @@ protected:
|
|||||||
|
|
||||||
/** create a transaction for creating a new child node of aParent of type aTag.
|
/** create a transaction for creating a new child node of aParent of type aTag.
|
||||||
*/
|
*/
|
||||||
NS_IMETHOD CreateTxnForCreateElement(const nsAString & aTag,
|
already_AddRefed<mozilla::dom::CreateElementTxn>
|
||||||
nsIDOMNode *aParent,
|
CreateTxnForCreateElement(nsIAtom& aTag,
|
||||||
int32_t aPosition,
|
nsINode& aParent,
|
||||||
CreateElementTxn ** aTxn);
|
int32_t aPosition);
|
||||||
|
|
||||||
|
already_AddRefed<mozilla::dom::Element> CreateNode(nsIAtom* aTag,
|
||||||
|
nsINode* aParent,
|
||||||
|
int32_t aPosition);
|
||||||
|
|
||||||
/** create a transaction for inserting aNode as a child of aParent.
|
/** create a transaction for inserting aNode as a child of aParent.
|
||||||
*/
|
*/
|
||||||
NS_IMETHOD CreateTxnForInsertElement(nsIDOMNode * aNode,
|
already_AddRefed<mozilla::dom::InsertNodeTxn>
|
||||||
nsIDOMNode * aParent,
|
CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent, int32_t aOffset);
|
||||||
int32_t aOffset,
|
|
||||||
InsertElementTxn ** aTxn);
|
|
||||||
|
|
||||||
/** create a transaction for removing aNode from its parent.
|
/** create a transaction for removing aNode from its parent.
|
||||||
*/
|
*/
|
||||||
@ -334,28 +327,18 @@ protected:
|
|||||||
NS_IMETHOD CreateTxnForRemoveStyleSheet(mozilla::CSSStyleSheet* aSheet,
|
NS_IMETHOD CreateTxnForRemoveStyleSheet(mozilla::CSSStyleSheet* aSheet,
|
||||||
RemoveStyleSheetTxn* *aTxn);
|
RemoveStyleSheetTxn* *aTxn);
|
||||||
|
|
||||||
NS_IMETHOD DeleteText(nsIDOMCharacterData *aElement,
|
nsresult DeleteText(nsGenericDOMDataNode& aElement,
|
||||||
uint32_t aOffset,
|
uint32_t aOffset, uint32_t aLength);
|
||||||
uint32_t aLength);
|
|
||||||
|
|
||||||
inline nsresult DeleteText(mozilla::dom::Text* aText, uint32_t aOffset,
|
|
||||||
uint32_t aLength)
|
|
||||||
{
|
|
||||||
return DeleteText(static_cast<nsIDOMCharacterData*>(GetAsDOMNode(aText)),
|
|
||||||
aOffset, aLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NS_IMETHOD DeleteRange(nsIDOMRange *aRange);
|
// NS_IMETHOD DeleteRange(nsIDOMRange *aRange);
|
||||||
|
|
||||||
nsresult CreateTxnForDeleteText(nsIDOMCharacterData* aElement,
|
already_AddRefed<mozilla::dom::DeleteTextTxn>
|
||||||
uint32_t aOffset,
|
CreateTxnForDeleteText(nsGenericDOMDataNode& aElement,
|
||||||
uint32_t aLength,
|
uint32_t aOffset, uint32_t aLength);
|
||||||
DeleteTextTxn** aTxn);
|
|
||||||
|
|
||||||
nsresult CreateTxnForDeleteCharacter(nsIDOMCharacterData* aData,
|
already_AddRefed<mozilla::dom::DeleteTextTxn>
|
||||||
uint32_t aOffset,
|
CreateTxnForDeleteCharacter(nsGenericDOMDataNode& aData, uint32_t aOffset,
|
||||||
EDirection aDirection,
|
EDirection aDirection);
|
||||||
DeleteTextTxn** aTxn);
|
|
||||||
|
|
||||||
NS_IMETHOD CreateTxnForSplitNode(nsIDOMNode *aNode,
|
NS_IMETHOD CreateTxnForSplitNode(nsIDOMNode *aNode,
|
||||||
uint32_t aOffset,
|
uint32_t aOffset,
|
||||||
|
@ -569,7 +569,7 @@ nsHTMLEditor::AbsolutelyPositionElement(nsIDOMElement * aElement,
|
|||||||
NS_ENSURE_TRUE(htmlRules, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(htmlRules, NS_ERROR_FAILURE);
|
||||||
nsresult res = htmlRules->MakeSureElemStartsOrEndsOnCR(aElement);
|
nsresult res = htmlRules->MakeSureElemStartsOrEndsOnCR(aElement);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
res = RemoveContainer(aElement);
|
res = RemoveContainer(element);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -613,7 +613,7 @@ nsHTMLCSSUtils::RemoveCSSInlineStyle(nsIDOMNode *aNode, nsIAtom *aProperty, cons
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mHTMLEditor->RemoveContainer(aNode);
|
return mHTMLEditor->RemoveContainer(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Answers true is the property can be removed by setting a "none" CSS value
|
// Answers true is the property can be removed by setting a "none" CSS value
|
||||||
|