зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound. a=merge
This commit is contained in:
Коммит
889cfefc83
|
@ -67,3 +67,6 @@ GPATH
|
|||
|
||||
# Git clone directory for updating web-platform-tests
|
||||
testing/web-platform/sync/
|
||||
|
||||
# Android Gradle artifacts.
|
||||
mobile/android/gradle/.gradle
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,9 +1106,27 @@ 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) => {
|
||||
LoopRooms.on("joined", this.maybeResumeTourOnRoomJoined.bind(this));
|
||||
|
||||
// If expiresTime is not in the future and the user hasn't
|
||||
// previously authenticated then skip registration.
|
||||
if (!MozLoopServiceInternal.urlExpiryTimeIsInFuture() &&
|
||||
!LoopRooms.getGuestCreatedRoom() &&
|
||||
!MozLoopServiceInternal.fxAOAuthTokenData) {
|
||||
return Promise.resolve("registration not needed");
|
||||
}
|
||||
|
||||
let deferredInitialization = Promise.defer();
|
||||
gInitializeTimerFunc(deferredInitialization);
|
||||
|
||||
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;
|
||||
|
||||
|
@ -1127,22 +1152,18 @@ this.MozLoopService = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.resumeTour("open");
|
||||
// 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 expiresTime is not in the future and the user hasn't
|
||||
// previously authenticated then skip registration.
|
||||
if (!MozLoopServiceInternal.urlExpiryTimeIsInFuture() &&
|
||||
!LoopRooms.getGuestCreatedRoom() &&
|
||||
!MozLoopServiceInternal.fxAOAuthTokenData) {
|
||||
return Promise.resolve("registration not needed");
|
||||
if (!chatboxesForRoom.length) {
|
||||
log.warn("Tried to resume the tour from a join when the chatbox was closed", room);
|
||||
return;
|
||||
}
|
||||
|
||||
let deferredInitialization = Promise.defer();
|
||||
gInitializeTimerFunc(deferredInitialization);
|
||||
|
||||
return deferredInitialization.promise;
|
||||
}),
|
||||
this.resumeTour("open");
|
||||
},
|
||||
|
||||
/**
|
||||
* The core of the initialization work that happens once the browser is ready
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 === */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,58 +496,79 @@ 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")) {
|
||||
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.
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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(¬Valid);
|
||||
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}}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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
|
||||
|
|
13
configure.in
13
configure.in
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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({
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
nsDocument::SetupCustomElement(Element* aElement,
|
||||
uint32_t aNamespaceID,
|
||||
ErrorResult& rv)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
// 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,
|
||||
mozilla::ErrorResult& rv);
|
||||
const nsAString* aTypeExtension);
|
||||
|
||||
static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
|
||||
|
||||
|
|
|
@ -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,
|
||||
virtual void SetupCustomElement(Element* aElement,
|
||||
uint32_t aNamespaceID,
|
||||
mozilla::ErrorResult& rv) = 0;
|
||||
const nsAString* aTypeExtension = nullptr) = 0;
|
||||
virtual void
|
||||
RegisterElement(JSContext* aCx, const nsAString& aName,
|
||||
const mozilla::dom::ElementRegistrationOptions& aOptions,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,20 +295,18 @@ 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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -250,9 +250,10 @@ public:
|
|||
|
||||
};
|
||||
|
||||
PlatformDecoderModule* CreateBlankDecoderModule()
|
||||
already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule()
|
||||
{
|
||||
return new BlankDecoderModule();
|
||||
nsRefPtr<PlatformDecoderModule> pdm = new BlankDecoderModule();
|
||||
return pdm.forget();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -672,9 +672,15 @@ MP4Reader::ResetDecode()
|
|||
{
|
||||
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
|
||||
Flush(kVideo);
|
||||
{
|
||||
MonitorAutoLock mon(mIndexMonitor);
|
||||
mDemuxer->SeekVideo(0);
|
||||
}
|
||||
Flush(kAudio);
|
||||
{
|
||||
MonitorAutoLock mon(mIndexMonitor);
|
||||
mDemuxer->SeekAudio(0);
|
||||
}
|
||||
return MediaDecoderReader::ResetDecode();
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(),
|
||||
nsRefPtr<PlatformDecoderModule> emepdm(
|
||||
new AVCCDecoderModule(new EMEDecoderModule(aProxy,
|
||||
pdm,
|
||||
cdmDecodesAudio,
|
||||
cdmDecodesVideo);
|
||||
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())) {
|
||||
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())) {
|
||||
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
|
||||
|
|
|
@ -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,7 +80,8 @@ 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,
|
||||
static already_AddRefed<PlatformDecoderModule>
|
||||
CreateCDMWrapper(CDMProxy* aProxy,
|
||||
bool aHasAudio,
|
||||
bool aHasVideo,
|
||||
MediaTaskQueue* aTaskQueue);
|
||||
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
if (mExtraData) {
|
||||
mCodecContext->extradata_size = mExtraData->Length();
|
||||
for (int i = 0; i < FF_INPUT_BUFFER_PADDING_SIZE; i++) {
|
||||
mExtraData.append(0);
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
memmove(data, aData, size);
|
||||
}
|
||||
|
||||
~VP8Sample()
|
||||
virtual ~VP8Sample()
|
||||
{
|
||||
delete data;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,9 +1203,11 @@ 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),
|
||||
aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height),
|
||||
offsetScaledDirtyRegion);
|
||||
|
||||
aTile.Flip();
|
||||
|
||||
return aTile;
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче