Merge m-c to b2g-inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-12-23 13:18:49 -05:00
commit bba9447754
290 changed files with 4191 additions and 2202 deletions

3
.gitignore vendored
View File

@ -67,3 +67,6 @@ GPATH
# Git clone directory for updating web-platform-tests
testing/web-platform/sync/
# Android Gradle artifacts.
mobile/android/gradle/.gradle

View File

@ -93,3 +93,6 @@ GPATH
# including the following three lines
^browser/components/loop/standalone/content/legal/styles/.*\.css$
^browser/components/loop/standalone/content/legal/terms/en_US\.html$
# Android Gradle artifacts.
^mobile/android/gradle/.gradle

View File

@ -23,6 +23,7 @@ NS_IMETHODIMP
xpcAccessible::GetParent(nsIAccessible** aParent)
{
NS_ENSURE_ARG_POINTER(aParent);
*aParent = nullptr;
if (!Intl())
return NS_ERROR_FAILURE;
@ -146,6 +147,10 @@ NS_IMETHODIMP
xpcAccessible::GetIndexInParent(int32_t* aIndexInParent)
{
NS_ENSURE_ARG_POINTER(aIndexInParent);
*aIndexInParent = -1;
if (!Intl())
return NS_ERROR_FAILURE;
*aIndexInParent = Intl()->IndexInParent();
return *aIndexInParent != -1 ? NS_OK : NS_ERROR_FAILURE;
@ -157,6 +162,9 @@ xpcAccessible::GetDOMNode(nsIDOMNode** aDOMNode)
NS_ENSURE_ARG_POINTER(aDOMNode);
*aDOMNode = nullptr;
if (!Intl())
return NS_ERROR_FAILURE;
nsINode* node = Intl()->GetNode();
if (node)
CallQueryInterface(node, aDOMNode);
@ -168,6 +176,10 @@ NS_IMETHODIMP
xpcAccessible::GetDocument(nsIAccessibleDocument** aDocument)
{
NS_ENSURE_ARG_POINTER(aDocument);
*aDocument = nullptr;
if (!Intl())
return NS_ERROR_FAILURE;
NS_IF_ADDREF(*aDocument = ToXPCDocument(Intl()->Document()));
return NS_OK;
@ -177,6 +189,10 @@ NS_IMETHODIMP
xpcAccessible::GetRootDocument(nsIAccessibleDocument** aRootDocument)
{
NS_ENSURE_ARG_POINTER(aRootDocument);
*aRootDocument = nullptr;
if (!Intl())
return NS_ERROR_FAILURE;
NS_IF_ADDREF(*aRootDocument = ToXPCDocument(Intl()->RootAccessible()));
return NS_OK;
@ -199,7 +215,7 @@ NS_IMETHODIMP
xpcAccessible::GetState(uint32_t* aState, uint32_t* aExtraState)
{
NS_ENSURE_ARG_POINTER(aState);
if (!Intl())
nsAccUtils::To32States(states::DEFUNCT, aState, aExtraState);
else

View File

@ -479,11 +479,9 @@ SocialShare = {
},
get iframe() {
// first element is our menu vbox.
if (this.panel.childElementCount == 1)
return null;
else
return this.panel.lastChild;
// panel.firstChild is our toolbar hbox, panel.lastChild is the iframe
// container hbox used for an interstitial "loading" graphic
return this.panel.lastChild.firstChild;
},
uninit: function () {
@ -505,7 +503,7 @@ SocialShare = {
iframe.setAttribute("tooltip", "aHTMLTooltip");
iframe.setAttribute("disableglobalhistory", "true");
iframe.setAttribute("flex", "1");
panel.appendChild(iframe);
panel.lastChild.appendChild(iframe);
iframe.addEventListener("load", function _firstload() {
iframe.removeEventListener("load", _firstload, true);
iframe.messageManager.loadFrameScript("chrome://browser/content/content.js", true);
@ -537,13 +535,13 @@ SocialShare = {
// remove everything before the add-share-provider button (which should also
// be lastChild if any share providers were added)
let addButton = document.getElementById("add-share-provider");
while (hbox.firstChild != addButton) {
hbox.removeChild(hbox.firstChild);
while (hbox.lastChild != addButton) {
hbox.removeChild(hbox.lastChild);
}
let selectedProvider = this.getSelectedProvider();
for (let provider of providers) {
let button = document.createElement("toolbarbutton");
button.setAttribute("class", "toolbarbutton share-provider-button");
button.setAttribute("class", "toolbarbutton-1 share-provider-button");
button.setAttribute("type", "radio");
button.setAttribute("group", "share-providers");
button.setAttribute("image", provider.iconURL);
@ -554,7 +552,7 @@ SocialShare = {
if (provider == selectedProvider) {
this.defaultButton = button;
}
hbox.insertBefore(button, addButton);
hbox.appendChild(button);
}
if (!this.defaultButton) {
this.defaultButton = addButton;
@ -577,7 +575,7 @@ SocialShare = {
_onclick: function() {
Services.telemetry.getHistogramById("SOCIAL_PANEL_CLICKS").add(0);
},
onShowing: function() {
this.anchor.setAttribute("open", "true");
this.iframe.addEventListener("click", this._onclick, true);
@ -682,48 +680,40 @@ SocialShare = {
let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
this._dynamicResizer.stop();
let size = provider.getPageSize("share");
if (size) {
this._dynamicResizer.stop();
// let the css on the share panel define width, but height
// calculations dont work on all sites, so we allow that to be
// defined.
delete size.width;
}
// if we've already loaded this provider/page share endpoint, we don't want
// to add another load event listener.
let reload = true;
let endpointMatch = shareEndpoint == iframe.getAttribute("src");
let docLoaded = iframe.contentDocument && iframe.contentDocument.readyState == "complete";
if (endpointMatch && docLoaded) {
reload = shareEndpoint != iframe.contentDocument.location.spec;
}
if (!reload) {
if (!size)
this._dynamicResizer.start(this.panel, iframe);
if (endpointMatch) {
this._dynamicResizer.start(iframe.parentNode, iframe, size);
iframe.docShell.isActive = true;
iframe.docShell.isAppTab = true;
let evt = iframe.contentDocument.createEvent("CustomEvent");
evt.initCustomEvent("OpenGraphData", true, true, JSON.stringify(pageData));
iframe.contentDocument.documentElement.dispatchEvent(evt);
} else {
iframe.parentNode.setAttribute("loading", "true");
// first time load, wait for load and dispatch after load
iframe.addEventListener("load", function panelBrowserOnload(e) {
iframe.removeEventListener("load", panelBrowserOnload, true);
iframe.docShell.isActive = true;
iframe.docShell.isAppTab = true;
iframe.parentNode.removeAttribute("loading");
// to support standard share endpoints mimick window.open by setting
// window.opener, some share endpoints rely on w.opener to know they
// should close the window when done.
iframe.contentWindow.opener = iframe.contentWindow;
setTimeout(function() {
if (size) {
let panel = SocialShare.panel;
let {width, height} = size;
width += panel.boxObject.width - iframe.boxObject.width;
height += panel.boxObject.height - iframe.boxObject.height;
panel.sizeTo(width, height);
} else {
SocialShare._dynamicResizer.start(iframe.parentNode, iframe);
}
}, 0);
SocialShare._dynamicResizer.start(iframe.parentNode, iframe, size);
let evt = iframe.contentDocument.createEvent("CustomEvent");
evt.initCustomEvent("OpenGraphData", true, true, JSON.stringify(pageData));
iframe.contentDocument.documentElement.dispatchEvent(evt);
@ -747,9 +737,18 @@ SocialShare = {
showDirectory: function() {
this._createFrame();
let iframe = this.iframe;
if (iframe.getAttribute("src") == "about:providerdirectory")
return;
iframe.removeAttribute("origin");
iframe.parentNode.setAttribute("loading", "true");
iframe.addEventListener("DOMContentLoaded", function _dcl(e) {
iframe.removeEventListener("DOMContentLoaded", _dcl, true);
iframe.parentNode.removeAttribute("loading");
}, true);
iframe.addEventListener("load", function panelBrowserOnload(e) {
iframe.removeEventListener("load", panelBrowserOnload, true);
hookWindowCloseForPanelClose(iframe.contentWindow);
SocialShare._dynamicResizer.start(iframe.parentNode, iframe);

View File

@ -287,17 +287,22 @@
<panel id="social-share-panel"
class="social-panel"
type="arrow"
orient="horizontal"
orient="vertical"
onpopupshowing="SocialShare.onShowing()"
onpopuphidden="SocialShare.onHidden()"
hidden="true">
<vbox class="social-share-toolbar">
<arrowscrollbox id="social-share-provider-buttons" orient="vertical" flex="1">
<hbox class="social-share-toolbar">
<toolbarbutton id="manage-share-providers" class="toolbarbutton share-provider-button"
tooltiptext="&social.addons.label;"
oncommand="BrowserOpenAddonsMgr('addons://list/service');
this.parentNode.parentNode.hidePopup();"/>
<arrowscrollbox id="social-share-provider-buttons" orient="horizontal" flex="1" pack="end">
<toolbarbutton id="add-share-provider" class="toolbarbutton share-provider-button" type="radio"
group="share-providers" tooltiptext="&findShareServices.label;"
oncommand="SocialShare.showDirectory()"/>
</arrowscrollbox>
</vbox>
</hbox>
<hbox id="share-container" flex="1"/>
</panel>
<panel id="social-notification-panel"

View File

@ -14,6 +14,12 @@ function test() {
};
runSocialTestWithProvider(manifest, function (finishcb) {
SocialSidebar.show();
// disable transitions for the test
let panel = document.getElementById("social-flyout-panel");
registerCleanupFunction(function () {
panel.removeAttribute("animate");
});
panel.setAttribute("animate", "false");
runSocialTests(tests, undefined, undefined, finishcb);
});
}
@ -21,8 +27,7 @@ function test() {
var tests = {
testOpenCloseFlyout: function(next) {
let panel = document.getElementById("social-flyout-panel");
panel.addEventListener("popupshowing", function onShowing() {
panel.removeEventListener("popupshowing", onShowing);
ensureEventFired(panel, "popupshown").then(() => {
is(panel.firstChild.contentDocument.readyState, "complete", "panel is loaded prior to showing");
});
let port = SocialSidebar.provider.getWorkerPort();
@ -75,8 +80,7 @@ var tests = {
is(cs.height, "400px", "should be 400px high");
is(iframe.boxObject.height, 400, "iframe should now be 400px high");
iframe.contentWindow.addEventListener("resize", function _doneHandler() {
iframe.contentWindow.removeEventListener("resize", _doneHandler, false);
ensureEventFired(iframe.contentWindow, "resize").then(() => {
cs = iframe.contentWindow.getComputedStyle(body);
is(cs.width, "500px", "should now be 500px wide");
@ -86,7 +90,7 @@ var tests = {
panel.hidePopup();
port.close();
next();
}, false);
});
SocialFlyout.dispatchPanelEvent("socialTest-MakeWider");
break;
}
@ -117,13 +121,12 @@ var tests = {
if (e.data.result != "shown")
return;
let iframe = panel.firstChild;
iframe.contentDocument.addEventListener("SocialTest-DoneCloseSelf", function _doneHandler() {
iframe.contentDocument.removeEventListener("SocialTest-DoneCloseSelf", _doneHandler, false);
ensureEventFired(iframe.contentDocument, "SocialTest-DoneCloseSelf").then(() => {
port.close();
is(panel.state, "closed", "flyout should have closed itself");
Services.prefs.setBoolPref(ALLOW_SCRIPTS_TO_CLOSE_PREF, oldAllowScriptsToClose);
next();
}, false);
});
is(panel.state, "open", "flyout should be open");
SocialFlyout.dispatchPanelEvent("socialTest-CloseSelf");
break;

View File

@ -31,7 +31,7 @@
</head>
<body style="width: 400px; height: 400px; margin: 0; overflow: hidden;" onload="pingWorker();">
<p>This is a test social flyout panel.</p>
<a id="traversal" href="http://mochi.test">test link</a>
<a id="traversal" href="https://test.example.com">test link</a>
</body>
</html>

View File

@ -1000,6 +1000,32 @@
class="search-setting-button search-panel-header"
label="&changeSearchSettings.button;"/>
</content>
<implementation>
<method name="updateHeader">
<body><![CDATA[
let currentEngine = Services.search.currentEngine;
let uri = currentEngine.iconURI;
if (uri) {
uri = uri.spec;
this.setAttribute("src", PlacesUtils.getImageURLForResolution(window, uri));
}
else {
// If the default has just been changed to a provider without icon,
// avoid showing the icon of the previous default provider.
this.removeAttribute("src");
}
const kBundleURI = "chrome://browser/locale/search.properties";
let bundle = Services.strings.createBundle(kBundleURI);
let headerText = bundle.formatStringFromName("searchHeader",
[currentEngine.name], 1);
document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine-name")
.setAttribute("value", headerText);
document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine")
.engine = currentEngine;
]]></body>
</method>
</implementation>
<handlers>
<handler event="popupshowing"><![CDATA[
// First handle deciding if we are showing the reduced version of the
@ -1022,26 +1048,7 @@
}
// Show the current default engine in the top header of the panel.
let currentEngine = Services.search.currentEngine;
let uri = currentEngine.iconURI;
if (uri) {
uri = uri.spec;
this.setAttribute("src", PlacesUtils.getImageURLForResolution(window, uri));
}
else {
// If the default has just been changed to a provider without icon,
// avoid showing the icon of the previous default provider.
this.removeAttribute("src");
}
const kBundleURI = "chrome://browser/locale/search.properties";
let bundle = Services.strings.createBundle(kBundleURI);
let headerText = bundle.formatStringFromName("searchHeader",
[currentEngine.name], 1);
document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine-name")
.setAttribute("value", headerText);
document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine")
.engine = currentEngine;
this.updateHeader();
// Update the 'Search for <keywords> with:" header.
let headerSearchText =
@ -1074,6 +1081,8 @@
let addEngines = gBrowser.selectedBrowser.engines;
if (addEngines && addEngines.length > 0) {
const kBundleURI = "chrome://browser/locale/search.properties";
let bundle = Services.strings.createBundle(kBundleURI);
for (let engine of addEngines) {
let button = document.createElementNS(kXULNS, "button");
let label = bundle.formatStringFromName("cmd_addFoundEngine",
@ -1109,8 +1118,9 @@
hiddenList = [];
}
let currentEngineName = Services.search.currentEngine.name;
let engines = Services.search.getVisibleEngines()
.filter(e => e.name != currentEngine.name &&
.filter(e => e.name != currentEngineName &&
hiddenList.indexOf(e.name) == -1);
let header = document.getAnonymousElementByAttribute(this, "anonid",

View File

@ -784,6 +784,22 @@ let MozLoopServiceInternal = {
}, pc.id);
},
getChatWindowID: function(conversationWindowData) {
// Try getting a window ID that can (re-)identify this conversation, or resort
// to a globally unique one as a last resort.
// XXX We can clean this up once rooms and direct contact calling are the only
// two modes left.
let windowId = ("contact" in conversationWindowData) ?
conversationWindowData.contact._guid || gLastWindowId++ :
conversationWindowData.roomToken || conversationWindowData.callId ||
gLastWindowId++;
return windowId.toString();
},
getChatURL: function(chatWindowId) {
return "about:loopconversation#" + chatWindowId;
},
/**
* Opens the chat window
*
@ -794,20 +810,11 @@ let MozLoopServiceInternal = {
openChatWindow: function(conversationWindowData) {
// So I guess the origin is the loop server!?
let origin = this.loopServerUri;
// Try getting a window ID that can (re-)identify this conversation, or resort
// to a globally unique one as a last resort.
// XXX We can clean this up once rooms and direct contact calling are the only
// two modes left.
let windowId = ("contact" in conversationWindowData) ?
conversationWindowData.contact._guid || gLastWindowId++ :
conversationWindowData.roomToken || conversationWindowData.callId ||
gLastWindowId++;
// Store the id as a string, as that's what we use elsewhere.
windowId = windowId.toString();
let windowId = this.getChatWindowID(conversationWindowData);
gConversationWindowData.set(windowId, conversationWindowData);
let url = "about:loopconversation#" + windowId;
let url = this.getChatURL(windowId);
let callback = chatbox => {
// We need to use DOMContentLoaded as otherwise the injection will happen
@ -1099,36 +1106,7 @@ this.MozLoopService = {
}
});
// Resume the tour (re-opening the tab, if necessary) if someone else joins
// a room of ours and it's currently open.
LoopRooms.on("joined", (e, room, participant) => {
let isOwnerInRoom = false;
let isOtherInRoom = false;
if (!this.getLoopPref("gettingStarted.resumeOnFirstJoin")) {
return;
}
if (!room.participants) {
return;
}
// The particpant that joined isn't necessarily included in room.participants (depending on
// when the broadcast happens) so concatenate.
for (let participant of room.participants.concat(participant)) {
if (participant.owner) {
isOwnerInRoom = true;
} else {
isOtherInRoom = true;
}
}
if (!isOwnerInRoom || !isOtherInRoom) {
return;
}
this.resumeTour("open");
});
LoopRooms.on("joined", this.maybeResumeTourOnRoomJoined.bind(this));
// If expiresTime is not in the future and the user hasn't
// previously authenticated then skip registration.
@ -1144,6 +1122,49 @@ this.MozLoopService = {
return deferredInitialization.promise;
}),
/**
* Maybe resume the tour (re-opening the tab, if necessary) if someone else joins
* a room of ours and it's currently open.
*/
maybeResumeTourOnRoomJoined: function(e, room, participant) {
let isOwnerInRoom = false;
let isOtherInRoom = false;
if (!this.getLoopPref("gettingStarted.resumeOnFirstJoin")) {
return;
}
if (!room.participants) {
return;
}
// The particpant that joined isn't necessarily included in room.participants (depending on
// when the broadcast happens) so concatenate.
for (let participant of room.participants.concat(participant)) {
if (participant.owner) {
isOwnerInRoom = true;
} else {
isOtherInRoom = true;
}
}
if (!isOwnerInRoom || !isOtherInRoom) {
return;
}
// Check that the room chatbox is still actually open using its URL
let chatboxesForRoom = [...Chat.chatboxes].filter(chatbox => {
return chatbox.src == MozLoopServiceInternal.getChatURL(room.roomToken);
});
if (!chatboxesForRoom.length) {
log.warn("Tried to resume the tour from a join when the chatbox was closed", room);
return;
}
this.resumeTour("open");
},
/**
* The core of the initialization work that happens once the browser is ready
* (after a timer when called during startup).

View File

@ -467,6 +467,9 @@
aEvent.preventDefault();
aEvent.stopPropagation();
if (this.hasAttribute("oneoffui"))
this.openSuggestionsPanel();
]]></body>
</method>
@ -647,10 +650,9 @@
<field name="_ignoreFocus">false</field>
<method name="selectEngine">
<method name="rebuildPopup">
<body><![CDATA[
// Override this method to avoid accidentally changing the default
// engine using the keyboard shortcuts of the old UI.
this._textbox.popup.updateHeader();
]]></body>
</method>
@ -914,7 +916,11 @@
<![CDATA[
// Don't open search popup if history popup is open
if (!this.popupOpen) {
document.getBindingParent(this).searchButton.open = true;
let searchBox = document.getBindingParent(this);
if (searchBox.hasAttribute("oneoffui"))
searchBox.openSuggestionsPanel();
else
searchBox.searchButton.open = true;
return false;
}
return true;
@ -980,8 +986,54 @@
if (!list)
return;
// accel + up/down changes the default engine and shouldn't affect
// the selection on the one-off buttons.
#ifdef XP_MACOSX
if (aEvent.metaKey)
#else
if (aEvent.ctrlKey)
#endif
return;
let selectedButton = this.getSelectedOneOff();
// Alt + up/down is very similar to (shift +) tab but differs in that
// it loops through the list, whereas tab will move the focus out.
if (aEvent.altKey &&
(aEvent.keyCode == KeyEvent.DOM_VK_DOWN ||
aEvent.keyCode == KeyEvent.DOM_VK_UP)) {
let forward = aEvent.keyCode == KeyEvent.DOM_VK_DOWN;
if (selectedButton) {
// cycle though the list of one-off buttons.
selectedButton.removeAttribute("selected");
if (forward)
selectedButton = selectedButton.nextSibling;
else
selectedButton = selectedButton.previousSibling;
// Avoid selecting dummy buttons.
if (selectedButton && selectedButton.classList.contains("dummy"))
selectedButton = null;
}
else {
// If no selection, select the first or last one-off button.
if (forward) {
selectedButton = list.firstChild;
}
else {
selectedButton = list.lastChild;
while (selectedButton.classList.contains("dummy"))
selectedButton = selectedButton.previousSibling;
}
}
if (selectedButton)
selectedButton.setAttribute("selected", "true");
aEvent.preventDefault();
aEvent.stopPropagation();
return;
}
// If the last suggestion is selected, DOWN selects the first one-off.
if (aEvent.keyCode == KeyEvent.DOM_VK_DOWN &&
popup.selectedIndex + 1 == popup.view.rowCount) {

View File

@ -71,7 +71,7 @@ function CheckLockState() {
let sYes = Strings.GetStringFromName("runtimedetails_checkyes");
let sNo = Strings.GetStringFromName("runtimedetails_checkno");
let sUnknown = Strings.GetStringFromName("runtimedetails_checkunkown");
let sUnknown = Strings.GetStringFromName("runtimedetails_checkunknown");
let sNotUSB = Strings.GetStringFromName("runtimedetails_notUSBDevice");
flipCertPerfButton.setAttribute("disabled", "true");

View File

@ -65,7 +65,7 @@ addons_status_installing=installing
runtimedetails_checkno=no
runtimedetails_checkyes=yes
runtimedetails_checkunkown=unknown
runtimedetails_checkunknown=unknown (requires ADB Helper 0.4.0 or later)
runtimedetails_notUSBDevice=Not a USB device
# Validation status

View File

@ -50,6 +50,38 @@ function getChromeWindow(contentWin) {
*/
let Chat = {
/**
* Iterator of <chatbox> elements from this module in all windows.
*/
get chatboxes() {
return function*() {
let winEnum = Services.wm.getEnumerator("navigator:browser");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
let chatbar = win.document.getElementById("pinnedchats");
if (!chatbar)
continue;
// Make a new array instead of the live NodeList so this iterator can be
// used for closing/deleting.
let chatboxes = [c for (c of chatbar.children)];
for (let chatbox of chatboxes) {
yield chatbox;
}
}
// include standalone chat windows
winEnum = Services.wm.getEnumerator("Social:Chat");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
if (win.closed)
continue;
yield win.document.getElementById("chatter");
}
}();
},
/**
* Open a new chatbox.
*
@ -108,26 +140,11 @@ let Chat = {
* The origin from which all chats should be closed.
*/
closeAll: function(origin) {
// close all attached chat windows
let winEnum = Services.wm.getEnumerator("navigator:browser");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
let chatbar = win.document.getElementById("pinnedchats");
if (!chatbar)
for (let chatbox of this.chatboxes) {
if (chatbox.content.getAttribute("origin") != origin) {
continue;
let chats = [c for (c of chatbar.children) if (c.content.getAttribute("origin") == origin)];
[c.close() for (c of chats)];
}
// close all standalone chat windows
winEnum = Services.wm.getEnumerator("Social:Chat");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
if (win.closed)
continue;
let chatOrigin = win.document.getElementById("chatter").content.getAttribute("origin");
if (origin == chatOrigin)
win.close();
}
chatbox.close();
}
},

View File

@ -400,7 +400,7 @@ SocialErrorListener.prototype = {
};
function sizeSocialPanelToContent(panel, iframe) {
function sizeSocialPanelToContent(panel, iframe, requestedSize) {
let doc = iframe.contentDocument;
if (!doc || !doc.body) {
return;
@ -408,14 +408,15 @@ function sizeSocialPanelToContent(panel, iframe) {
// We need an element to use for sizing our panel. See if the body defines
// an id for that element, otherwise use the body itself.
let body = doc.body;
let docEl = doc.documentElement;
let bodyId = body.getAttribute("contentid");
if (bodyId) {
body = doc.getElementById(bodyId) || doc.body;
}
// offsetHeight/Width don't include margins, so account for that.
let cs = doc.defaultView.getComputedStyle(body);
let width = PANEL_MIN_WIDTH;
let height = PANEL_MIN_HEIGHT;
let width = Math.max(PANEL_MIN_WIDTH, docEl.offsetWidth);
let height = Math.max(PANEL_MIN_HEIGHT, docEl.offsetHeight);
// if the panel is preloaded prior to being shown, cs will be null. in that
// case use the minimum size for the panel until it is shown.
if (cs) {
@ -425,19 +426,33 @@ function sizeSocialPanelToContent(panel, iframe) {
width = Math.max(computedWidth, width);
}
// only add the extra space if the iframe has been loaded
// if our scrollHeight is still larger than the iframe, the css calculations
// above did not work for this site, increase the height. This can happen if
// the site increases its height for additional UI.
if (docEl.scrollHeight > iframe.boxObject.height)
height = docEl.scrollHeight;
// if a size was defined in the manifest use it as a minimum
if (requestedSize) {
if (requestedSize.height)
height = Math.max(height, requestedSize.height);
if (requestedSize.width)
width = Math.max(width, requestedSize.width);
}
// add the extra space used by the panel (toolbar, borders, etc) if the iframe
// has been loaded
if (iframe.boxObject.width && iframe.boxObject.height) {
// add extra space the panel needs if any
width += panel.boxObject.width - iframe.boxObject.width;
height += panel.boxObject.height - iframe.boxObject.height;
}
// when size is computed, we want to be sure changes are "significant" since
// some sites will resize when the iframe is resized by a small amount, making
// the panel slowly shrink to some minimum.
if (Math.abs(panel.boxObject.width - width) > 2 || Math.abs(panel.boxObject.height - height) > 2) {
panel.sizeTo(width, height);
}
// using panel.sizeTo will ignore css transitions, set size via style
if (Math.abs(panel.boxObject.width - width) >= 2)
panel.style.width = width + "px";
if (Math.abs(panel.boxObject.height - height) >= 2)
panel.style.height = height + "px";
}
function DynamicResizeWatcher() {
@ -445,18 +460,18 @@ function DynamicResizeWatcher() {
}
DynamicResizeWatcher.prototype = {
start: function DynamicResizeWatcher_start(panel, iframe) {
start: function DynamicResizeWatcher_start(panel, iframe, requestedSize) {
this.stop(); // just in case...
let doc = iframe.contentDocument;
this._mutationObserver = new iframe.contentWindow.MutationObserver(function(mutations) {
sizeSocialPanelToContent(panel, iframe);
this._mutationObserver = new iframe.contentWindow.MutationObserver((mutations) => {
sizeSocialPanelToContent(panel, iframe, requestedSize);
});
// Observe anything that causes the size to change.
let config = {attributes: true, characterData: true, childList: true, subtree: true};
this._mutationObserver.observe(doc, config);
// and since this may be setup after the load event has fired we do an
// initial resize now.
sizeSocialPanelToContent(panel, iframe);
sizeSocialPanelToContent(panel, iframe, requestedSize);
},
stop: function DynamicResizeWatcher_stop() {
if (this._mutationObserver) {

View File

@ -1609,46 +1609,47 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* social share panel */
.social-share-frame {
background: linear-gradient(to bottom, rgba(242,242,242,.99), rgba(242,242,242,.95));
border-left: 1px solid #f8f8f8;
width: 330px;
border-top: 1px solid #f8f8f8;
width: 756px;
height: 150px;
/* we resize our panels dynamically, make it look nice */
transition: height 100ms ease-out, width 100ms ease-out;
}
#share-container {
min-width: 756px;
background-color: white;
background-repeat: no-repeat;
background-position: center center;
}
#share-container[loading] {
background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
}
#share-container > browser {
transition: opacity 150ms ease-in-out;
opacity: 1;
}
#share-container[loading] > browser {
opacity: 0;
}
.social-share-toolbar {
border-right: 1px solid #dedede;
background: linear-gradient(to bottom, rgba(247,247,247,.99), rgba(247,247,247,.95));
border-bottom: 1px solid #dedede;
padding: 2px;
}
#social-share-provider-buttons {
border-right: 1px solid #fbfbfb;
padding: 6px;
}
#social-share-provider-buttons > .share-provider-button {
padding: 6px;
padding: 0;
margin: 0;
border: none;
border-radius: 2px;
}
#social-share-provider-buttons > .share-provider-button[checked],
#social-share-provider-buttons > .share-provider-button:active {
.share-provider-button {
padding: 5px;
border: 1px solid #b5b5b8;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
margin: 2px;
}
#social-share-provider-buttons > .share-provider-button[checked] {
background: linear-gradient(to bottom, #d9d9d9, #e3e3e3);
}
#social-share-provider-buttons > .share-provider-button > .toolbarbutton-text {
.share-provider-button > .toolbarbutton-text {
display: none;
}
#social-share-provider-buttons > .share-provider-button > .toolbarbutton-icon {
.share-provider-button > .toolbarbutton-icon {
width: 16px;
min-height: 16px;
max-height: 16px;

View File

@ -2514,46 +2514,55 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
/* social share panel */
.social-share-frame {
background: linear-gradient(to bottom, rgba(242,242,242,.99), rgba(242,242,242,.95));
border-left: 1px solid #f8f8f8;
width: 330px;
border-top: 1px solid #f8f8f8;
min-width: 756px;
height: 150px;
/* we resize our panels dynamically, make it look nice */
transition: height 100ms ease-out, width 100ms ease-out;
}
#share-container {
min-width: 756px;
background-color: white;
background-repeat: no-repeat;
background-position: center center;
}
#share-container[loading] {
background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
}
#share-container > browser {
transition: opacity 150ms ease-in-out;
opacity: 1;
}
#share-container[loading] > browser {
opacity: 0;
}
#manage-share-providers,
#social-sidebar-button:hover,
#social-sidebar-button:hover:active {
-moz-image-region: rect(18px, 468px, 36px, 450px);
}
.social-share-toolbar {
border-right: 1px solid #dedede;
background: linear-gradient(to bottom, rgba(247,247,247,.99), rgba(247,247,247,.95));
border-bottom: 1px solid #dedede;
padding: 2px;
}
#social-share-provider-buttons {
border-right: 1px solid #fbfbfb;
padding: 6px;
}
#social-share-provider-buttons > .share-provider-button {
padding: 6px;
padding: 0;
margin: 0;
border: none;
border-radius: 2px;
}
#social-share-provider-buttons > .share-provider-button[checked],
#social-share-provider-buttons > .share-provider-button:active {
.share-provider-button {
padding: 5px;
border: 1px solid #b5b5b8;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.2);
margin: 2px;
}
#social-share-provider-buttons > .share-provider-button[checked] {
background: linear-gradient(to bottom, #d9d9d9, #e3e3e3);
}
#social-share-provider-buttons > .share-provider-button > .toolbarbutton-text {
.share-provider-button > .toolbarbutton-text {
display: none;
}
#social-share-provider-buttons > .share-provider-button > .toolbarbutton-icon {
.share-provider-button > .toolbarbutton-icon {
width: 16px;
min-height: 16px;
max-height: 16px;
@ -4500,44 +4509,27 @@ menulist.translate-infobar-element > .menulist-dropmarker {
}
#social-share-panel {
max-height: 600px;
min-height: 100px;
max-width: 800px;
min-width: 300px;
transition: height .3s ease-in-out, width .3s ease-in-out;
}
.social-share-frame:-moz-locale-dir(ltr) {
#share-container,
.social-share-frame {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: inherit;
border-bottom-right-radius: inherit;
}
.social-share-frame:-moz-locale-dir(rtl) {
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
#social-share-panel > .social-share-toolbar:-moz-locale-dir(ltr) {
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
}
#social-share-panel > .social-share-toolbar:-moz-locale-dir(rtl) {
border-top-right-radius: inherit;
border-bottom-right-radius: inherit;
}
#social-share-provider-buttons:-moz-locale-dir(ltr) {
#social-share-panel > .social-share-toolbar {
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
border-top-right-radius: inherit;
}
#social-share-provider-buttons:-moz-locale-dir(rtl) {
#social-share-provider-buttons {
border-top-left-radius: inherit;
border-top-right-radius: inherit;
border-bottom-right-radius: inherit;
}
/* === end of social toolbar provider menu === */

View File

@ -702,6 +702,7 @@ panelview .toolbarbutton-1,
.subviewbutton,
.widget-overflow-list .toolbarbutton-1,
.panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
.share-provider-button,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
-moz-appearance: none;
padding: 0 6px;
@ -714,6 +715,7 @@ panelview .toolbarbutton-1,
panelview .toolbarbutton-1,
.subviewbutton,
.widget-overflow-list .toolbarbutton-1,
.share-provider-button,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
border-width: 1px;
}
@ -786,6 +788,7 @@ panelview .toolbarbutton-1@buttonStateHover@,
toolbarbutton.subviewbutton@buttonStateHover@,
menu.subviewbutton@menuStateHover@,
menuitem.subviewbutton@menuStateHover@,
.share-provider-button@buttonStateHover@,
.widget-overflow-list .toolbarbutton-1@buttonStateHover@,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
background-color: hsla(210,4%,10%,.08);
@ -800,6 +803,7 @@ panelview .toolbarbutton-1:-moz-any(@buttonStateActive@,[checked=true]),
toolbarbutton.subviewbutton@buttonStateActive@,
menu.subviewbutton@menuStateActive@,
menuitem.subviewbutton@menuStateActive@,
.share-provider-button:-moz-any(@buttonStateActive@,[checked=true]),
.widget-overflow-list .toolbarbutton-1@buttonStateActive@,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
background-color: hsla(210,4%,10%,.12);

View File

@ -8,21 +8,24 @@
padding: 3px;
}
#manage-share-providers,
#social-sidebar-button {
list-style-image: url("chrome://browser/skin/Toolbar.png");
-moz-image-region: rect(0, 468px, 18px, 450px);
}
#social-sidebar-button {
-moz-appearance: none;
list-style-image: url(chrome://browser/skin/social/gear_default.png);
border: none;
padding: 0;
margin: 2px;
}
#manage-share-providers > .toolbarbutton-icon,
#social-sidebar-button > .toolbarbutton-icon {
min-height: 16px;
min-width: 16px;
}
#social-sidebar-button:hover,
#social-sidebar-button:hover:active {
list-style-image: url(chrome://browser/skin/social/gear_clicked.png);
min-height: 18px;
min-width: 18px;
}
#social-sidebar-button > .toolbarbutton-menu-dropmarker {
display: none;
}

View File

@ -1573,46 +1573,46 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* social share panel */
#social-share-panel > iframe {
background: linear-gradient(to bottom, #f0f4f7, #fafbfc);
width: 300px;
.social-share-frame {
min-width: 756px;
height: 150px;
}
#share-container {
min-width: 756px;
background-color: white;
background-repeat: no-repeat;
background-position: center center;
}
#share-container[loading] {
background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
}
#share-container > browser {
transition: opacity 150ms ease-in-out;
opacity: 1;
}
#share-container[loading] > browser {
opacity: 0;
}
.social-share-toolbar {
border-right: 1px solid #e2e5e8;
background: linear-gradient(to bottom, #ffffff, #f5f7fa);
border-bottom: 1px solid #e2e5e8;
padding: 2px;
}
#social-share-provider-buttons {
padding: 6px;
padding: 0;
margin: 0;
}
#social-share-provider-buttons > .share-provider-button {
-moz-appearance: none;
.share-provider-button {
padding: 5px;
margin: 1px;
border: none;
background: none;
border-radius: 2px;
margin: 2px;
}
#social-share-provider-buttons > .share-provider-button[checked="true"]:not([disabled="true"]),
#social-share-provider-buttons > .share-provider-button:hover,
#social-share-provider-buttons > .share-provider-button:active {
padding: 4px;
border: 1px solid #aeb8c1;
box-shadow: inset 1px 1px 1px rgba(10, 31, 51, 0.1);
}
#social-share-provider-buttons > .share-provider-button[checked="true"]:not([disabled="true"]) {
background: linear-gradient(to bottom, rgba(230,232,234,.65), #d2d5d9);
}
#social-share-provider-buttons > .share-provider-button > .toolbarbutton-text {
.share-provider-button > .toolbarbutton-text {
display: none;
}
#social-share-provider-buttons > .share-provider-button > .toolbarbutton-icon {
.share-provider-button > .toolbarbutton-icon {
width: 16px;
min-height: 16px;
max-height: 16px;
@ -1632,52 +1632,26 @@ toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
}
#social-share-panel {
max-height: 600px;
min-height: 100px;
max-width: 800px;
min-width: 300px;
min-width: 766px;
}
#share-container,
.social-share-frame {
background: linear-gradient(to bottom, #f0f4f7, #fafbfc);
width: 330px;
height: 150px;
/* we resize our panels dynamically, make it look nice */
transition: height 100ms ease-out, width 100ms ease-out;
}
.social-share-frame:-moz-locale-dir(ltr) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: inherit;
border-bottom-right-radius: inherit;
}
.social-share-frame:-moz-locale-dir(rtl) {
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
#social-share-panel > .social-share-toolbar:-moz-locale-dir(ltr) {
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
}
#social-share-panel > .social-share-toolbar:-moz-locale-dir(rtl) {
border-top-right-radius: inherit;
border-bottom-right-radius: inherit;
}
#social-share-provider-buttons:-moz-locale-dir(ltr) {
#social-share-panel > .social-share-toolbar {
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
border-top-right-radius: inherit;
}
#social-share-provider-buttons:-moz-locale-dir(rtl) {
#social-share-provider-buttons {
border-top-left-radius: inherit;
border-top-right-radius: inherit;
border-bottom-right-radius: inherit;
}
/* social recommending panel */

View File

@ -686,6 +686,8 @@ browser.jar:
skin/classic/aero/browser/tabbrowser/tab-background-end@2x.png (tabbrowser/tab-background-end@2x.png)
skin/classic/aero/browser/tabbrowser/tab-overflow-indicator.png (../shared/tabbrowser/tab-overflow-indicator.png)
skin/classic/aero/browser/tabbrowser/pendingpaint.png (../shared/tabbrowser/pendingpaint.png)
# NOTE: The following two files (tab-selected-end.svg, tab-selected-start.svg) get pre-processed in
# Makefile.in with a non-default marker of "%" and the result of that gets packaged.
skin/classic/aero/browser/tabbrowser/tab-selected-end.svg (tab-selected-end-aero.svg)

View File

@ -39,10 +39,18 @@ public:
}
private:
class StackClassChecker : public MatchFinder::MatchCallback {
class ScopeChecker : public MatchFinder::MatchCallback {
public:
enum Scope {
eLocal,
eGlobal
};
ScopeChecker(Scope scope_) :
scope(scope_) {}
virtual void run(const MatchFinder::MatchResult &Result);
void noteInferred(QualType T, DiagnosticsEngine &Diag);
private:
Scope scope;
};
class NonHeapClassChecker : public MatchFinder::MatchCallback {
@ -56,9 +64,16 @@ private:
virtual void run(const MatchFinder::MatchResult &Result);
};
StackClassChecker stackClassChecker;
class TrivialCtorDtorChecker : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult &Result);
};
ScopeChecker stackClassChecker;
ScopeChecker globalClassChecker;
NonHeapClassChecker nonheapClassChecker;
ArithmeticArgChecker arithmeticArgChecker;
TrivialCtorDtorChecker trivialCtorDtorChecker;
MatchFinder astMatcher;
};
@ -236,7 +251,8 @@ public:
enum ClassAllocationNature {
RegularClass = 0,
NonHeapClass = 1,
StackClass = 2
StackClass = 2,
GlobalClass = 3
};
/// A cached data of whether classes are stack classes, non-heap classes, or
@ -255,6 +271,9 @@ ClassAllocationNature getClassAttrs(CXXRecordDecl *D) {
// Base class: anyone with this annotation is obviously a stack class
if (MozChecker::hasCustomAnnotation(D, "moz_stack_class"))
return StackClass;
// Base class: anyone with this annotation is obviously a global class
if (MozChecker::hasCustomAnnotation(D, "moz_global_class"))
return GlobalClass;
// See if we cached the result.
DenseMap<const CXXRecordDecl *,
@ -283,6 +302,10 @@ ClassAllocationNature getClassAttrs(CXXRecordDecl *D) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), StackClass);
return StackClass;
} else if (super == GlobalClass) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), GlobalClass);
return GlobalClass;
} else if (super == NonHeapClass) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), NonHeapClass);
@ -297,6 +320,9 @@ ClassAllocationNature getClassAttrs(CXXRecordDecl *D) {
if (fieldType == StackClass) {
inferredAllocCauses[D] = std::make_pair(*field, StackClass);
return StackClass;
} else if (fieldType == GlobalClass) {
inferredAllocCauses[D] = std::make_pair(*field, GlobalClass);
return GlobalClass;
} else if (fieldType == NonHeapClass) {
inferredAllocCauses[D] = std::make_pair(*field, NonHeapClass);
type = NonHeapClass;
@ -324,6 +350,12 @@ AST_MATCHER(QualType, stackClassAggregate) {
return getClassAttrs(Node) == StackClass;
}
/// This matcher will match any class with the global class assertion or an
/// array of such classes.
AST_MATCHER(QualType, globalClassAggregate) {
return getClassAttrs(Node) == GlobalClass;
}
/// This matcher will match any class with the stack class assertion or an
/// array of such classes.
AST_MATCHER(QualType, nonheapClassAggregate) {
@ -342,6 +374,12 @@ AST_MATCHER(Decl, noArithmeticExprInArgs) {
return MozChecker::hasCustomAnnotation(&Node, "moz_no_arith_expr_in_arg");
}
/// This matcher will match any C++ class that is marked as having a trivial
/// constructor and destructor.
AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) {
return MozChecker::hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor");
}
/// This matcher will match all arithmetic binary operators.
AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
BinaryOperatorKind opcode = Node.getOpcode();
@ -393,7 +431,10 @@ bool isPlacementNew(const CXXNewExpr *expr) {
return true;
}
DiagnosticsMatcher::DiagnosticsMatcher() {
DiagnosticsMatcher::DiagnosticsMatcher()
: stackClassChecker(ScopeChecker::eLocal),
globalClassChecker(ScopeChecker::eGlobal)
{
// Stack class assertion: non-local variables of a stack class are forbidden
// (non-localness checked in the callback)
astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"),
@ -402,14 +443,22 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
astMatcher.addMatcher(newExpr(hasType(pointerType(
pointee(stackClassAggregate())
))).bind("node"), &stackClassChecker);
// Global class assertion: non-global variables of a global class are forbidden
// (globalness checked in the callback)
astMatcher.addMatcher(varDecl(hasType(globalClassAggregate())).bind("node"),
&globalClassChecker);
// Global class assertion: new global class is forbidden
astMatcher.addMatcher(newExpr(hasType(pointerType(
pointee(globalClassAggregate())
))).bind("node"), &globalClassChecker);
// Non-heap class assertion: new non-heap class is forbidden (unless placement
// new)
astMatcher.addMatcher(newExpr(hasType(pointerType(
pointee(nonheapClassAggregate())
))).bind("node"), &nonheapClassChecker);
// Any heap allocation function that returns a non-heap or a stack class is
// definitely doing something wrong
// Any heap allocation function that returns a non-heap or a stack class or
// a global class is definitely doing something wrong
astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
returns(pointerType(pointee(nonheapClassAggregate()))))))).bind("node"),
&nonheapClassChecker);
@ -417,6 +466,10 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
returns(pointerType(pointee(stackClassAggregate()))))))).bind("node"),
&stackClassChecker);
astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
returns(pointerType(pointee(globalClassAggregate()))))))).bind("node"),
&globalClassChecker);
astMatcher.addMatcher(callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()),
anyOf(
hasDescendant(binaryOperator(allOf(binaryArithmeticOperator(),
@ -443,60 +496,81 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
)
)).bind("call"),
&arithmeticArgChecker);
astMatcher.addMatcher(recordDecl(hasTrivialCtorDtor()).bind("node"),
&trivialCtorDtorChecker);
}
void DiagnosticsMatcher::StackClassChecker::run(
void DiagnosticsMatcher::ScopeChecker::run(
const MatchFinder::MatchResult &Result) {
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
unsigned globalID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "variable of type %0 only valid as global");
unsigned errorID = (scope == eGlobal) ? globalID : stackID;
if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
// Ignore the match if it's a local variable.
if (d->hasLocalStorage())
return;
if (scope == eLocal) {
// Ignore the match if it's a local variable.
if (d->hasLocalStorage())
return;
} else if (scope == eGlobal) {
// Ignore the match if it's a global variable or a static member of a
// class. The latter is technically not in the global scope, but for the
// use case of classes that intend to avoid introducing static
// initializers that is fine.
if (d->hasGlobalStorage() && !d->isStaticLocal())
return;
}
Diag.Report(d->getLocation(), stackID) << d->getType();
Diag.Report(d->getLocation(), errorID) << d->getType();
noteInferred(d->getType(), Diag);
} else if (const CXXNewExpr *expr =
Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
// If it's placement new, then this match doesn't count.
if (isPlacementNew(expr))
if (scope == eLocal && isPlacementNew(expr))
return;
Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
Diag.Report(expr->getStartLoc(), errorID) << expr->getAllocatedType();
noteInferred(expr->getAllocatedType(), Diag);
} else if (const CallExpr *expr =
Result.Nodes.getNodeAs<CallExpr>("node")) {
QualType badType = expr->getCallReturnType()->getPointeeType();
Diag.Report(expr->getLocStart(), stackID) << badType;
Diag.Report(expr->getLocStart(), errorID) << badType;
noteInferred(badType, Diag);
}
}
void DiagnosticsMatcher::StackClassChecker::noteInferred(QualType T,
void DiagnosticsMatcher::ScopeChecker::noteInferred(QualType T,
DiagnosticsEngine &Diag) {
unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a stack class because it inherits from a stack class %1");
"%0 is a %2 class because it inherits from a %2 class %1");
unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a stack class because member %1 is a stack class %2");
"%0 is a %3 class because member %1 is a %3 class %2");
const char* attribute = (scope == eGlobal) ?
"moz_global_class" : "moz_stack_class";
const char* type = (scope == eGlobal) ?
"global" : "stack";
// Find the CXXRecordDecl that is the stack class of interest
// Find the CXXRecordDecl that is the local/global class of interest
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
// Direct result, we're done.
if (MozChecker::hasCustomAnnotation(clazz, "moz_stack_class"))
if (MozChecker::hasCustomAnnotation(clazz, attribute))
return;
const Decl *cause = inferredAllocCauses[clazz].first;
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
Diag.Report(clazz->getLocation(), inheritsID) <<
T << CRD->getDeclName() << type;
} else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
Diag.Report(FD->getLocation(), memberID) <<
T << FD << FD->getType() << type;
}
// Recursively follow this back.
noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
}
@ -561,6 +635,19 @@ void DiagnosticsMatcher::ArithmeticArgChecker::run(
}
}
void DiagnosticsMatcher::TrivialCtorDtorChecker::run(
const MatchFinder::MatchResult &Result) {
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "class %0 must have trivial constructors and destructors");
const CXXRecordDecl *node = Result.Nodes.getNodeAs<CXXRecordDecl>("node");
bool badCtor = !node->hasTrivialDefaultConstructor();
bool badDtor = !node->hasTrivialDestructor();
if (badCtor || badDtor)
Diag.Report(node->getLocStart(), errorID) << node;
}
class MozCheckAction : public PluginASTAction {
public:
ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, StringRef fileName) override {

View File

@ -0,0 +1,52 @@
#define MOZ_GLOBAL_CLASS __attribute__((annotate("moz_global_class")))
#include <stddef.h>
struct MOZ_GLOBAL_CLASS Global {
int i;
void *operator new(size_t x) throw() { return 0; }
void *operator new(size_t blah, char *buffer) { return buffer; }
};
template <class T>
struct MOZ_GLOBAL_CLASS TemplateClass {
T i;
};
void gobble(void *) { }
void misuseGlobalClass(int len) {
Global notValid; // expected-error {{variable of type 'Global' only valid as global}}
Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}}
static Global valid; // expected-error {{variable of type 'Global' only valid as global}}
static Global alsoValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}}
gobble(&valid);
gobble(&notValid);
gobble(&alsoValid[0]);
gobble(new Global); // expected-error {{variable of type 'Global' only valid as global}}
gobble(new Global[10]); // expected-error {{variable of type 'Global' only valid as global}}
gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid as global}}
gobble(len <= 5 ? &valid : new Global); // expected-error {{variable of type 'Global' only valid as global}}
char buffer[sizeof(Global)];
gobble(new (buffer) Global); // expected-error {{variable of type 'Global' only valid as global}}
}
Global valid;
struct RandomClass {
Global nonstaticMember; // expected-note {{'RandomClass' is a global class because member 'nonstaticMember' is a global class 'Global'}}
static Global staticMember;
};
struct MOZ_GLOBAL_CLASS RandomGlobalClass {
Global nonstaticMember;
static Global staticMember;
};
struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global class because it inherits from a global class 'Global'}}
struct MOZ_GLOBAL_CLASS GoodInherit : Global {};
void misuseGlobalClassEvenMore(int len) {
BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid as global}}
RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid as global}}
}

View File

@ -0,0 +1,58 @@
#define MOZ_TRIVIAL_CTOR_DTOR __attribute__((annotate("moz_trivial_ctor_dtor")))
struct MOZ_TRIVIAL_CTOR_DTOR EmptyClass{};
template <class T>
struct MOZ_TRIVIAL_CTOR_DTOR TemplateEmptyClass{};
struct MOZ_TRIVIAL_CTOR_DTOR BadUserDefinedCtor { // expected-error {{class 'BadUserDefinedCtor' must have trivial constructors and destructors}}
BadUserDefinedCtor() {}
};
struct MOZ_TRIVIAL_CTOR_DTOR BadUserDefinedDtor { // expected-error {{class 'BadUserDefinedDtor' must have trivial constructors and destructors}}
~BadUserDefinedDtor() {}
};
struct MOZ_TRIVIAL_CTOR_DTOR BadVirtualDtor { // expected-error {{class 'BadVirtualDtor' must have trivial constructors and destructors}}
virtual ~BadVirtualDtor() {}
};
struct MOZ_TRIVIAL_CTOR_DTOR BadVirtualMember { // expected-error {{class 'BadVirtualMember' must have trivial constructors and destructors}}
virtual void f();
};
void foo();
struct MOZ_TRIVIAL_CTOR_DTOR BadNonEmptyCtorDtor { // expected-error {{class 'BadNonEmptyCtorDtor' must have trivial constructors and destructors}}
BadNonEmptyCtorDtor() { foo(); }
~BadNonEmptyCtorDtor() { foo(); }
};
struct NonTrivialCtor {
NonTrivialCtor() { foo(); }
};
struct NonTrivialDtor {
~NonTrivialDtor() { foo(); }
};
struct VirtualMember {
virtual void f();
};
struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialCtorInBase : NonTrivialCtor { // expected-error {{class 'BadNonTrivialCtorInBase' must have trivial constructors and destructors}}
};
struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialDtorInBase : NonTrivialDtor { // expected-error {{class 'BadNonTrivialDtorInBase' must have trivial constructors and destructors}}
};
struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialCtorInMember { // expected-error {{class 'BadNonTrivialCtorInMember' must have trivial constructors and destructors}}
NonTrivialCtor m;
};
struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialDtorInMember { // expected-error {{class 'BadNonTrivialDtorInMember' must have trivial constructors and destructors}}
NonTrivialDtor m;
};
struct MOZ_TRIVIAL_CTOR_DTOR BadVirtualMemberInMember { // expected-error {{class 'BadVirtualMemberInMember' must have trivial constructors and destructors}}
VirtualMember m;
};

View File

@ -7,10 +7,12 @@
SOURCES += [
'TestBadImplicitConversionCtor.cpp',
'TestCustomHeap.cpp',
'TestGlobalClass.cpp',
'TestMustOverride.cpp',
'TestNoArithmeticExprInArgument.cpp',
'TestNonHeapClass.cpp',
'TestStackClass.cpp',
'TestTrivialCtorDtor.cpp',
]
DISABLE_STL_WRAPPING = True

View File

@ -2274,8 +2274,17 @@ ia64*-hpux*)
dnl For profile-guided optimization
PROFILE_GEN_CFLAGS="-GL"
PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT"
PROFILE_USE_CFLAGS="-GL"
PROFILE_USE_LDFLAGS="-LTCG:PGOPTIMIZE"
dnl XXX: PGO builds can fail with warnings treated as errors,
dnl specifically "no profile data available" appears to be
dnl treated as an error sometimes. This might be a consequence
dnl of using WARNINGS_AS_ERRORS in some modules, combined
dnl with the linker doing most of the work in the whole-program
dnl optimization/PGO case. I think it's probably a compiler bug,
dnl but we work around it here.
PROFILE_USE_CFLAGS="-GL -wd4624 -wd4952"
dnl XXX: should be -LTCG:PGOPTIMIZE, but that fails on libxul.
dnl Probably also a compiler bug, but what can you do?
PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE"
LDFLAGS="$LDFLAGS -DYNAMICBASE"
dnl Minimum reqiurement of Gecko is VS2010 or later which supports
dnl both SSSE3 and SSE4.1.

View File

@ -1096,6 +1096,13 @@ nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString,
return NS_OK;
}
// ... unless there are no dots, and a slash, and alpha characters, and this is a valid host:
if (firstDotLoc == uint32_t(kNotFound) && lastSlashLoc != uint32_t(kNotFound) &&
hasAsciiAlpha && isValidAsciiHost) {
return NS_OK;
}
// If we get here, we don't have a valid URI, or we did but the
// host is not whitelisted, so we do a keyword search *anyway*:
rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo, aPostData);

View File

@ -36,6 +36,11 @@ support-files =
print_postdata.sjs
test-form_sjis.html
timelineMarkers-04.html
browser_timelineMarkers-frame-02.js
browser_timelineMarkers-frame-03.js
browser_timelineMarkers-frame-04.js
head.js
frame-head.js
[browser_bug134911.js]
skip-if = e10s # Bug ?????? - BrowserSetForcedCharacterSet() in browser.js references docShell
@ -98,8 +103,5 @@ skip-if = e10s
[browser_search_notification.js]
[browser_timelineMarkers-01.js]
[browser_timelineMarkers-02.js]
skip-if = e10s
[browser_timelineMarkers-03.js]
skip-if = e10s
[browser_timelineMarkers-04.js]
skip-if = e10s

View File

@ -3,9 +3,6 @@
"use strict";
// Test that the docShell profile timeline API returns the right markers when
// restyles, reflows and paints occur
let URL = '<!DOCTYPE html><style>' +
'body {margin:0; padding: 0;} ' +
'div {width:100px;height:100px;background:red;} ' +
@ -13,157 +10,6 @@ let URL = '<!DOCTYPE html><style>' +
'.change-color {width:50px;height:50px;background:yellow;} ' +
'.add-class {}' +
'</style><div></div>';
URL = "data:text/html;charset=utf8," + encodeURIComponent(URL);
let TESTS = [{
desc: "Changing the width of the test element",
searchFor: "Paint",
setup: function(div) {
div.setAttribute("class", "resize-change-color");
},
check: function(markers) {
ok(markers.length > 0, "markers were returned");
console.log(markers);
info(JSON.stringify(markers.filter(m => m.name == "Paint")));
ok(markers.some(m => m.name == "Reflow"), "markers includes Reflow");
ok(markers.some(m => m.name == "Paint"), "markers includes Paint");
for (let marker of markers.filter(m => m.name == "Paint")) {
// This change should generate at least one rectangle.
ok(marker.rectangles.length >= 1, "marker has one rectangle");
// One of the rectangles should contain the div.
ok(marker.rectangles.some(r => rectangleContains(r, 0, 0, 100, 100)));
}
ok(markers.some(m => m.name == "Styles"), "markers includes Restyle");
}
}, {
desc: "Changing the test element's background color",
searchFor: "Paint",
setup: function(div) {
div.setAttribute("class", "change-color");
},
check: function(markers) {
ok(markers.length > 0, "markers were returned");
ok(!markers.some(m => m.name == "Reflow"), "markers doesn't include Reflow");
ok(markers.some(m => m.name == "Paint"), "markers includes Paint");
for (let marker of markers.filter(m => m.name == "Paint")) {
// This change should generate at least one rectangle.
ok(marker.rectangles.length >= 1, "marker has one rectangle");
// One of the rectangles should contain the div.
ok(marker.rectangles.some(r => rectangleContains(r, 0, 0, 50, 50)));
}
ok(markers.some(m => m.name == "Styles"), "markers includes Restyle");
}
}, {
desc: "Changing the test element's classname",
searchFor: "Paint",
setup: function(div) {
div.setAttribute("class", "change-color add-class");
},
check: function(markers) {
ok(markers.length > 0, "markers were returned");
ok(!markers.some(m => m.name == "Reflow"), "markers doesn't include Reflow");
ok(!markers.some(m => m.name == "Paint"), "markers doesn't include Paint");
ok(markers.some(m => m.name == "Styles"), "markers includes Restyle");
}
}, {
desc: "sync console.time/timeEnd",
searchFor: "ConsoleTime",
setup: function(div, docShell) {
content.console.time("FOOBAR");
content.console.timeEnd("FOOBAR");
let markers = docShell.popProfileTimelineMarkers();
is(markers.length, 1, "Got one marker");
is(markers[0].name, "ConsoleTime", "Got ConsoleTime marker");
is(markers[0].causeName, "FOOBAR", "Got ConsoleTime FOOBAR detail");
content.console.time("FOO");
content.setTimeout(() => {
content.console.time("BAR");
content.setTimeout(() => {
content.console.timeEnd("FOO");
content.console.timeEnd("BAR");
}, 100);
}, 100);
},
check: function(markers) {
is(markers.length, 2, "Got 2 markers");
is(markers[0].name, "ConsoleTime", "Got first ConsoleTime marker");
is(markers[0].causeName, "FOO", "Got ConsoleTime FOO detail");
is(markers[1].name, "ConsoleTime", "Got second ConsoleTime marker");
is(markers[1].causeName, "BAR", "Got ConsoleTime BAR detail");
}
}];
let test = Task.async(function*() {
waitForExplicitFinish();
yield openUrl("data:text/html;charset=utf8," + encodeURIComponent(URL));
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
let div = content.document.querySelector("div");
info("Start recording");
docShell.recordProfileTimelineMarkers = true;
for (let {desc, searchFor, setup, check} of TESTS) {
info("Running test: " + desc);
info("Flushing the previous markers if any");
docShell.popProfileTimelineMarkers();
info("Running the test setup function");
let onMarkers = waitForMarkers(docShell, searchFor);
setup(div, docShell);
info("Waiting for new markers on the docShell");
let markers = yield onMarkers;
info("Running the test check function");
check(markers);
}
info("Stop recording");
docShell.recordProfileTimelineMarkers = false;
gBrowser.removeCurrentTab();
finish();
});
function openUrl(url) {
return new Promise(function(resolve, reject) {
window.focus();
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
let linkedBrowser = tab.linkedBrowser;
linkedBrowser.addEventListener("load", function onload() {
linkedBrowser.removeEventListener("load", onload, true);
resolve(tab);
}, true);
});
}
function waitForMarkers(docshell, searchFor) {
return new Promise(function(resolve, reject) {
let waitIterationCount = 0;
let maxWaitIterationCount = 10; // Wait for 2sec maximum
let markers = [];
let interval = setInterval(() => {
let newMarkers = docshell.popProfileTimelineMarkers();
markers = [...markers, ...newMarkers];
if (newMarkers.some(m => m.name == searchFor) ||
waitIterationCount > maxWaitIterationCount) {
clearInterval(interval);
resolve(markers);
}
waitIterationCount++;
}, 200);
});
}
function rectangleContains(rect, x, y, width, height) {
return rect.x <= x && rect.y <= y && rect.width >= width &&
rect.height >= height;
}
let test = makeTimelineTest("browser_timelineMarkers-frame-02.js", URL);

View File

@ -3,139 +3,6 @@
"use strict";
// Test that the docShell profile timeline API returns the right
// markers for DOM events.
let URL = "data:text/html;charset=utf-8,<p>Test page</p>";
let TESTS = [{
desc: "Event dispatch with single handler",
setup: function() {
content.document.body.addEventListener("dog",
function(e) { console.log("hi"); },
true);
content.document.body.dispatchEvent(new Event("dog"));
},
check: function(markers) {
is(markers.length, 1, "Got 1 marker");
is(markers[0].type, "dog", "Got dog event name");
is(markers[0].eventPhase, 2, "Got phase 2");
}
}, {
desc: "Event dispatch with a second handler",
setup: function() {
content.document.body.addEventListener("dog",
function(e) { console.log("hi"); },
false);
content.document.body.dispatchEvent(new Event("dog"));
},
check: function(markers) {
is(markers.length, 2, "Got 2 markers");
}
}, {
desc: "Event targeted at child",
setup: function() {
let child = content.document.body.firstElementChild;
child.addEventListener("dog", function(e) { });
child.dispatchEvent(new Event("dog"));
},
check: function(markers) {
is(markers.length, 2, "Got 2 markers");
is(markers[0].eventPhase, 1, "Got phase 1 marker");
is(markers[1].eventPhase, 2, "Got phase 2 marker");
}
}, {
desc: "Event dispatch on a new document",
setup: function() {
let doc = content.document.implementation.createHTMLDocument("doc");
let p = doc.createElement("p");
p.innerHTML = "inside";
doc.body.appendChild(p);
p.addEventListener("zebra", function(e) {console.log("hi");});
p.dispatchEvent(new Event("zebra"));
},
check: function(markers) {
is(markers.length, 1, "Got 1 marker");
}
}, {
desc: "Event dispatch on window",
setup: function() {
let doc = content.window.addEventListener("aardvark", function(e) {
console.log("I like ants!");
});
content.window.dispatchEvent(new Event("aardvark"));
},
check: function(markers) {
is(markers.length, 1, "Got 1 marker");
}
}];
let test = Task.async(function*() {
waitForExplicitFinish();
yield openUrl("data:text/html;charset=utf-8,<p>Test page</p>");
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
info("Start recording");
docShell.recordProfileTimelineMarkers = true;
for (let {desc, setup, check} of TESTS) {
info("Running test: " + desc);
info("Flushing the previous markers if any");
docShell.popProfileTimelineMarkers();
info("Running the test setup function");
let onMarkers = waitForMarkers(docShell);
setup();
info("Waiting for new markers on the docShell");
let markers = yield onMarkers;
info("Running the test check function");
check(markers.filter(m => m.name == "DOMEvent"));
}
info("Stop recording");
docShell.recordProfileTimelineMarkers = false;
gBrowser.removeCurrentTab();
finish();
});
function openUrl(url) {
return new Promise(function(resolve, reject) {
window.focus();
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
let linkedBrowser = tab.linkedBrowser;
linkedBrowser.addEventListener("load", function onload() {
linkedBrowser.removeEventListener("load", onload, true);
resolve(tab);
}, true);
});
}
function waitForMarkers(docshell) {
return new Promise(function(resolve, reject) {
let waitIterationCount = 0;
let maxWaitIterationCount = 10; // Wait for 2sec maximum
let interval = setInterval(() => {
let markers = docshell.popProfileTimelineMarkers();
if (markers.length > 0) {
clearInterval(interval);
resolve(markers);
}
if (waitIterationCount > maxWaitIterationCount) {
clearInterval(interval);
resolve([]);
}
waitIterationCount++;
}, 200);
});
}
let test = makeTimelineTest("browser_timelineMarkers-frame-03.js", URL);

View File

@ -3,91 +3,6 @@
"use strict";
// Test that the docShell profile timeline API returns the right
// markers for XMLHttpRequest events.
const URL = "http://mochi.test:8888/browser/docshell/test/browser/timelineMarkers-04.html";
let TESTS = [{
desc: "Event dispatch from XMLHttpRequest",
setup: function() {
content.dispatchEvent(new Event("dog"));
},
check: function(markers) {
// One subtlety here is that we have five events: the event we
// inject in "setup", plus the four state transition events. The
// first state transition is reported synchronously and so should
// show up as a nested marker.
is(markers.length, 5, "Got 5 markers");
}
}];
let test = Task.async(function*() {
waitForExplicitFinish();
const testDir = "http://mochi.test:8888/browser/docshell/test/browser/";
const testName = "timelineMarkers-04.html";
yield openUrl(testDir + testName);
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
info("Start recording");
docShell.recordProfileTimelineMarkers = true;
for (let {desc, setup, check} of TESTS) {
info("Running test: " + desc);
info("Flushing the previous markers if any");
docShell.popProfileTimelineMarkers();
info("Running the test setup function");
let onMarkers = waitForDOMMarkers(docShell, 5);
setup();
info("Waiting for new markers on the docShell");
let markers = yield onMarkers;
info("Running the test check function");
check(markers);
}
info("Stop recording");
docShell.recordProfileTimelineMarkers = false;
gBrowser.removeCurrentTab();
finish();
});
function openUrl(url) {
return new Promise(function(resolve, reject) {
window.focus();
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
let linkedBrowser = tab.linkedBrowser;
linkedBrowser.addEventListener("load", function onload() {
linkedBrowser.removeEventListener("load", onload, true);
resolve(tab);
}, true);
});
}
function waitForDOMMarkers(docshell, numExpected) {
return new Promise(function(resolve, reject) {
let waitIterationCount = 0;
let maxWaitIterationCount = 10; // Wait for 2sec maximum
let markers = [];
let interval = setInterval(() => {
let newMarkers = docshell.popProfileTimelineMarkers();
markers = [...markers, ...newMarkers.filter(m => m.name == "DOMEvent")];
if (markers.length >= numExpected
|| waitIterationCount > maxWaitIterationCount) {
clearInterval(interval);
resolve(markers);
}
waitIterationCount++;
}, 200);
});
}
let test = makeTimelineTest("browser_timelineMarkers-frame-04.js", URL);

View File

@ -0,0 +1,95 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the docShell profile timeline API returns the right markers when
// restyles, reflows and paints occur
function rectangleContains(rect, x, y, width, height) {
return rect.x <= x && rect.y <= y && rect.width >= width &&
rect.height >= height;
}
let TESTS = [{
desc: "Changing the width of the test element",
searchFor: "Paint",
setup: function(docShell) {
let div = content.document.querySelector("div");
div.setAttribute("class", "resize-change-color");
},
check: function(markers) {
ok(markers.length > 0, "markers were returned");
console.log(markers);
info(JSON.stringify(markers.filter(m => m.name == "Paint")));
ok(markers.some(m => m.name == "Reflow"), "markers includes Reflow");
ok(markers.some(m => m.name == "Paint"), "markers includes Paint");
for (let marker of markers.filter(m => m.name == "Paint")) {
// This change should generate at least one rectangle.
ok(marker.rectangles.length >= 1, "marker has one rectangle");
// One of the rectangles should contain the div.
ok(marker.rectangles.some(r => rectangleContains(r, 0, 0, 100, 100)));
}
ok(markers.some(m => m.name == "Styles"), "markers includes Restyle");
}
}, {
desc: "Changing the test element's background color",
searchFor: "Paint",
setup: function(docShell) {
let div = content.document.querySelector("div");
div.setAttribute("class", "change-color");
},
check: function(markers) {
ok(markers.length > 0, "markers were returned");
ok(!markers.some(m => m.name == "Reflow"), "markers doesn't include Reflow");
ok(markers.some(m => m.name == "Paint"), "markers includes Paint");
for (let marker of markers.filter(m => m.name == "Paint")) {
// This change should generate at least one rectangle.
ok(marker.rectangles.length >= 1, "marker has one rectangle");
// One of the rectangles should contain the div.
ok(marker.rectangles.some(r => rectangleContains(r, 0, 0, 50, 50)));
}
ok(markers.some(m => m.name == "Styles"), "markers includes Restyle");
}
}, {
desc: "Changing the test element's classname",
searchFor: "Paint",
setup: function(docShell) {
let div = content.document.querySelector("div");
div.setAttribute("class", "change-color add-class");
},
check: function(markers) {
ok(markers.length > 0, "markers were returned");
ok(!markers.some(m => m.name == "Reflow"), "markers doesn't include Reflow");
ok(!markers.some(m => m.name == "Paint"), "markers doesn't include Paint");
ok(markers.some(m => m.name == "Styles"), "markers includes Restyle");
}
}, {
desc: "sync console.time/timeEnd",
searchFor: "ConsoleTime",
setup: function(docShell) {
content.console.time("FOOBAR");
content.console.timeEnd("FOOBAR");
let markers = docShell.popProfileTimelineMarkers();
is(markers.length, 1, "Got one marker");
is(markers[0].name, "ConsoleTime", "Got ConsoleTime marker");
is(markers[0].causeName, "FOOBAR", "Got ConsoleTime FOOBAR detail");
content.console.time("FOO");
content.setTimeout(() => {
content.console.time("BAR");
content.setTimeout(() => {
content.console.timeEnd("FOO");
content.console.timeEnd("BAR");
}, 100);
}, 100);
},
check: function(markers) {
is(markers.length, 2, "Got 2 markers");
is(markers[0].name, "ConsoleTime", "Got first ConsoleTime marker");
is(markers[0].causeName, "FOO", "Got ConsoleTime FOO detail");
is(markers[1].name, "ConsoleTime", "Got second ConsoleTime marker");
is(markers[1].causeName, "BAR", "Got ConsoleTime BAR detail");
}
}];
timelineContentTest(TESTS);

View File

@ -0,0 +1,91 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the docShell profile timeline API returns the right
// markers for DOM events.
let TESTS = [{
desc: "Event dispatch with single handler",
searchFor: 'DOMEvent',
setup: function(docShell) {
content.document.body.addEventListener("dog",
function(e) { console.log("hi"); },
true);
content.document.body.dispatchEvent(new content.Event("dog"));
},
check: function(markers) {
markers = markers.filter(m => m.name == 'DOMEvent');
is(markers.length, 1, "Got 1 marker");
is(markers[0].type, "dog", "Got dog event name");
is(markers[0].eventPhase, 2, "Got phase 2");
}
}, {
desc: "Event dispatch with a second handler",
searchFor: function(markers) {
return markers.filter(m => m.name == 'DOMEvent').length >= 2;
},
setup: function(docShell) {
content.document.body.addEventListener("dog",
function(e) { console.log("hi"); },
false);
content.document.body.dispatchEvent(new content.Event("dog"));
},
check: function(markers) {
markers = markers.filter(m => m.name == 'DOMEvent');
is(markers.length, 2, "Got 2 markers");
}
}, {
desc: "Event targeted at child",
searchFor: function(markers) {
return markers.filter(m => m.name == 'DOMEvent').length >= 2;
},
setup: function(docShell) {
let child = content.document.body.firstElementChild;
child.addEventListener("dog", function(e) { });
child.dispatchEvent(new content.Event("dog"));
},
check: function(markers) {
markers = markers.filter(m => m.name == 'DOMEvent');
is(markers.length, 2, "Got 2 markers");
is(markers[0].eventPhase, 1, "Got phase 1 marker");
is(markers[1].eventPhase, 2, "Got phase 2 marker");
}
}, {
desc: "Event dispatch on a new document",
searchFor: function(markers) {
return markers.filter(m => m.name == 'DOMEvent').length >= 2;
},
setup: function(docShell) {
let doc = content.document.implementation.createHTMLDocument("doc");
let p = doc.createElement("p");
p.innerHTML = "inside";
doc.body.appendChild(p);
p.addEventListener("zebra", function(e) {console.log("hi");});
p.dispatchEvent(new content.Event("zebra"));
},
check: function(markers) {
markers = markers.filter(m => m.name == 'DOMEvent');
is(markers.length, 1, "Got 1 marker");
}
}, {
desc: "Event dispatch on window",
searchFor: function(markers) {
return markers.filter(m => m.name == 'DOMEvent').length >= 2;
},
setup: function(docShell) {
let doc = content.window.addEventListener("aardvark", function(e) {
console.log("I like ants!");
});
content.window.dispatchEvent(new content.Event("aardvark"));
},
check: function(markers) {
markers = markers.filter(m => m.name == 'DOMEvent');
is(markers.length, 1, "Got 1 marker");
}
}];
timelineContentTest(TESTS);

View File

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the docShell profile timeline API returns the right
// markers for XMLHttpRequest events.
let TESTS = [{
desc: "Event dispatch from XMLHttpRequest",
searchFor: function(markers) {
return markers.filter(m => m.name == "DOMEvent").length >= 5;
},
setup: function(docShell) {
content.dispatchEvent(new content.Event("dog"));
},
check: function(markers) {
markers = markers.filter(m => m.name == "DOMEvent");
// One subtlety here is that we have five events: the event we
// inject in "setup", plus the four state transition events. The
// first state transition is reported synchronously and so should
// show up as a nested marker.
is(markers.length, 5, "Got 5 markers");
}
}];
timelineContentTest(TESTS);

View File

@ -0,0 +1,105 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Functions that are automatically loaded as frame scripts for
// timeline tests.
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
let { Promise } = Cu.import('resource://gre/modules/Promise.jsm', {});
// Functions that look like mochitest functions but forward to the
// browser process.
this.ok = function(value, message) {
sendAsyncMessage("browser:test:ok", {
value: !!value,
message: message});
}
this.is = function(v1, v2, message) {
ok(v1 == v2, message);
}
this.info = function(message) {
sendAsyncMessage("browser:test:info", {message: message});
}
this.finish = function() {
sendAsyncMessage("browser:test:finish");
}
/* Start a task that runs some timeline tests in the ordinary way.
*
* @param array tests
* The tests to run. This is an array where each element
* is of the form { desc, searchFor, setup, check }.
*
* desc is the test description, a string.
* searchFor is a string or a function
* If a string, then when a marker with this name is
* found, marker-reading is stopped.
* If a function, then the accumulated marker array is
* passed to it, and marker reading stops when it returns
* true.
* setup is a function that takes the docshell as an argument.
* It should start the test.
* check is a function that takes an array of markers
* as an argument and checks the results of the test.
*/
this.timelineContentTest = function(tests) {
Task.spawn(function*() {
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
info("Start recording");
docShell.recordProfileTimelineMarkers = true;
for (let {desc, searchFor, setup, check} of tests) {
info("Running test: " + desc);
info("Flushing the previous markers if any");
docShell.popProfileTimelineMarkers();
info("Running the test setup function");
let onMarkers = timelineWaitForMarkers(docShell, searchFor);
setup(docShell);
info("Waiting for new markers on the docShell");
let markers = yield onMarkers;
info("Running the test check function");
check(markers);
}
info("Stop recording");
docShell.recordProfileTimelineMarkers = false;
finish();
});
}
function timelineWaitForMarkers(docshell, searchFor) {
if (typeof(searchFor) == "string") {
let f = function (markers) {
return markers.some(m => m.name == searchFor);
};
searchFor = f;
}
return new Promise(function(resolve, reject) {
let waitIterationCount = 0;
let maxWaitIterationCount = 10; // Wait for 2sec maximum
let markers = [];
let interval = content.setInterval(() => {
let newMarkers = docshell.popProfileTimelineMarkers();
markers = [...markers, ...newMarkers];
if (searchFor(markers) || waitIterationCount > maxWaitIterationCount) {
content.clearInterval(interval);
resolve(markers);
}
waitIterationCount++;
}, 200);
});
}

View File

@ -0,0 +1,53 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* Helper function for timeline tests. Returns an async task that is
* suitable for use as a particular timeline test.
* @param string frameScriptName
* Base name of the frame script file.
* @param string url
* URL to load.
*/
function makeTimelineTest(frameScriptName, url) {
info("in timelineTest");
return Task.async(function*() {
info("in in timelineTest");
waitForExplicitFinish();
yield timelineTestOpenUrl(url);
const here = "chrome://mochitests/content/browser/docshell/test/browser/";
let mm = gBrowser.selectedBrowser.messageManager;
mm.loadFrameScript(here + "frame-head.js", false);
mm.loadFrameScript(here + frameScriptName, false);
// Set up some listeners so that timeline tests running in the
// content process can forward their results to the main process.
mm.addMessageListener("browser:test:ok", function(message) {
ok(message.data.value, message.data.message);
});
mm.addMessageListener("browser:test:info", function(message) {
info(message.data.message);
});
mm.addMessageListener("browser:test:finish", function(ignore) {
finish();
gBrowser.removeCurrentTab();
});
});
}
/* Open a URL for a timeline test. */
function timelineTestOpenUrl(url) {
return new Promise(function(resolve, reject) {
window.focus();
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
let linkedBrowser = tab.linkedBrowser;
linkedBrowser.addEventListener("load", function onload() {
linkedBrowser.removeEventListener("load", onload, true);
resolve(tab);
}, true);
});
}

View File

@ -375,6 +375,14 @@ let testcases = [ {
protocolChange: true,
affectedByWhitelist: true,
affectedByDNSForSingleHosts: true,
}, {
input: "5/2",
fixedURI: "http://5/2",
alternateURI: "http://www.5.com/2",
keywordLookup: true,
protocolChange: true,
affectedByWhitelist: true,
affectedByDNSForSingleHosts: true,
}, {
input: "moz ?.::%27",
keywordLookup: true,
@ -448,6 +456,32 @@ let testcases = [ {
keywordLookup: true,
protocolChange: true,
affectedByWhitelist: true
}, {
input: "mozilla/",
fixedURI: "http://mozilla/",
alternateURI: "http://www.mozilla.com/",
protocolChange: true,
affectedByWhitelist: true,
}, {
input: "mozilla",
fixedURI: "http://mozilla/",
alternateURI: "http://www.mozilla.com/",
protocolChange: true,
keywordLookup: true,
affectedByWhitelist: true,
affectedByDNSForSingleHosts: true,
}, {
input: "mozilla5/2",
fixedURI: "http://mozilla5/2",
alternateURI: "http://www.mozilla5.com/2",
protocolChange: true,
affectedByWhitelist: true,
}, {
input: "mozilla/foo",
fixedURI: "http://mozilla/foo",
alternateURI: "http://www.mozilla.com/foo",
protocolChange: true,
affectedByWhitelist: true,
}];
if (Services.appinfo.OS.toLowerCase().startsWith("win")) {
@ -467,10 +501,8 @@ if (Services.appinfo.OS.toLowerCase().startsWith("win")) {
input: "mozilla\\",
fixedURI: "http://mozilla/",
alternateURI: "http://www.mozilla.com/",
keywordLookup: true,
protocolChange: true,
affectedByWhitelist: true,
affectedByDNSForSingleHosts: true,
});
} else {
testcases.push({

View File

@ -1461,7 +1461,9 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
// Being added to a document.
SetInDocument();
if (GetCustomElementData()) {
// Attached callback must be enqueued whenever custom element is inserted into a
// document and this document has a browsing context.
if (GetCustomElementData() && aDocument->GetDocShell()) {
// Enqueue an attached callback for the custom element.
aDocument->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
}
@ -1673,7 +1675,9 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
document->ClearBoxObjectFor(this);
if (GetCustomElementData()) {
// Detached must be enqueued whenever custom element is removed from
// the document and this document has a browsing context.
if (GetCustomElementData() && document->GetDocShell()) {
// Enqueue a detached callback for the custom element.
document->EnqueueLifecycleCallback(nsIDocument::eDetached, this);
}

View File

@ -445,13 +445,27 @@ CustomElementCallback::Call()
ErrorResult rv;
switch (mType) {
case nsIDocument::eCreated:
{
// For the duration of this callback invocation, the element is being created
// flag must be set to true.
mOwnerData->mElementIsBeingCreated = true;
// The callback hasn't actually been invoked yet, but we need to flip
// this now in order to enqueue the attached callback. This is a spec
// bug (w3c bug 27437).
mOwnerData->mCreatedCallbackInvoked = true;
// If ELEMENT is in a document and this document has a browsing context,
// enqueue attached callback for ELEMENT.
nsIDocument* document = mThisObject->GetUncomposedDoc();
if (document && document->GetDocShell()) {
document->EnqueueLifecycleCallback(nsIDocument::eAttached, mThisObject);
}
static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
mOwnerData->mElementIsBeingCreated = false;
break;
}
case nsIDocument::eAttached:
static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
break;
@ -5424,23 +5438,30 @@ nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
}
void
nsDocument::SwizzleCustomElement(Element* aElement,
const nsAString& aTypeExtension,
uint32_t aNamespaceID,
ErrorResult& rv)
nsDocument::SetupCustomElement(Element* aElement,
uint32_t aNamespaceID,
const nsAString* aTypeExtension)
{
nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(aTypeExtension));
nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
if (!mRegistry || tagAtom == typeAtom) {
if (!mRegistry) {
return;
}
nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
do_GetAtom(*aTypeExtension) : tagAtom;
if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
// Custom element setup in the parser happens after the "is"
// attribute is added.
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
}
CustomElementDefinition* data;
CustomElementHashKey key(aNamespaceID, typeAtom);
if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
// The type extension doesn't exist in the registry,
// thus we don't need to swizzle, but it is possibly
// an upgrade candidate.
// thus we don't need to enqueue callback or adjust
// the "is" attribute, but it is possibly an upgrade candidate.
RegisterUnresolvedElement(aElement, typeAtom);
return;
}
@ -5452,11 +5473,6 @@ nsDocument::SwizzleCustomElement(Element* aElement,
return;
}
if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
// Swizzling in the parser happens after the "is" attribute is added.
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true);
}
// Enqueuing the created callback will set the CustomElementData on the
// element, causing prototype swizzling to occur in Element::WrapObject.
EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
@ -5472,10 +5488,9 @@ nsDocument::CreateElement(const nsAString& aTagName,
return nullptr;
}
SwizzleCustomElement(elem, aTypeExtension,
GetDefaultNamespaceID(), rv);
if (rv.Failed()) {
return nullptr;
if (!aTagName.Equals(aTypeExtension)) {
// Custom element type can not extend itself.
SetupCustomElement(elem, GetDefaultNamespaceID(), &aTypeExtension);
}
return elem.forget();
@ -5540,9 +5555,9 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
}
}
SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv);
if (rv.Failed()) {
return nullptr;
if (!aQualifiedName.Equals(aTypeExtension)) {
// A custom element type can not extend itself.
SetupCustomElement(elem, nameSpaceId, &aTypeExtension);
}
return elem.forget();
@ -5769,12 +5784,12 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
getter_AddRefs(newElement));
NS_ENSURE_SUCCESS(rv, true);
ErrorResult errorResult;
nsCOMPtr<Element> element = do_QueryInterface(newElement);
document->SwizzleCustomElement(element, elemName, definition->mNamespaceID,
errorResult);
if (errorResult.Failed()) {
return true;
if (definition->mLocalName != typeAtom) {
// This element is a custom element by extension, thus we need to
// do some special setup. For non-extended custom elements, this happens
// when the element is created.
document->SetupCustomElement(element, definition->mNamespaceID, &elemName);
}
rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval());
@ -6095,7 +6110,7 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
}
JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
nsCOMPtr<nsIAtom> nameAtom;;
nsCOMPtr<nsIAtom> nameAtom;
int32_t namespaceID = kNameSpaceID_XHTML;
JS::Rooted<JSObject*> protoObject(aCx);
{
@ -6273,16 +6288,6 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
}
EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
//XXXsmaug It is unclear if we should use GetComposedDoc() here.
if (elem->GetUncomposedDoc()) {
// Normally callbacks can not be enqueued until the created
// callback has been invoked, however, the attached callback
// in element upgrade is an exception so pretend the created
// callback has been invoked.
elem->GetCustomElementData()->mCreatedCallbackInvoked = true;
EnqueueLifecycleCallback(nsIDocument::eAttached, elem, nullptr, definition);
}
}
}

View File

@ -1557,11 +1557,12 @@ private:
public:
static void ProcessBaseElementQueue();
// Modify the prototype and "is" attribute of newly created custom elements.
virtual void SwizzleCustomElement(Element* aElement,
const nsAString& aTypeExtension,
uint32_t aNamespaceID,
mozilla::ErrorResult& rv);
// Enqueue created callback or register upgrade candidate for
// newly created custom elements, possibly extending an existing type.
// ex. <x-button>, <button is="x-button> (type extension)
virtual void SetupCustomElement(Element* aElement,
uint32_t aNamespaceID,
const nsAString* aTypeExtension);
static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);

View File

@ -2237,10 +2237,9 @@ public:
Element* aCustomElement,
mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
mozilla::dom::CustomElementDefinition* aDefinition = nullptr) = 0;
virtual void SwizzleCustomElement(Element* aElement,
const nsAString& aTypeExtension,
uint32_t aNamespaceID,
mozilla::ErrorResult& rv) = 0;
virtual void SetupCustomElement(Element* aElement,
uint32_t aNamespaceID,
const nsAString* aTypeExtension = nullptr) = 0;
virtual void
RegisterElement(JSContext* aCx, const nsAString& aName,
const mozilla::dom::ElementRegistrationOptions& aOptions,

View File

@ -363,6 +363,24 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
NS_ENSURE_SUCCESS(rv, rv);
if (clone->IsElement()) {
// The cloned node may be a custom element that may require
// enqueing created callback and prototype swizzling.
Element* elem = clone->AsElement();
if (nsContentUtils::IsCustomElementName(nodeInfo->NameAtom())) {
elem->OwnerDoc()->SetupCustomElement(elem, nodeInfo->NamespaceID());
} else {
// Check if node may be custom element by type extension.
// ex. <button is="x-button">
nsAutoString extension;
if (elem->GetAttr(kNameSpaceID_None, nsGkAtoms::is, extension) &&
!extension.IsEmpty()) {
elem->OwnerDoc()->SetupCustomElement(elem, nodeInfo->NamespaceID(),
&extension);
}
}
}
if (aParent) {
// If we're cloning we need to insert the cloned children into the cloned
// parent.

View File

@ -2654,12 +2654,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
mDecoder->SetPreservesPitch(mPreservesPitch);
mDecoder->SetPlaybackRate(mPlaybackRate);
#ifdef MOZ_EME
if (mMediaKeys) {
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
}
#endif
if (mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
mDecoder->SetMinimizePrerollUntilPlaybackStarts();
}
@ -2681,6 +2675,12 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
return rv;
}
#ifdef MOZ_EME
if (mMediaKeys) {
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
}
#endif
// Decoder successfully created, the decoder now owns the MediaResource
// which owns the channel.
mChannel = nullptr;

View File

@ -266,13 +266,7 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
return NS_ERROR_OUT_OF_MEMORY;
}
// Element may be unresolved at this point.
doc->RegisterUnresolvedElement(*aResult);
// Try to enqueue a created callback. The custom element data will be set
// and created callback will be enqueued if the custom element type
// has already been registered.
doc->EnqueueLifecycleCallback(nsIDocument::eCreated, *aResult);
doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML);
return NS_OK;
}

View File

@ -281,6 +281,9 @@ void MediaDecoder::DestroyDecodedStream()
if (GetDecodedStream()) {
GetStateMachine()->ResyncMediaStreamClock();
} else {
// Avoid the redundant blocking to output stream.
return;
}
// All streams are having their SourceMediaStream disconnected, so they
@ -292,19 +295,17 @@ void MediaDecoder::DestroyDecodedStream()
// be careful not to send any messages after the Destroy().
if (os.mStream->IsDestroyed()) {
// Probably the DOM MediaStream was GCed. Clean up.
if (os.mPort) {
os.mPort->Destroy();
}
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
mOutputStreams.RemoveElementAt(i);
continue;
}
os.mStream->ChangeExplicitBlockerCount(1);
// Explicitly remove all existing ports. This is not strictly necessary but it's
// good form.
if (os.mPort) {
os.mPort->Destroy();
os.mPort = nullptr;
}
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
os.mPort = nullptr;
}
mDecodedStream = nullptr;
@ -842,7 +843,7 @@ void MediaDecoder::PlaybackEnded()
if (mShuttingDown ||
mPlayState == PLAY_STATE_SEEKING ||
(mPlayState == PLAY_STATE_LOADING)) {
mPlayState == PLAY_STATE_LOADING) {
return;
}
@ -853,9 +854,8 @@ void MediaDecoder::PlaybackEnded()
OutputStreamData& os = mOutputStreams[i];
if (os.mStream->IsDestroyed()) {
// Probably the DOM MediaStream was GCed. Clean up.
if (os.mPort) {
os.mPort->Destroy();
}
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
mOutputStreams.RemoveElementAt(i);
continue;
}
@ -863,9 +863,8 @@ void MediaDecoder::PlaybackEnded()
// Shouldn't really be needed since mDecodedStream should already have
// finished, but doesn't hurt.
os.mStream->Finish();
if (os.mPort) {
os.mPort->Destroy();
}
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
// Not really needed but it keeps the invariant that a stream not
// connected to mDecodedStream is explicity blocked.
os.mStream->ChangeExplicitBlockerCount(1);

View File

@ -2958,7 +2958,8 @@ void MediaDecoderStateMachine::AdvanceFrame()
(JustExitedQuickBuffering() || HasLowUndecodedData());
} else {
MOZ_ASSERT(mReader->IsWaitForDataSupported());
shouldBuffer = OutOfDecodedAudio() || OutOfDecodedVideo();
shouldBuffer = (OutOfDecodedAudio() && mAudioRequestStatus == RequestStatus::Waiting) ||
(OutOfDecodedVideo() && mVideoRequestStatus == RequestStatus::Waiting);
}
if (shouldBuffer) {
if (currentFrame) {

View File

@ -207,7 +207,8 @@ ExtractH264CodecDetails(const nsAString& aCodec,
int16_t& aProfile,
int16_t& aLevel)
{
// H.264 codecs parameters have a type defined as avc1.PPCCLL, where
// H.264 codecs parameters have a type defined as avcN.PPCCLL, where
// N = avc type. avc3 is avcc with SPS & PPS implicit (within stream)
// PP = profile_idc, CC = constraint_set flags, LL = level_idc.
// We ignore the constraint_set flags, as it's not clear from any
// documentation what constraints the platform decoders support.
@ -217,9 +218,9 @@ ExtractH264CodecDetails(const nsAString& aCodec,
return false;
}
// Verify the codec starts with "avc1.".
// Verify the codec starts with "avc1." or "avc3.".
const nsAString& sample = Substring(aCodec, 0, 5);
if (!sample.EqualsASCII("avc1.")) {
if (!sample.EqualsASCII("avc1.") && !sample.EqualsASCII("avc3.")) {
return false;
}

View File

@ -0,0 +1,286 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "AVCCDecoderModule.h"
#include "ImageContainer.h"
#include "MediaTaskQueue.h"
#include "mp4_demuxer/DecoderData.h"
#include "mp4_demuxer/AnnexB.h"
namespace mozilla
{
class AVCCMediaDataDecoder : public MediaDataDecoder {
public:
AVCCMediaDataDecoder(PlatformDecoderModule* aPDM,
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback);
virtual ~AVCCMediaDataDecoder();
virtual nsresult Init() MOZ_OVERRIDE;
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
virtual nsresult Flush() MOZ_OVERRIDE;
virtual nsresult Drain() MOZ_OVERRIDE;
virtual nsresult Shutdown() MOZ_OVERRIDE;
virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
virtual bool IsDormantNeeded() MOZ_OVERRIDE;
virtual void AllocateMediaResources() MOZ_OVERRIDE;
virtual void ReleaseMediaResources() MOZ_OVERRIDE;
virtual void ReleaseDecoder() MOZ_OVERRIDE;
private:
// Will create the required MediaDataDecoder if we have a AVC SPS.
// Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
// will set mError accordingly.
nsresult CreateDecoder();
nsresult CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample);
nsRefPtr<PlatformDecoderModule> mPDM;
mp4_demuxer::VideoDecoderConfig mCurrentConfig;
layers::LayersBackend mLayersBackend;
nsRefPtr<layers::ImageContainer> mImageContainer;
nsRefPtr<MediaTaskQueue> mVideoTaskQueue;
MediaDataDecoderCallback* mCallback;
nsRefPtr<MediaDataDecoder> mDecoder;
nsresult mLastError;
};
AVCCMediaDataDecoder::AVCCMediaDataDecoder(PlatformDecoderModule* aPDM,
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback)
: mPDM(aPDM)
, mCurrentConfig(aConfig)
, mLayersBackend(aLayersBackend)
, mImageContainer(aImageContainer)
, mVideoTaskQueue(aVideoTaskQueue)
, mCallback(aCallback)
, mDecoder(nullptr)
, mLastError(NS_OK)
{
CreateDecoder();
}
AVCCMediaDataDecoder::~AVCCMediaDataDecoder()
{
}
nsresult
AVCCMediaDataDecoder::Init()
{
if (mDecoder) {
return mDecoder->Init();
}
return mLastError;
}
nsresult
AVCCMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
{
mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample);
if (!mDecoder) {
// It is not possible to create an AVCC H264 decoder without SPS.
// As such, creation will fail if the extra_data just extracted doesn't
// contain a SPS.
nsresult rv = CreateDecoderAndInit(aSample);
if (rv == NS_ERROR_NOT_INITIALIZED) {
// We are missing the required SPS to create the decoder.
// Ignore for the time being, the MP4Sample will be dropped.
return NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
}
aSample->extra_data = mCurrentConfig.extra_data;
return mDecoder->Input(aSample);
}
nsresult
AVCCMediaDataDecoder::Flush()
{
if (mDecoder) {
return mDecoder->Flush();
}
return mLastError;
}
nsresult
AVCCMediaDataDecoder::Drain()
{
if (mDecoder) {
return mDecoder->Drain();
}
return mLastError;
}
nsresult
AVCCMediaDataDecoder::Shutdown()
{
if (mDecoder) {
return mDecoder->Shutdown();
}
return NS_OK;
}
bool
AVCCMediaDataDecoder::IsWaitingMediaResources()
{
if (mDecoder) {
return mDecoder->IsWaitingMediaResources();
}
return MediaDataDecoder::IsWaitingMediaResources();
}
bool
AVCCMediaDataDecoder::IsDormantNeeded()
{
if (mDecoder) {
return mDecoder->IsDormantNeeded();
}
return MediaDataDecoder::IsDormantNeeded();
}
void
AVCCMediaDataDecoder::AllocateMediaResources()
{
if (mDecoder) {
mDecoder->AllocateMediaResources();
}
}
void
AVCCMediaDataDecoder::ReleaseMediaResources()
{
if (mDecoder) {
mDecoder->ReleaseMediaResources();
}
}
void
AVCCMediaDataDecoder::ReleaseDecoder()
{
if (mDecoder) {
mDecoder->ReleaseDecoder();
}
}
nsresult
AVCCMediaDataDecoder::CreateDecoder()
{
if (!mp4_demuxer::AnnexB::HasSPS(mCurrentConfig.extra_data)) {
// nothing found yet, will try again later
return NS_ERROR_NOT_INITIALIZED;
}
mDecoder = mPDM->CreateVideoDecoder(mCurrentConfig,
mLayersBackend,
mImageContainer,
mVideoTaskQueue,
mCallback);
if (!mDecoder) {
mLastError = NS_ERROR_FAILURE;
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
AVCCMediaDataDecoder::CreateDecoderAndInit(mp4_demuxer::MP4Sample* aSample)
{
nsRefPtr<mp4_demuxer::ByteBuffer> extra_data =
mp4_demuxer::AnnexB::ExtractExtraData(aSample);
if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
return NS_ERROR_NOT_INITIALIZED;
}
mCurrentConfig.extra_data = extra_data;
nsresult rv = CreateDecoder();
NS_ENSURE_SUCCESS(rv, rv);
return Init();
}
// AVCCDecoderModule
AVCCDecoderModule::AVCCDecoderModule(PlatformDecoderModule* aPDM)
: mPDM(aPDM)
{
MOZ_ASSERT(aPDM);
}
AVCCDecoderModule::~AVCCDecoderModule()
{
}
nsresult
AVCCDecoderModule::Startup()
{
return mPDM->Startup();
}
nsresult
AVCCDecoderModule::Shutdown()
{
return mPDM->Shutdown();
}
already_AddRefed<MediaDataDecoder>
AVCCDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback)
{
nsRefPtr<MediaDataDecoder> decoder;
if (strcmp(aConfig.mime_type, "video/avc") ||
!mPDM->DecoderNeedsAVCC(aConfig)) {
// There is no need for an AVCC wrapper for non-AVC content.
decoder = mPDM->CreateVideoDecoder(aConfig,
aLayersBackend,
aImageContainer,
aVideoTaskQueue,
aCallback);
} else {
decoder = new AVCCMediaDataDecoder(mPDM,
aConfig,
aLayersBackend,
aImageContainer,
aVideoTaskQueue,
aCallback);
}
return decoder.forget();
}
already_AddRefed<MediaDataDecoder>
AVCCDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
{
return mPDM->CreateAudioDecoder(aConfig,
aAudioTaskQueue,
aCallback);
}
bool
AVCCDecoderModule::SupportsAudioMimeType(const char* aMimeType)
{
return mPDM->SupportsAudioMimeType(aMimeType);
}
bool
AVCCDecoderModule::SupportsVideoMimeType(const char* aMimeType)
{
return mPDM->SupportsVideoMimeType(aMimeType);
}
} // namespace mozilla

View File

@ -0,0 +1,54 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#ifndef mozilla_AVCCDecoderModule_h
#define mozilla_AVCCDecoderModule_h
#include "PlatformDecoderModule.h"
namespace mozilla {
class AVCCMediaDataDecoder;
// AVCCDecoderModule is a PlatformDecoderModule wrapper used to ensure that
// only AVCC format is fed to the underlying PlatformDecoderModule.
// The AVCCDecoderModule allows playback of content where the SPS NAL may not be
// provided in the init segment (e.g. AVC3 or Annex B)
// AVCCDecoderModule will monitor the input data, and will delay creation of the
// MediaDataDecoder until a SPS and PPS NALs have been extracted.
//
// AVCC-only decoder modules are AppleVideoDecoder and EMEH264Decoder.
class AVCCDecoderModule : public PlatformDecoderModule {
public:
explicit AVCCDecoderModule(PlatformDecoderModule* aPDM);
virtual ~AVCCDecoderModule();
virtual nsresult Startup() MOZ_OVERRIDE;
virtual nsresult Shutdown() MOZ_OVERRIDE;
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
virtual already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
virtual bool SupportsAudioMimeType(const char* aMimeType) MOZ_OVERRIDE;
virtual bool SupportsVideoMimeType(const char* aMimeType) MOZ_OVERRIDE;
private:
nsRefPtr<PlatformDecoderModule> mPDM;
};
} // namespace mozilla
#endif // mozilla_AVCCDecoderModule_h

View File

@ -250,9 +250,10 @@ public:
};
PlatformDecoderModule* CreateBlankDecoderModule()
already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule()
{
return new BlankDecoderModule();
nsRefPtr<PlatformDecoderModule> pdm = new BlankDecoderModule();
return pdm.forget();
}
} // namespace mozilla

View File

@ -672,9 +672,15 @@ MP4Reader::ResetDecode()
{
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
Flush(kVideo);
mDemuxer->SeekVideo(0);
{
MonitorAutoLock mon(mIndexMonitor);
mDemuxer->SeekVideo(0);
}
Flush(kAudio);
mDemuxer->SeekAudio(0);
{
MonitorAutoLock mon(mIndexMonitor);
mDemuxer->SeekAudio(0);
}
return MediaDecoderReader::ResetDecode();
}

View File

@ -123,7 +123,7 @@ private:
size_t SizeOfQueue(TrackType aTrack);
nsAutoPtr<mp4_demuxer::MP4Demuxer> mDemuxer;
nsAutoPtr<PlatformDecoderModule> mPlatform;
nsRefPtr<PlatformDecoderModule> mPlatform;
class DecoderCallback : public MediaDataDecoderCallback {
public:

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PlatformDecoderModule.h"
#include "AVCCDecoderModule.h"
#ifdef XP_WIN
#include "WMFDecoderModule.h"
#endif
@ -31,7 +33,7 @@
namespace mozilla {
extern PlatformDecoderModule* CreateBlankDecoderModule();
extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
bool PlatformDecoderModule::sUseBlankDecoder = false;
bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
@ -76,7 +78,7 @@ PlatformDecoderModule::Init()
#ifdef MOZ_EME
/* static */
PlatformDecoderModule*
already_AddRefed<PlatformDecoderModule>
PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
bool aHasAudio,
bool aHasVideo,
@ -90,7 +92,7 @@ PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
cdmDecodesVideo = caps.CanDecryptAndDecodeVideo();
}
nsAutoPtr<PlatformDecoderModule> pdm;
nsRefPtr<PlatformDecoderModule> pdm;
if ((!cdmDecodesAudio && aHasAudio) || (!cdmDecodesVideo && aHasVideo)) {
// The CDM itself can't decode. We need to wrap a PDM to decode the
// decrypted output of the CDM.
@ -100,56 +102,69 @@ PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
}
}
return new EMEDecoderModule(aProxy,
pdm.forget(),
cdmDecodesAudio,
cdmDecodesVideo);
nsRefPtr<PlatformDecoderModule> emepdm(
new AVCCDecoderModule(new EMEDecoderModule(aProxy,
pdm,
cdmDecodesAudio,
cdmDecodesVideo)));
return emepdm.forget();
}
#endif
/* static */
PlatformDecoderModule*
already_AddRefed<PlatformDecoderModule>
PlatformDecoderModule::Create()
{
// Note: This runs on the decode thread.
MOZ_ASSERT(!NS_IsMainThread());
nsRefPtr<PlatformDecoderModule> m(CreatePDM());
if (m && NS_SUCCEEDED(m->Startup())) {
return m.forget();
}
return nullptr;
}
/* static */
already_AddRefed<PlatformDecoderModule>
PlatformDecoderModule::CreatePDM()
{
#ifdef MOZ_WIDGET_ANDROID
if(sAndroidMCDecoderPreferred && sAndroidMCDecoderEnabled){
return new AndroidDecoderModule();
nsRefPtr<PlatformDecoderModule> m(new AndroidDecoderModule());
return m.forget();
}
#endif
if (sUseBlankDecoder) {
return CreateBlankDecoderModule();
}
#ifdef XP_WIN
nsAutoPtr<WMFDecoderModule> m(new WMFDecoderModule());
if (NS_SUCCEEDED(m->Startup())) {
return m.forget();
}
nsRefPtr<PlatformDecoderModule> m(new WMFDecoderModule());
return m.forget();
#endif
#ifdef MOZ_FFMPEG
if (sFFmpegDecoderEnabled) {
nsAutoPtr<PlatformDecoderModule> m(FFmpegRuntimeLinker::CreateDecoderModule());
nsRefPtr<PlatformDecoderModule> m(FFmpegRuntimeLinker::CreateDecoderModule());
if (m) {
return m.forget();
}
}
#endif
#ifdef MOZ_APPLEMEDIA
nsAutoPtr<AppleDecoderModule> m(new AppleDecoderModule());
if (NS_SUCCEEDED(m->Startup())) {
return m.forget();
}
nsRefPtr<PlatformDecoderModule> m(new AVCCDecoderModule(new AppleDecoderModule()));
return m.forget();
#endif
#ifdef MOZ_GONK_MEDIACODEC
if (sGonkDecoderEnabled) {
return new GonkDecoderModule();
nsRefPtr<PlatformDecoderModule> m(new GonkDecoderModule());
return m.forget();
}
#endif
#ifdef MOZ_WIDGET_ANDROID
if(sAndroidMCDecoderEnabled){
return new AndroidDecoderModule();
nsRefPtr<PlatformDecoderModule> m(new AndroidDecoderModule());
return m.forget();
}
#endif
return nullptr;
@ -167,4 +182,10 @@ PlatformDecoderModule::SupportsVideoMimeType(const char* aMimeType)
return !strcmp(aMimeType, "video/mp4") || !strcmp(aMimeType, "video/avc");
}
bool
PlatformDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
{
return false;
}
} // namespace mozilla

View File

@ -54,6 +54,8 @@ typedef int64_t Microseconds;
// "media.fragmented-mp4.use-blank-decoder" is true.
class PlatformDecoderModule {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformDecoderModule)
// Call on the main thread to initialize the static state
// needed by Create().
static void Init();
@ -64,7 +66,13 @@ public:
// PlatformDecoderModules alive at the same time. There is one
// PlatformDecoderModule created per MP4Reader.
// This is called on the decode task queue.
static PlatformDecoderModule* Create();
static already_AddRefed<PlatformDecoderModule> Create();
// As Create() but do not initialize the created PlatformDecoderModule.
static already_AddRefed<PlatformDecoderModule> CreatePDM();
// Perform any per-instance initialization.
// This is called on the decode task queue.
virtual nsresult Startup() { return NS_OK; };
#ifdef MOZ_EME
// Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
@ -72,10 +80,11 @@ public:
// does not decode, we create a PDM and use that to create MediaDataDecoders
// that we use on on aTaskQueue to decode the decrypted stream.
// This is called on the decode task queue.
static PlatformDecoderModule* CreateCDMWrapper(CDMProxy* aProxy,
bool aHasAudio,
bool aHasVideo,
MediaTaskQueue* aTaskQueue);
static already_AddRefed<PlatformDecoderModule>
CreateCDMWrapper(CDMProxy* aProxy,
bool aHasAudio,
bool aHasVideo,
MediaTaskQueue* aTaskQueue);
#endif
// Called to shutdown the decoder module and cleanup state. The PDM
@ -124,10 +133,12 @@ public:
virtual bool SupportsAudioMimeType(const char* aMimeType);
virtual bool SupportsVideoMimeType(const char* aMimeType);
virtual ~PlatformDecoderModule() {}
// Indicates if the video decoder requires AVCC format.
virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig);
protected:
PlatformDecoderModule() {}
virtual ~PlatformDecoderModule() {}
// Caches pref media.fragmented-mp4.use-blank-decoder
static bool sUseBlankDecoder;
static bool sFFmpegDecoderEnabled;

View File

@ -72,7 +72,7 @@ SharedDecoderManager::CreateVideoDecoder(
MediaTaskQueue* aVideoTaskQueue, MediaDataDecoderCallback* aCallback)
{
if (!mDecoder) {
nsAutoPtr<PlatformDecoderModule> platform(PlatformDecoderModule::Create());
nsRefPtr<PlatformDecoderModule> platform(PlatformDecoderModule::Create());
mDecoder = platform->CreateVideoDecoder(
aConfig, aLayersBackend, aImageContainer, aVideoTaskQueue, mCallback);
if (!mDecoder) {

View File

@ -66,7 +66,7 @@ public:
}
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
mp4_demuxer::AnnexB::ConvertSample(aSample);
mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample);
return MediaCodecDataDecoder::Input(aSample);
}
@ -262,8 +262,8 @@ AndroidDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig&
if (!format->GetByteBuffer(NS_LITERAL_CSTRING("csd-0"))) {
uint8_t* csd0 = new uint8_t[2];
csd0[0] = aConfig.audio_specific_config[0];
csd0[1] = aConfig.audio_specific_config[1];
csd0[0] = (*aConfig.audio_specific_config)[0];
csd0[1] = (*aConfig.audio_specific_config)[1];
jobject buffer = env->NewDirectByteBuffer(csd0, 2);
format->SetByteBuffer(NS_LITERAL_CSTRING("csd-0"), buffer);

View File

@ -293,7 +293,7 @@ AppleATDecoder::DecodeSample(mp4_demuxer::MP4Sample* aSample)
nsresult
AppleATDecoder::GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
const mozilla::Vector<uint8_t>& aExtraData)
const nsTArray<uint8_t>& aExtraData)
{
// Request the properties from CoreAudio using the codec magic cookie
AudioFormatInfo formatInfo;
@ -302,8 +302,8 @@ AppleATDecoder::GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
if (mFormatID == kAudioFormatMPEG4AAC) {
formatInfo.mASBD.mFormatFlags = mConfig.extended_profile;
}
formatInfo.mMagicCookieSize = aExtraData.length();
formatInfo.mMagicCookie = aExtraData.begin();
formatInfo.mMagicCookieSize = aExtraData.Length();
formatInfo.mMagicCookie = aExtraData.Elements();
UInt32 formatListSize;
// Attempt to retrieve the default format using
@ -374,7 +374,7 @@ AppleATDecoder::SetupDecoder(mp4_demuxer::MP4Sample* aSample)
// This will provide us with an updated magic cookie for use with
// GetInputAudioDescription.
if (NS_SUCCEEDED(GetImplicitAACMagicCookie(aSample)) &&
!mMagicCookie.length()) {
!mMagicCookie.Length()) {
// nothing found yet, will try again later
return NS_ERROR_NOT_INITIALIZED;
}
@ -387,8 +387,8 @@ AppleATDecoder::SetupDecoder(mp4_demuxer::MP4Sample* aSample)
PodZero(&inputFormat);
nsresult rv =
GetInputAudioDescription(inputFormat,
mMagicCookie.length() ?
mMagicCookie : mConfig.extra_data);
mMagicCookie.Length() ?
mMagicCookie : *mConfig.extra_data);
if (NS_FAILED(rv)) {
return rv;
}
@ -449,7 +449,7 @@ _MetadataCallback(void* aAppleATDecoder,
decoder->mFileStreamError = true;
return;
}
decoder->mMagicCookie.append(data.get(), size);
decoder->mMagicCookie.AppendElements(data.get(), size);
}
}
@ -495,7 +495,7 @@ AppleATDecoder::GetImplicitAACMagicCookie(const mp4_demuxer::MP4Sample* aSample)
NS_WARNING("Couldn't parse sample");
}
if (status || mFileStreamError || mMagicCookie.length()) {
if (status || mFileStreamError || mMagicCookie.Length()) {
// We have decoded a magic cookie or an error occurred as such
// we won't need the stream any longer.
AudioFileStreamClose(mStream);

View File

@ -9,7 +9,6 @@
#include <AudioToolbox/AudioToolbox.h>
#include "PlatformDecoderModule.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Vector.h"
#include "nsIThread.h"
@ -24,7 +23,7 @@ public:
AppleATDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback);
~AppleATDecoder();
virtual ~AppleATDecoder();
virtual nsresult Init() MOZ_OVERRIDE;
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
@ -36,13 +35,13 @@ public:
const mp4_demuxer::AudioDecoderConfig& mConfig;
// Use to extract magic cookie for HE-AAC detection.
mozilla::Vector<uint8_t> mMagicCookie;
nsTArray<uint8_t> mMagicCookie;
// Will be set to true should an error occurred while attempting to retrieve
// the magic cookie property.
bool mFileStreamError;
private:
RefPtr<MediaTaskQueue> mTaskQueue;
nsRefPtr<MediaTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
AudioConverterRef mConverter;
AudioStreamBasicDescription mOutputFormat;
@ -53,7 +52,7 @@ private:
void SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample);
nsresult DecodeSample(mp4_demuxer::MP4Sample* aSample);
nsresult GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
const mozilla::Vector<uint8_t>& aExtraData);
const nsTArray<uint8_t>& aExtraData);
// Setup AudioConverter once all information required has been gathered.
// Will return NS_ERROR_NOT_INITIALIZED if more data is required.
nsresult SetupDecoder(mp4_demuxer::MP4Sample* aSample);

View File

@ -198,4 +198,10 @@ AppleDecoderModule::SupportsAudioMimeType(const char* aMimeType)
return !strcmp(aMimeType, "audio/mp4a-latm") || !strcmp(aMimeType, "audio/mpeg");
}
bool
AppleDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
{
return true;
}
} // namespace mozilla

View File

@ -16,9 +16,7 @@ public:
AppleDecoderModule();
virtual ~AppleDecoderModule();
// Perform any per-instance initialization.
// Main thread only.
nsresult Startup();
virtual nsresult Startup() MOZ_OVERRIDE;
// Called when the decoders have shutdown. Main thread only.
// Does this really need to be main thread only????
@ -39,6 +37,8 @@ public:
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
virtual bool SupportsAudioMimeType(const char* aMimeType) MOZ_OVERRIDE;
virtual bool
DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) MOZ_OVERRIDE;
static void Init();
static nsresult CanDecode();

View File

@ -404,8 +404,8 @@ AppleVDADecoder::InitializeSession()
CFDictionaryRef
AppleVDADecoder::CreateDecoderSpecification()
{
const uint8_t* extradata = mConfig.extra_data.begin();
int extrasize = mConfig.extra_data.length();
const uint8_t* extradata = mConfig.extra_data->Elements();
int extrasize = mConfig.extra_data->Length();
OSType format = 'avc1';
AutoCFRelease<CFNumberRef> avc_width =

View File

@ -8,7 +8,6 @@
#define mozilla_AppleVDADecoder_h
#include "PlatformDecoderModule.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ReentrantMonitor.h"
#include "MP4Reader.h"
#include "MP4Decoder.h"
@ -70,7 +69,7 @@ public:
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
~AppleVDADecoder();
virtual ~AppleVDADecoder();
virtual nsresult Init() MOZ_OVERRIDE;
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
virtual nsresult Flush() MOZ_OVERRIDE;
@ -87,9 +86,9 @@ public:
CFDictionaryRef CreateOutputConfiguration();
const mp4_demuxer::VideoDecoderConfig& mConfig;
RefPtr<MediaTaskQueue> mTaskQueue;
nsRefPtr<MediaTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
layers::ImageContainer* mImageContainer;
nsRefPtr<layers::ImageContainer> mImageContainer;
ReorderQueue mReorderQueue;
private:

View File

@ -260,7 +260,7 @@ AppleVTDecoder::InitializeSession()
#ifdef LOG_MEDIA_SHA1
SHA1Sum avc_hash;
avc_hash.update(mConfig.extra_data.begin(), mConfig.extra_data.length());
avc_hash.update(mConfig.extra_data->Elements(), mConfig.extra_data->Length());
uint8_t digest_buf[SHA1Sum::kHashSize];
avc_hash.finish(digest_buf);
nsAutoCString avc_digest;
@ -268,7 +268,7 @@ AppleVTDecoder::InitializeSession()
avc_digest.AppendPrintf("%02x", digest_buf[i]);
}
LOG("AVCDecoderConfig %ld bytes sha1 %s",
mConfig.extra_data.length(), avc_digest.get());
mConfig.extra_data->Length(), avc_digest.get());
#endif // LOG_MEDIA_SHA1
AutoCFRelease<CFDictionaryRef> extensions = CreateDecoderExtensions();
@ -312,8 +312,8 @@ AppleVTDecoder::CreateDecoderExtensions()
{
AutoCFRelease<CFDataRef> avc_data =
CFDataCreate(kCFAllocatorDefault,
mConfig.extra_data.begin(),
mConfig.extra_data.length());
mConfig.extra_data->Elements(),
mConfig.extra_data->Length());
const void* atomsKey[] = { CFSTR("avcC") };
const void* atomsValue[] = { avc_data };

View File

@ -19,7 +19,7 @@ public:
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
~AppleVTDecoder();
virtual ~AppleVTDecoder();
virtual nsresult Init() MOZ_OVERRIDE;
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
virtual nsresult Flush() MOZ_OVERRIDE;

View File

@ -278,9 +278,8 @@ EMEAudioDecoder::GmpInit()
mAudioChannels = mConfig.channel_count;
nsTArray<uint8_t> extraData;
extraData.AppendElements(&mConfig.audio_specific_config[0],
mConfig.audio_specific_config.length());
extraData.AppendElements(mConfig.audio_specific_config->Elements(),
mConfig.audio_specific_config->Length());
mGMP->InitDecode(kGMPAudioCodecAAC,
mAudioChannels,
mConfig.bits_per_sample,

View File

@ -258,4 +258,10 @@ EMEDecoderModule::CreateAudioDecoder(const AudioDecoderConfig& aConfig,
return emeDecoder.forget();
}
bool
EMEDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
{
return mCDMDecodesVideo && aConfig.crypto.valid;
}
} // namespace mozilla

View File

@ -45,10 +45,13 @@ public:
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
virtual bool
DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) MOZ_OVERRIDE;
private:
nsRefPtr<CDMProxy> mProxy;
// Will be null if CDM has decoding capability.
nsAutoPtr<PlatformDecoderModule> mPDM;
nsRefPtr<PlatformDecoderModule> mPDM;
// We run the PDM on its own task queue.
nsRefPtr<MediaTaskQueue> mTaskQueue;
bool mCDMDecodesAudio;

View File

@ -277,8 +277,8 @@ EMEH264Decoder::GmpInit()
nsTArray<uint8_t> codecSpecific;
codecSpecific.AppendElement(0); // mPacketizationMode.
codecSpecific.AppendElements(mConfig.extra_data.begin(),
mConfig.extra_data.length());
codecSpecific.AppendElements(mConfig.extra_data->Elements(),
mConfig.extra_data->Length());
rv = mGMP->InitDecode(codec,
codecSpecific,

View File

@ -24,8 +24,7 @@ FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
, mCallback(aCallback)
{
MOZ_COUNT_CTOR(FFmpegAudioDecoder);
mExtraData.append(aConfig.audio_specific_config.begin(),
aConfig.audio_specific_config.length());
mExtraData = aConfig.audio_specific_config;
}
nsresult

View File

@ -8,7 +8,6 @@
#include <unistd.h>
#include "MediaTaskQueue.h"
#include "mp4_demuxer/mp4_demuxer.h"
#include "FFmpegLibs.h"
#include "FFmpegLog.h"
#include "FFmpegDataDecoder.h"
@ -25,6 +24,7 @@ FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(MediaTaskQueue* aTaskQueue,
: mTaskQueue(aTaskQueue)
, mCodecContext(nullptr)
, mFrame(NULL)
, mExtraData(nullptr)
, mCodecID(aCodecID)
{
MOZ_COUNT_CTOR(FFmpegDataDecoder);
@ -94,11 +94,15 @@ FFmpegDataDecoder<LIBAV_VER>::Init()
mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
mCodecContext->thread_safe_callbacks = false;
mCodecContext->extradata_size = mExtraData.length();
for (int i = 0; i < FF_INPUT_BUFFER_PADDING_SIZE; i++) {
mExtraData.append(0);
if (mExtraData) {
mCodecContext->extradata_size = mExtraData->Length();
for (int i = 0; i < FF_INPUT_BUFFER_PADDING_SIZE; i++) {
mExtraData->AppendElement(0);
}
mCodecContext->extradata = mExtraData->Elements();
} else {
mCodecContext->extradata_size = 0;
}
mCodecContext->extradata = mExtraData.begin();
if (codec->capabilities & CODEC_CAP_DR1) {
mCodecContext->flags |= CODEC_FLAG_EMU_EDGE;

View File

@ -9,8 +9,8 @@
#include "PlatformDecoderModule.h"
#include "FFmpegLibs.h"
#include "mozilla/Vector.h"
#include "mozilla/StaticMutex.h"
#include "mp4_demuxer/mp4_demuxer.h"
namespace mozilla
{
@ -41,7 +41,7 @@ protected:
MediaTaskQueue* mTaskQueue;
AVCodecContext* mCodecContext;
AVFrame* mFrame;
Vector<uint8_t> mExtraData;
nsRefPtr<mp4_demuxer::ByteBuffer> mExtraData;
private:
static bool sFFmpegInitDone;

View File

@ -18,7 +18,12 @@ template <int V>
class FFmpegDecoderModule : public PlatformDecoderModule
{
public:
static PlatformDecoderModule* Create() { return new FFmpegDecoderModule(); }
static already_AddRefed<PlatformDecoderModule>
Create()
{
nsRefPtr<PlatformDecoderModule> pdm = new FFmpegDecoderModule();
return pdm.forget();
}
FFmpegDecoderModule() {}
virtual ~FFmpegDecoderModule() {}
@ -57,6 +62,7 @@ public:
{
return FFmpegH264Decoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
}
};
} // namespace mozilla

View File

@ -10,6 +10,7 @@
#include "ImageContainer.h"
#include "mp4_demuxer/mp4_demuxer.h"
#include "mp4_demuxer/AnnexB.h"
#include "FFmpegH264Decoder.h"
@ -32,7 +33,6 @@ FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
, mImageContainer(aImageContainer)
{
MOZ_COUNT_CTOR(FFmpegH264Decoder);
mExtraData.append(aConfig.extra_data.begin(), aConfig.extra_data.length());
}
nsresult
@ -53,6 +53,7 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(mp4_demuxer::MP4Sample* aSample)
AVPacket packet;
av_init_packet(&packet);
mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample);
aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE);
packet.data = aSample->data;
packet.size = aSample->size;

View File

@ -21,14 +21,14 @@ FFmpegRuntimeLinker::LinkStatus FFmpegRuntimeLinker::sLinkStatus =
struct AvFormatLib
{
const char* Name;
PlatformDecoderModule* (*Factory)();
already_AddRefed<PlatformDecoderModule> (*Factory)();
uint32_t Version;
};
template <int V> class FFmpegDecoderModule
{
public:
static PlatformDecoderModule* Create();
static already_AddRefed<PlatformDecoderModule> Create();
};
static const AvFormatLib sLibs[] = {
@ -101,14 +101,14 @@ FFmpegRuntimeLinker::Bind(const char* aLibName, uint32_t Version)
return true;
}
/* static */ PlatformDecoderModule*
/* static */ already_AddRefed<PlatformDecoderModule>
FFmpegRuntimeLinker::CreateDecoderModule()
{
if (!Link()) {
return nullptr;
}
PlatformDecoderModule* module = sLib->Factory();
return module;
nsRefPtr<PlatformDecoderModule> module = sLib->Factory();
return module.forget();
}
/* static */ void

View File

@ -7,12 +7,12 @@
#ifndef __FFmpegRuntimeLinker_h__
#define __FFmpegRuntimeLinker_h__
#include "PlatformDecoderModule.h"
#include <stdint.h>
namespace mozilla
{
class PlatformDecoderModule;
struct AvFormatLib;
class FFmpegRuntimeLinker
@ -20,7 +20,7 @@ class FFmpegRuntimeLinker
public:
static bool Link();
static void Unlink();
static PlatformDecoderModule* CreateDecoderModule();
static already_AddRefed<PlatformDecoderModule> CreateDecoderModule();
private:
static void* sLinkedLib;

View File

@ -46,8 +46,8 @@ GonkAudioDecoderManager::GonkAudioDecoderManager(
{
MOZ_COUNT_CTOR(GonkAudioDecoderManager);
MOZ_ASSERT(mAudioChannels);
mUserData.AppendElements(&aConfig.audio_specific_config[0],
aConfig.audio_specific_config.length());
mUserData.AppendElements(aConfig.audio_specific_config->Elements(),
aConfig.audio_specific_config->Length());
// Pass through mp3 without applying an ADTS header.
if (strcmp(aConfig.mime_type, "audio/mp4a-latm") != 0) {
mUseAdts = false;

View File

@ -445,7 +445,7 @@ GonkVideoDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
status_t rv;
if (aSample != nullptr) {
// We must prepare samples in AVC Annex B.
mp4_demuxer::AnnexB::ConvertSample(aSample);
mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample);
// Forward sample data to the decoder.
QueueFrameTimeIn(aSample->composition_timestamp, aSample->duration);

View File

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'AVCCDecoderModule.h',
'MP4Decoder.h',
'MP4Reader.h',
'MP4Stream.h',
@ -13,6 +14,7 @@ EXPORTS += [
]
UNIFIED_SOURCES += [
'AVCCDecoderModule.cpp',
'BlankDecoderModule.cpp',
'MP4Decoder.cpp',
'MP4Stream.cpp',

View File

@ -83,8 +83,8 @@ WMFAudioMFTManager::WMFAudioMFTManager(
} else if (!strcmp(aConfig.mime_type, "audio/mp4a-latm")) {
mStreamType = AAC;
AACAudioSpecificConfigToUserData(aConfig.aac_profile,
&aConfig.audio_specific_config[0],
aConfig.audio_specific_config.length(),
aConfig.audio_specific_config->Elements(),
aConfig.audio_specific_config->Length(),
mUserData);
} else {
mStreamType = Unknown;

View File

@ -17,7 +17,7 @@ public:
virtual ~WMFDecoderModule();
// Initializes the module, loads required dynamic libraries, etc.
nsresult Startup();
virtual nsresult Startup() MOZ_OVERRIDE;
// Called when the decoders have shutdown.
virtual nsresult Shutdown() MOZ_OVERRIDE;

View File

@ -215,7 +215,7 @@ WMFVideoMFTManager::Input(mp4_demuxer::MP4Sample* aSample)
{
if (mStreamType != VP8 && mStreamType != VP9) {
// We must prepare samples in AVC Annex B.
mp4_demuxer::AnnexB::ConvertSample(aSample);
mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample);
}
// Forward sample data to the decoder.
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);

View File

@ -8,6 +8,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/NullPtr.h"
class WriteRecordClient : public GMPRecordClient {
public:
@ -25,7 +26,7 @@ public:
virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
if (GMP_SUCCEEDED(aStatus)) {
mRecord->Write(&mData.front(), mData.size());
mRecord->Write(mData.size() ? &mData.front() : nullptr, mData.size());
} else {
GMPRunOnMainThread(mOnFailure);
mOnSuccess->Destroy();

View File

@ -72,43 +72,6 @@ GMPLoader* CreateGMPLoader(SandboxStarter* aStarter) {
return static_cast<GMPLoader*>(new GMPLoaderImpl(aStarter));
}
#if defined(XP_WIN) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
MOZ_NEVER_INLINE
static bool
GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
{
// "Top" of the free space on the stack is directly after the memory
// holding our return address.
uint8_t* top = (uint8_t*)_AddressOfReturnAddress();
// Look down the stack until we find the guard page...
MEMORY_BASIC_INFORMATION memInfo = {0};
uint8_t* bottom = top;
while (1) {
if (!VirtualQuery(bottom, &memInfo, sizeof(memInfo))) {
return false;
}
if ((memInfo.Protect & PAGE_GUARD) == PAGE_GUARD) {
bottom = (uint8_t*)memInfo.BaseAddress + memInfo.RegionSize;
#ifdef DEBUG
if (!VirtualQuery(bottom, &memInfo, sizeof(memInfo))) {
return false;
}
assert(!(memInfo.Protect & PAGE_GUARD)); // Should have found boundary.
#endif
break;
} else if (memInfo.State != MEM_COMMIT ||
(memInfo.AllocationProtect & PAGE_READWRITE) != PAGE_READWRITE) {
return false;
}
bottom = (uint8_t*)memInfo.BaseAddress - 1;
}
*aOutTop = top;
*aOutBottom = bottom;
return true;
}
#endif
bool
GMPLoaderImpl::Load(const char* aLibPath,
uint32_t aLibPathLen,
@ -146,17 +109,9 @@ GMPLoaderImpl::Load(const char* aLibPath,
if (!rlz_lib::BytesToString(digest, SHA256_LENGTH, &nodeId)) {
return false;
}
// We've successfully bound the origin salt to node id.
// rlz_lib::GetRawMachineId and/or the system functions it
// called could have left user identifiable data on the stack,
// so carefully zero the stack down to the guard page.
uint8_t* top;
uint8_t* bottom;
if (!GetStackAfterCurrentFrame(&top, &bottom)) {
return false;
}
assert(top >= bottom);
SecureZeroMemory(bottom, (top - bottom));
// TODO: (Bug 1114867) Clear any memory on the stack that may have been
// used by functions we've called that may have left behind data that
// can be used to uniquely identify the user.
} else
#endif
{

View File

@ -148,27 +148,30 @@ GMPParent::LoadProcess()
bool opened = Open(mProcess->GetChannel(), mProcess->GetChildProcessHandle());
if (!opened) {
LOGD(("%s::%s: Failed to create new child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
mProcess->Delete();
mProcess = nullptr;
return NS_ERROR_FAILURE;
}
LOGD(("%s::%s: Created new process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
LOGD(("%s::%s: Created new child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
bool ok = SendSetNodeId(mNodeId);
if (!ok) {
LOGD(("%s::%s: Failed to send node id to child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
mProcess->Delete();
mProcess = nullptr;
return NS_ERROR_FAILURE;
}
LOGD(("%s::%s: Failed to send node id %p", __CLASS__, __FUNCTION__, (void *)mProcess));
LOGD(("%s::%s: Sent node id to child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
ok = SendStartPlugin();
if (!ok) {
LOGD(("%s::%s: Failed to send start to child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
mProcess->Delete();
mProcess = nullptr;
return NS_ERROR_FAILURE;
}
LOGD(("%s::%s: Failed to send start %p", __CLASS__, __FUNCTION__, (void *)mProcess));
LOGD(("%s::%s: Sent StartPlugin to child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
}
mState = GMPStateLoaded;

View File

@ -9,6 +9,7 @@ support-files =
[test_MediaSource_disabled.html]
[test_BufferedSeek.html]
[test_BufferingWait.html]
skip-if = true # bug 1093133
[test_EndOfStream.html]
skip-if = (toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187
[test_FrameSelection.html]

View File

@ -171,10 +171,6 @@ var gPlayTests = [
// Test playback of a WebM file with non-zero start time.
{ name:"split.webm", type:"video/webm", duration:1.967 },
// Test playback of a WebM file with vp9 video
//{ name:"vp9.webm", type:"video/webm", duration:4 },
{ name:"vp9cake.webm", type:"video/webm", duration:7.966 },
// Test playback of a raw file
{ name:"seek.yuv", type:"video/x-raw-yuv", duration:1.833 },
@ -222,7 +218,11 @@ var gPlayTests = [
{ name:"vbr-head.mp3", type:"audio/mpeg", duration:10.00 },
// Invalid file
{ name:"bogus.duh", type:"bogus/duh", duration:Number.NaN }
{ name:"bogus.duh", type:"bogus/duh", duration:Number.NaN },
// Test playback of a WebM file with vp9 video
//{ name:"vp9.webm", type:"video/webm", duration:4 },
{ name:"vp9cake.webm", type:"video/webm", duration:7.966 }
];
// A file for each type we can support.

View File

@ -116,6 +116,20 @@ function startTest(test, token) {
document.body.appendChild(v);
v.play();
if (test.name == "vp9cake.webm") {
// Log events for debugging.
var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
"loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort",
"waiting", "pause"];
function logEvent(e) {
var v = e.target;
info(e.target.token + ": got " + e.type);
}
events.forEach(function(e) {
v.addEventListener(e, logEvent, false);
});
}
}
manager.runTests(gPlayTests, startTest);

View File

@ -56,7 +56,7 @@ public:
memmove(data, aData, size);
}
~VP8Sample()
virtual ~VP8Sample()
{
delete data;
}

View File

@ -59,7 +59,7 @@ private:
VP8Sample* PopSample();
nsRefPtr<WebMReader> mReader;
nsAutoPtr<PlatformDecoderModule> mPlatform;
nsRefPtr<PlatformDecoderModule> mPlatform;
nsRefPtr<MediaDataDecoder> mMediaDataDecoder;
// TaskQueue on which decoder can choose to decode.

View File

@ -5,6 +5,10 @@ support-files =
[test_bug900724.html]
[test_bug1017896.html]
[test_content_element.html]
[test_custom_element_adopt_callbacks.html]
[test_custom_element_callback_innerhtml.html]
[test_custom_element_clone_callbacks.html]
[test_custom_element_clone_callbacks_extended.html]
[test_nested_content_element.html]
[test_dest_insertion_points.html]
[test_dest_insertion_points_shadow.html]

View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
-->
<head>
<title>Test callbacks for adopted custom elements.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<template id="template"><x-foo></x-foo></template>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
<script>
var p = Object.create(HTMLElement.prototype);
p.createdCallback = function() {
ok(false, "Created callback should not be called for adopted node.");
};
document.registerElement("x-foo", { prototype: p });
var template = document.getElementById("template");
var adoptedFoo = document.adoptNode(template.content.firstChild);
is(adoptedFoo.nodeName, "X-FOO");
</script>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1102502
-->
<head>
<title>Test for attached callback for element created in the document by the parser</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1102502">Bug 1102502</a>
<div id="container"></div>
<script>
SimpleTest.waitForExplicitFinish();
var attachedCallbackCount = 0;
var p = Object.create(HTMLElement.prototype);
p.createdCallback = function() {
ok(true, "createdCallback called.");
};
p.attachedCallback = function() {
ok(true, "attachedCallback should be called when the parser creates an element in the document.");
attachedCallbackCount++;
// attachedCallback should be called twice, once for the element created for innerHTML and
// once for the element created in this document.
if (attachedCallbackCount == 2) {
SimpleTest.finish();
}
}
document.registerElement("x-foo", { prototype: p });
var container = document.getElementById("container");
container.innerHTML = '<x-foo></x-foo>';
</script>
<x-foo></x-foo>
</body>
</html>

View File

@ -0,0 +1,54 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
-->
<head>
<title>Test callbacks for cloned custom elements.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
<script>
SimpleTest.waitForExplicitFinish();
// Test to make sure created callback is called on clones that are upgraded and clones
// created after registering the custom element.
var callbackCalledOnUpgrade = false;
var callbackCalledOnClone = false;
var foo = document.createElement("x-foo");
var fooClone = foo.cloneNode(true);
var p = Object.create(HTMLElement.prototype);
p.createdCallback = function() {
is(this.__proto__, p, "Correct prototype should be set on custom elements.");
if (this == fooClone) {
// Callback called for the element created before registering the custom element.
// Should be called on element upgrade.
is(callbackCalledOnUpgrade, false, "Upgrade should only be called once per clone.");
callbackCalledOnUpgrade = true;
} else if (this != foo) {
// Callback called for the element created after registering the custom element.
is(callbackCalledOnClone, false, "Upgrade should only be called once per clone.");
callbackCalledOnClone = true;
}
if (callbackCalledOnUpgrade && callbackCalledOnClone) {
SimpleTest.finish();
}
};
document.registerElement("x-foo", { prototype: p });
var anotherFooClone = foo.cloneNode(true);
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
-->
<head>
<title>Test callbacks for cloned extended custom elements.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
<script>
SimpleTest.waitForExplicitFinish();
// Test to make sure created callback is called on clones that are upgraded and clones
// created after registering the custom element.
var callbackCalledOnUpgrade = false;
var callbackCalledOnClone = false;
var foo = document.createElement("button", "x-foo");
is(foo.getAttribute("is"), "x-foo");
var fooClone = foo.cloneNode(true);
var p = Object.create(HTMLButtonElement.prototype);
p.createdCallback = function() {
is(this.__proto__, p, "Correct prototype should be set on custom elements.");
if (this == fooClone) {
// Callback called for the element created before registering the custom element.
// Should be called on element upgrade.
is(callbackCalledOnUpgrade, false, "Upgrade should only be called once per clone.");
callbackCalledOnUpgrade = true;
} else if (this != foo) {
// Callback called for the element created after registering the custom element.
is(callbackCalledOnClone, false, "Upgrade should only be called once per clone.");
callbackCalledOnClone = true;
}
if (callbackCalledOnUpgrade && callbackCalledOnClone) {
SimpleTest.finish();
}
};
document.registerElement("x-foo", { prototype: p, extends: "button" });
var anotherFooClone = foo.cloneNode(true);
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View File

@ -14,6 +14,8 @@
<pre id="feedback"></pre>
<script class="testbody" type="text/javascript">
SpecialPowers.pushPrefEnv({"set": [["dom.workers.websocket.enabled", true]]},
function() {
var worker = new Worker("websocket_worker.js");
worker.onmessage = function(event) {
@ -38,7 +40,9 @@
};
worker.postMessage('foobar');
SimpleTest.waitForExplicitFinish();
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>

View File

@ -15,6 +15,8 @@
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
SpecialPowers.pushPrefEnv({"set": [["dom.workers.websocket.enabled", true]]},
function() {
var worker = new Worker("websocket_basic_worker.js");
worker.onmessage = function(event) {
@ -49,7 +51,9 @@
}
runTest();
SimpleTest.waitForExplicitFinish();
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>

View File

@ -15,6 +15,8 @@
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
SpecialPowers.pushPrefEnv({"set": [["dom.workers.websocket.enabled", true]]},
function() {
var worker = new Worker("websocket_loadgroup_worker.js");
var stopped = false;
@ -53,7 +55,9 @@
}
runTest();
SimpleTest.waitForExplicitFinish();
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>

View File

@ -19,6 +19,8 @@ onmessage = function() {
<script class="testbody" type="text/javascript">
SpecialPowers.pushPrefEnv({"set": [["dom.workers.websocket.enabled", true]]},
function() {
var blob = new Blob([document.getElementById("js_script").textContent],
{type: "text/javascript"});
var url = URL.createObjectURL(blob);
@ -63,8 +65,10 @@ onmessage = function() {
t();
}
SimpleTest.waitForExplicitFinish();
runTest();
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>

View File

@ -152,7 +152,7 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"URLSearchParams",
// IMPORTANT: Do not change this list without review from a DOM peer!
"WebSocket",
{ name: "WebSocket", pref: "dom.workers.websocket.enabled" },
// IMPORTANT: Do not change this list without review from a DOM peer!
"Worker",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -111,7 +111,7 @@ ClientCanvasLayer::Initialize(const Data& aData)
case mozilla::layers::LayersBackend::LAYERS_D3D10:
case mozilla::layers::LayersBackend::LAYERS_D3D11: {
#ifdef XP_WIN
if (mGLContext->IsANGLE() && DoesD3D11DeviceWork(gfxWindowsPlatform::GetPlatform()->GetD3D11Device())) {
if (mGLContext->IsANGLE() && DoesD3D11TextureSharingWork(gfxWindowsPlatform::GetPlatform()->GetD3D11Device())) {
factory = SurfaceFactory_ANGLEShareHandle::Create(mGLContext, caps);
}
#endif

View File

@ -595,7 +595,6 @@ CopyFrontToBack(TextureClient* aFront,
void
TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
bool aCanRerasterizeValidRegion,
nsIntRegion& aAddPaintedRegion)
{
if (mBackBuffer && mFrontBuffer) {
@ -614,9 +613,7 @@ TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
aAddPaintedRegion = regionToCopy;
if (regionToCopy.IsEmpty() ||
(aCanRerasterizeValidRegion &&
regionToCopy.Area() < tileSize.width * tileSize.height * MINIMUM_TILE_COPY_AREA)) {
if (regionToCopy.IsEmpty()) {
// Just redraw it all.
return;
}
@ -713,7 +710,6 @@ TileClient::GetBackBuffer(const nsIntRegion& aDirtyRegion,
SurfaceMode aMode,
bool *aCreatedTextureClient,
nsIntRegion& aAddPaintedRegion,
bool aCanRerasterizeValidRegion,
RefPtr<TextureClient>* aBackBufferOnWhite)
{
// Try to re-use the front-buffer if possible
@ -778,7 +774,7 @@ TileClient::GetBackBuffer(const nsIntRegion& aDirtyRegion,
mInvalidBack = nsIntRect(0, 0, mBackBuffer->GetSize().width, mBackBuffer->GetSize().height);
}
ValidateBackBufferFromFront(aDirtyRegion, aCanRerasterizeValidRegion, aAddPaintedRegion);
ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion);
*aBackBufferOnWhite = mBackBufferOnWhite;
return mBackBuffer;
@ -1116,7 +1112,6 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
aTile.GetBackBuffer(offsetScaledDirtyRegion,
content, mode,
&createdTextureClient, extraPainted,
usingTiledDrawTarget,
&backBufferOnWhite);
extraPainted.MoveBy(aTileOrigin);
@ -1150,8 +1145,6 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
}
if (usingTiledDrawTarget) {
aTile.Flip();
if (createdTextureClient) {
if (!mCompositableClient->AddTextureClient(backBuffer)) {
NS_WARNING("Failed to add tile TextureClient.");
@ -1180,6 +1173,7 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x * mResolution, aTileOrigin.y * mResolution);
if (!dt || (backBufferOnWhite && !dtOnWhite)) {
aTile.DiscardFrontBuffer();
aTile.DiscardBackBuffer();
return aTile;
}
@ -1195,13 +1189,10 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
dirtyRect->height);
drawRect.Scale(mResolution);
gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution),
NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution),
drawRect.width,
drawRect.height);
gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y));
// Mark the newly updated area as invalid in the back buffer
aTile.mInvalidBack.Or(aTile.mInvalidBack, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
// Mark the newly updated area as invalid in the front buffer
aTile.mInvalidFront.Or(aTile.mInvalidFront,
nsIntRect(NS_roundf(drawRect.x), NS_roundf(drawRect.y),
drawRect.width, drawRect.height));
if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
@ -1212,8 +1203,10 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
}
// The new buffer is now validated, remove the dirty region from it.
aTile.mInvalidFront.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height),
offsetScaledDirtyRegion);
aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height),
offsetScaledDirtyRegion);
aTile.Flip();
return aTile;
}

Some files were not shown because too many files have changed in this diff Show More