merge mozilla-inbound to mozilla-central a=merge
|
@ -271,7 +271,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||||
if (text.mString.IsEmpty()) {
|
if (text.mString.IsEmpty()) {
|
||||||
#ifdef A11Y_LOG
|
#ifdef A11Y_LOG
|
||||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||||
logging::MsgBegin("TREE", "text node lost its content");
|
logging::MsgBegin("TREE", "text node lost its content; doc: %p", mDocument);
|
||||||
logging::Node("container", containerElm);
|
logging::Node("container", containerElm);
|
||||||
logging::Node("content", textNode);
|
logging::Node("content", textNode);
|
||||||
logging::MsgEnd();
|
logging::MsgEnd();
|
||||||
|
@ -285,7 +285,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||||
// Update text of the accessible and fire text change events.
|
// Update text of the accessible and fire text change events.
|
||||||
#ifdef A11Y_LOG
|
#ifdef A11Y_LOG
|
||||||
if (logging::IsEnabled(logging::eText)) {
|
if (logging::IsEnabled(logging::eText)) {
|
||||||
logging::MsgBegin("TEXT", "text may be changed");
|
logging::MsgBegin("TEXT", "text may be changed; doc: %p", mDocument);
|
||||||
logging::Node("container", containerElm);
|
logging::Node("container", containerElm);
|
||||||
logging::Node("content", textNode);
|
logging::Node("content", textNode);
|
||||||
logging::MsgEntry("old text '%s'",
|
logging::MsgEntry("old text '%s'",
|
||||||
|
@ -304,7 +304,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||||
if (!text.mString.IsEmpty()) {
|
if (!text.mString.IsEmpty()) {
|
||||||
#ifdef A11Y_LOG
|
#ifdef A11Y_LOG
|
||||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||||
logging::MsgBegin("TREE", "text node gains new content");
|
logging::MsgBegin("TREE", "text node gains new content; doc: %p", mDocument);
|
||||||
logging::Node("container", containerElm);
|
logging::Node("container", containerElm);
|
||||||
logging::Node("content", textNode);
|
logging::Node("content", textNode);
|
||||||
logging::MsgEnd();
|
logging::MsgEnd();
|
||||||
|
|
|
@ -558,9 +558,10 @@ nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
|
||||||
nsIContent* aStartChild,
|
nsIContent* aStartChild,
|
||||||
nsIContent* aEndChild)
|
nsIContent* aEndChild)
|
||||||
{
|
{
|
||||||
|
DocAccessible* document = GetDocAccessible(aPresShell);
|
||||||
#ifdef A11Y_LOG
|
#ifdef A11Y_LOG
|
||||||
if (logging::IsEnabled(logging::eTree)) {
|
if (logging::IsEnabled(logging::eTree)) {
|
||||||
logging::MsgBegin("TREE", "content inserted");
|
logging::MsgBegin("TREE", "content inserted; doc: %p", document);
|
||||||
logging::Node("container", aContainer);
|
logging::Node("container", aContainer);
|
||||||
for (nsIContent* child = aStartChild; child != aEndChild;
|
for (nsIContent* child = aStartChild; child != aEndChild;
|
||||||
child = child->GetNextSibling()) {
|
child = child->GetNextSibling()) {
|
||||||
|
@ -571,25 +572,25 @@ nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DocAccessible* docAccessible = GetDocAccessible(aPresShell);
|
if (document) {
|
||||||
if (docAccessible)
|
document->ContentInserted(aContainer, aStartChild, aEndChild);
|
||||||
docAccessible->ContentInserted(aContainer, aStartChild, aEndChild);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
|
nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
|
||||||
nsIContent* aChildNode)
|
nsIContent* aChildNode)
|
||||||
{
|
{
|
||||||
|
DocAccessible* document = GetDocAccessible(aPresShell);
|
||||||
#ifdef A11Y_LOG
|
#ifdef A11Y_LOG
|
||||||
if (logging::IsEnabled(logging::eTree)) {
|
if (logging::IsEnabled(logging::eTree)) {
|
||||||
logging::MsgBegin("TREE", "content removed");
|
logging::MsgBegin("TREE", "content removed; doc: %p", document);
|
||||||
logging::Node("container node", aChildNode->GetFlattenedTreeParent());
|
logging::Node("container node", aChildNode->GetFlattenedTreeParent());
|
||||||
logging::Node("content node", aChildNode);
|
logging::Node("content node", aChildNode);
|
||||||
logging::MsgEnd();
|
logging::MsgEnd();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DocAccessible* document = GetDocAccessible(aPresShell);
|
|
||||||
if (document) {
|
if (document) {
|
||||||
// Flatten hierarchy may be broken at this point so we cannot get a true
|
// Flatten hierarchy may be broken at this point so we cannot get a true
|
||||||
// container by traversing up the DOM tree. Find a parent of first accessible
|
// container by traversing up the DOM tree. Find a parent of first accessible
|
||||||
|
|
|
@ -1814,6 +1814,15 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer, nsIContent* aNode)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef A11Y_LOG
|
||||||
|
logging::TreeInfo("children before insertion", logging::eVerbose, aContainer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef A11Y_LOG
|
||||||
|
logging::TreeInfo("traversing an inserted node", logging::eVerbose,
|
||||||
|
"container", aContainer, "node", aNode);
|
||||||
|
#endif
|
||||||
|
|
||||||
TreeWalker walker(aContainer);
|
TreeWalker walker(aContainer);
|
||||||
if (aContainer->IsAcceptableChild(aNode) && walker.Seek(aNode)) {
|
if (aContainer->IsAcceptableChild(aNode) && walker.Seek(aNode)) {
|
||||||
Accessible* child = GetAccessible(aNode);
|
Accessible* child = GetAccessible(aNode);
|
||||||
|
@ -1831,6 +1840,10 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer, nsIContent* aNode)
|
||||||
FireEventsOnInsertion(aContainer);
|
FireEventsOnInsertion(aContainer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef A11Y_LOG
|
||||||
|
logging::TreeInfo("children after insertion", logging::eVerbose, aContainer);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -138,7 +138,7 @@ GetAppIni(int argc, const char *argv[])
|
||||||
|
|
||||||
char appEnv[MAXPATHLEN];
|
char appEnv[MAXPATHLEN];
|
||||||
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
||||||
if (putenv(appEnv)) {
|
if (putenv(strdup(appEnv))) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ static int do_main(int argc, char* argv[])
|
||||||
|
|
||||||
char appEnv[MAXPATHLEN];
|
char appEnv[MAXPATHLEN];
|
||||||
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
||||||
if (putenv(appEnv)) {
|
if (putenv(strdup(appEnv))) {
|
||||||
Output("Couldn't set %s.\n", appEnv);
|
Output("Couldn't set %s.\n", appEnv);
|
||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ static int do_main(int argc, char* argv[], char* envp[], nsIFile *xreDirectory)
|
||||||
|
|
||||||
char appEnv[MAXPATHLEN];
|
char appEnv[MAXPATHLEN];
|
||||||
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
||||||
if (putenv(appEnv)) {
|
if (putenv(strdup(appEnv))) {
|
||||||
Output("Couldn't set %s.\n", appEnv);
|
Output("Couldn't set %s.\n", appEnv);
|
||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ LOCAL_INCLUDES += [
|
||||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||||
OS_LIBS += [
|
OS_LIBS += [
|
||||||
'esent',
|
'esent',
|
||||||
|
'netapi32',
|
||||||
'ole32',
|
'ole32',
|
||||||
'shell32',
|
'shell32',
|
||||||
'shlwapi',
|
'shlwapi',
|
||||||
|
@ -32,6 +33,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||||
]
|
]
|
||||||
DELAYLOAD_DLLS += [
|
DELAYLOAD_DLLS += [
|
||||||
'esent.dll',
|
'esent.dll',
|
||||||
|
'netapi32.dll',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code)
|
# Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code)
|
||||||
|
|
|
@ -873,6 +873,7 @@ BrowserGlue.prototype = {
|
||||||
label: win.gNavigatorBundle.getString("pendingCrashReports.viewAll"),
|
label: win.gNavigatorBundle.getString("pendingCrashReports.viewAll"),
|
||||||
callback: function() {
|
callback: function() {
|
||||||
win.openUILinkIn("about:crashes", "tab");
|
win.openUILinkIn("about:crashes", "tab");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
||||||
|
"resource:///modules/CustomizableUI.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Console",
|
||||||
|
"resource://gre/modules/Console.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||||
|
"resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyGetter(this, "gFlyWebBundle", function() {
|
||||||
|
const tns = {
|
||||||
|
"flyweb-button.label": "FlyWeb",
|
||||||
|
"flyweb-button.tooltiptext": "Discover nearby FlyWeb services",
|
||||||
|
"flyweb-items-empty": "There are no FlyWeb services currently nearby"
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
GetStringFromName(name) {
|
||||||
|
return tns[name];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const FLYWEB_ENABLED_PREF = "dom.flyweb.enabled";
|
||||||
|
|
||||||
|
function install(aData, aReason) {}
|
||||||
|
|
||||||
|
function uninstall(aData, aReason) {}
|
||||||
|
|
||||||
|
function startup(aData, aReason) {
|
||||||
|
// Observe pref changes and enable/disable as necessary.
|
||||||
|
Services.prefs.addObserver(FLYWEB_ENABLED_PREF, prefObserver, false);
|
||||||
|
|
||||||
|
// Only initialize if pref is enabled.
|
||||||
|
let enabled = Services.prefs.getBoolPref(FLYWEB_ENABLED_PREF);
|
||||||
|
if (enabled) {
|
||||||
|
FlyWebView.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shutdown(aData, aReason) {
|
||||||
|
Services.prefs.removeObserver(FLYWEB_ENABLED_PREF, prefObserver);
|
||||||
|
|
||||||
|
let enabled = Services.prefs.getBoolPref(FLYWEB_ENABLED_PREF);
|
||||||
|
if (enabled) {
|
||||||
|
FlyWebView.uninit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use enabled pref as a way for tests (e.g. test_contextmenu.html) to disable
|
||||||
|
// the addon when running.
|
||||||
|
function prefObserver(aSubject, aTopic, aData) {
|
||||||
|
let enabled = Services.prefs.getBoolPref(FLYWEB_ENABLED_PREF);
|
||||||
|
if (enabled) {
|
||||||
|
FlyWebView.init();
|
||||||
|
} else {
|
||||||
|
FlyWebView.uninit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gDiscoveryManagerInstance;
|
||||||
|
|
||||||
|
class DiscoveryManager {
|
||||||
|
constructor(aWindow) {
|
||||||
|
this._discoveryManager = new aWindow.FlyWebDiscoveryManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this._id) {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._discoveryManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
start(callback) {
|
||||||
|
if (!this._id) {
|
||||||
|
this._id = this._discoveryManager.startDiscovery(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this._discoveryManager.stopDiscovery(this._id);
|
||||||
|
|
||||||
|
this._id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pairWith(serviceId, callback) {
|
||||||
|
this._discoveryManager.pairWithService(serviceId, {
|
||||||
|
pairingSucceeded(service) {
|
||||||
|
callback(service);
|
||||||
|
},
|
||||||
|
|
||||||
|
pairingFailed(error) {
|
||||||
|
console.error("FlyWeb failed to pair with service " + serviceId, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDiscoveredServicesChanged(services) {
|
||||||
|
if (!this._id || !this._callback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._callback(services);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let FlyWebView = {
|
||||||
|
init() {
|
||||||
|
// Create widget and add it to the menu panel.
|
||||||
|
CustomizableUI.createWidget({
|
||||||
|
id: "flyweb-button",
|
||||||
|
type: "view",
|
||||||
|
viewId: "flyweb-panel",
|
||||||
|
label: gFlyWebBundle.GetStringFromName("flyweb-button.label"),
|
||||||
|
tooltiptext: gFlyWebBundle.GetStringFromName("flyweb-button.tooltiptext"),
|
||||||
|
|
||||||
|
onBeforeCreated(aDocument) {
|
||||||
|
let panel = aDocument.createElement("panelview");
|
||||||
|
panel.id = "flyweb-panel";
|
||||||
|
panel.setAttribute("class", "PanelUI-subView");
|
||||||
|
panel.setAttribute("flex", "1");
|
||||||
|
|
||||||
|
let label = aDocument.createElement("label");
|
||||||
|
label.setAttribute("class", "panel-subview-header");
|
||||||
|
label.setAttribute("value", gFlyWebBundle.GetStringFromName("flyweb-button.label"));
|
||||||
|
|
||||||
|
let empty = aDocument.createElement("description");
|
||||||
|
empty.id = "flyweb-items-empty";
|
||||||
|
empty.setAttribute("mousethrough", "always");
|
||||||
|
empty.textContent = gFlyWebBundle.GetStringFromName("flyweb-items-empty");
|
||||||
|
|
||||||
|
let items = aDocument.createElement("vbox");
|
||||||
|
items.id = "flyweb-items";
|
||||||
|
items.setAttribute("class", "panel-subview-body");
|
||||||
|
|
||||||
|
panel.appendChild(label);
|
||||||
|
panel.appendChild(empty);
|
||||||
|
panel.appendChild(items);
|
||||||
|
|
||||||
|
panel.addEventListener("command", this);
|
||||||
|
|
||||||
|
aDocument.getElementById("PanelUI-multiView").appendChild(panel);
|
||||||
|
|
||||||
|
this._sheetURI = Services.io.newURI("chrome://flyweb/skin/flyweb.css", null, null);
|
||||||
|
aDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||||
|
getInterface(Ci.nsIDOMWindowUtils).loadSheet(this._sheetURI, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDestroyed(aDocument) {
|
||||||
|
aDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||||
|
getInterface(Ci.nsIDOMWindowUtils).removeSheet(this._sheetURI, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
onViewShowing(aEvent) {
|
||||||
|
let doc = aEvent.target.ownerDocument;
|
||||||
|
|
||||||
|
let panel = doc.getElementById("flyweb-panel");
|
||||||
|
let items = doc.getElementById("flyweb-items");
|
||||||
|
let empty = doc.getElementById("flyweb-items-empty");
|
||||||
|
|
||||||
|
if (!gDiscoveryManagerInstance) {
|
||||||
|
gDiscoveryManagerInstance = new DiscoveryManager(doc.defaultView);
|
||||||
|
}
|
||||||
|
|
||||||
|
gDiscoveryManagerInstance.start((services) => {
|
||||||
|
while (items.firstChild) {
|
||||||
|
items.firstChild.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
let fragment = doc.createDocumentFragment();
|
||||||
|
|
||||||
|
for (let service of services) {
|
||||||
|
let button = doc.createElement("toolbarbutton");
|
||||||
|
button.setAttribute("class", "subviewbutton cui-withicon");
|
||||||
|
button.setAttribute("label", service.displayName);
|
||||||
|
button.setAttribute("data-service-id", service.serviceId);
|
||||||
|
fragment.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.appendChild(fragment);
|
||||||
|
|
||||||
|
empty.hidden = services.length > 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onViewHiding(aEvent) {
|
||||||
|
gDiscoveryManagerInstance.stop();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEvent(aEvent) {
|
||||||
|
if (aEvent.type === "command") {
|
||||||
|
let serviceId = aEvent.target.getAttribute("data-service-id");
|
||||||
|
gDiscoveryManagerInstance.pairWith(serviceId, (service) => {
|
||||||
|
aEvent.view.openUILinkIn(service.uiUrl, "tab");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
uninit() {
|
||||||
|
CustomizableUI.destroyWidget("flyweb-button");
|
||||||
|
|
||||||
|
if (gDiscoveryManagerInstance) {
|
||||||
|
gDiscoveryManagerInstance.destroy();
|
||||||
|
gDiscoveryManagerInstance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- 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/. -->
|
||||||
|
|
||||||
|
#filter substitution
|
||||||
|
|
||||||
|
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||||
|
|
||||||
|
<Description about="urn:mozilla:install-manifest">
|
||||||
|
<em:id>flyweb@mozilla.org</em:id>
|
||||||
|
<em:version>1.0.0</em:version>
|
||||||
|
<em:type>2</em:type>
|
||||||
|
<em:bootstrap>true</em:bootstrap>
|
||||||
|
|
||||||
|
<!-- Target Application this theme can install into,
|
||||||
|
with minimum and maximum supported versions. -->
|
||||||
|
<em:targetApplication>
|
||||||
|
<Description>
|
||||||
|
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||||
|
<em:minVersion>@FIREFOX_VERSION@</em:minVersion>
|
||||||
|
<em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
|
||||||
|
</Description>
|
||||||
|
</em:targetApplication>
|
||||||
|
|
||||||
|
<!-- Front End MetaData -->
|
||||||
|
<em:name>FlyWeb</em:name>
|
||||||
|
<em:description>Discover nearby services in the browser</em:description>
|
||||||
|
</Description>
|
||||||
|
</RDF>
|
|
@ -0,0 +1,10 @@
|
||||||
|
# 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/.
|
||||||
|
|
||||||
|
[features/flyweb@mozilla.org] chrome.jar:
|
||||||
|
% skin flyweb classic/1.0 %skin/linux/
|
||||||
|
% skin flyweb classic/1.0 %skin/osx/ os=Darwin
|
||||||
|
% skin flyweb classic/1.0 %skin/windows/ os=WINNT
|
||||||
|
% skin flyweb-shared classic/1.0 %skin/shared/
|
||||||
|
skin/ (skin/*)
|
|
@ -0,0 +1,15 @@
|
||||||
|
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||||
|
# vim: set filetype=python:
|
||||||
|
# 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/.
|
||||||
|
|
||||||
|
FINAL_TARGET_FILES.features['flyweb@mozilla.org'] += [
|
||||||
|
'bootstrap.js'
|
||||||
|
]
|
||||||
|
|
||||||
|
FINAL_TARGET_PP_FILES.features['flyweb@mozilla.org'] += [
|
||||||
|
'install.rdf.in'
|
||||||
|
]
|
||||||
|
|
||||||
|
JAR_MANIFESTS += ['jar.mn']
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
|
||||||
|
<circle fill="#797C80" cx="32" cy="52" r="6"/>
|
||||||
|
<g>
|
||||||
|
<path fill="#797C80" d="M6.894,15.255c-2.254,1.547-4.386,3.304-6.361,5.279L0.18,20.887l3.536,3.536l0.354-0.354
|
||||||
|
c1.621-1.62,3.363-3.072,5.196-4.369C8.126,18.464,7.296,16.943,6.894,15.255z"/>
|
||||||
|
<path fill="#797C80" d="M63.465,20.532C55.061,12.128,43.887,7.5,32,7.5c-2.265,0-4.504,0.17-6.703,0.501
|
||||||
|
c0.822,1.44,1.3,3.1,1.312,4.87C28.382,12.631,30.181,12.5,32,12.5c10.55,0,20.468,4.108,27.928,11.567l0.354,0.354l3.537-3.535
|
||||||
|
L63.465,20.532z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#797C80" d="M16.613,10.94c1.103,0,2,0.897,2,2s-0.897,2-2,2s-2-0.897-2-2S15.51,10.94,16.613,10.94 M16.613,6.94
|
||||||
|
c-3.313,0-6,2.687-6,6s2.687,6,6,6s6-2.687,6-6S19.926,6.94,16.613,6.94L16.613,6.94z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#797C80" d="M46.492,37.502c-1.853-1.852-4.002-3.292-6.334-4.305c0.031,0.324,0.05,0.652,0.05,0.984
|
||||||
|
c0,1.477-0.33,2.874-0.906,4.137c1.329,0.712,2.561,1.623,3.657,2.719l0.354,0.354l3.533-3.535L46.492,37.502z"/>
|
||||||
|
<path fill="#797C80" d="M20.262,35.207c-0.972,0.683-1.9,1.439-2.758,2.297l-0.354,0.354l3.536,3.537l0.354-0.354
|
||||||
|
c0.35-0.35,0.715-0.679,1.091-0.99C21.118,38.66,20.446,37.007,20.262,35.207z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#797C80" d="M30.209,32.182c1.102,0,1.999,0.897,1.999,2s-0.896,2-1.999,2c-1.103,0-2-0.897-2-2
|
||||||
|
S29.106,32.182,30.209,32.182 M30.209,28.182c-3.313,0-6,2.686-6,6c0,3.312,2.687,6,6,6c3.313,0,5.999-2.688,5.999-6
|
||||||
|
C36.208,30.867,33.522,28.182,30.209,28.182L30.209,28.182z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#797C80" d="M32.207,23.716c0-1.497,0.34-2.912,0.932-4.188C32.76,19.515,32.381,19.5,32,19.5
|
||||||
|
c-8.681,0-16.843,3.381-22.981,9.52l-0.354,0.354l3.535,3.535l0.354-0.354C17.748,27.36,24.654,24.5,32,24.5
|
||||||
|
c0.083,0,0.165,0.005,0.247,0.006C32.227,24.245,32.207,23.982,32.207,23.716z"/>
|
||||||
|
<path fill="#797C80" d="M54.98,29.018c-0.987-0.987-2.033-1.896-3.119-2.738c-0.447,1.68-1.313,3.188-2.491,4.399
|
||||||
|
c0.717,0.586,1.409,1.21,2.073,1.874l0.354,0.354l3.537-3.535L54.98,29.018z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#797C80" d="M42.207,21.716c1.103,0,2,0.897,2,2s-0.897,2-2,2s-2-0.897-2-2S41.104,21.716,42.207,21.716 M42.207,17.716
|
||||||
|
c-3.313,0-6,2.687-6,6s2.687,6,6,6s6-2.687,6-6S45.521,17.716,42.207,17.716L42.207,17.716z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
После Ширина: | Высота: | Размер: 2.6 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
@import url("chrome://flyweb-shared/skin/flyweb.css");
|
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 2.6 KiB |
После Ширина: | Высота: | Размер: 699 B |
После Ширина: | Высота: | Размер: 6.3 KiB |
После Ширина: | Высота: | Размер: 1.3 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
@import url("chrome://flyweb-shared/skin/flyweb.css");
|
После Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 2.6 KiB |
После Ширина: | Высота: | Размер: 2.5 KiB |
После Ширина: | Высота: | Размер: 6.3 KiB |
После Ширина: | Высота: | Размер: 3.1 KiB |
|
@ -0,0 +1,54 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#flyweb-panel {
|
||||||
|
width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-items-empty {
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: GrayText;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-button {
|
||||||
|
list-style-image: url("chrome://flyweb/skin/icon-16.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-button[cui-areatype="menu-panel"],
|
||||||
|
toolbarpaletteitem[place="palette"] > #flyweb-button {
|
||||||
|
list-style-image: url("chrome://flyweb/skin/icon-32.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-button[cui-areatype="menu-panel"][panel-multiview-anchor="true"] {
|
||||||
|
list-style-image: url("chrome://flyweb/skin/icon-32-anchored.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-items > toolbarbutton {
|
||||||
|
list-style-image: url("chrome://flyweb/skin/icon-16.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-resolution: 2dppx) {
|
||||||
|
#flyweb-button {
|
||||||
|
list-style-image: url("chrome://flyweb/skin/icon-32.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-button[cui-areatype="menu-panel"],
|
||||||
|
toolbarpaletteitem[place="palette"] > #flyweb-button {
|
||||||
|
list-style-image: url("chrome://flyweb/skin/icon-64.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-button[cui-areatype="menu-panel"][panel-multiview-anchor="true"] {
|
||||||
|
list-style-image: url("chrome://flyweb/skin/icon-64-anchored.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-items > toolbarbutton {
|
||||||
|
list-style-image: url("chrome://flyweb/skin/icon-32.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#flyweb-items > toolbarbutton > .toolbarbutton-icon {
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
@import url("chrome://flyweb-shared/skin/flyweb.css");
|
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 2.6 KiB |
После Ширина: | Высота: | Размер: 699 B |
После Ширина: | Высота: | Размер: 6.3 KiB |
После Ширина: | Высота: | Размер: 1.3 KiB |
|
@ -11,3 +11,9 @@ DIRS += [
|
||||||
'pocket',
|
'pocket',
|
||||||
'webcompat',
|
'webcompat',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Only include the following system add-ons if building Aurora or Nightly
|
||||||
|
if 'a' in CONFIG['GRE_MILESTONE']:
|
||||||
|
DIRS += [
|
||||||
|
'flyweb',
|
||||||
|
]
|
||||||
|
|
|
@ -96,6 +96,7 @@ leak:libdricore.so
|
||||||
leak:libdricore9.2.1.so
|
leak:libdricore9.2.1.so
|
||||||
leak:libGL.so
|
leak:libGL.so
|
||||||
leak:libglib-2.0.so
|
leak:libglib-2.0.so
|
||||||
|
leak:libglsl.so
|
||||||
leak:libp11-kit.so
|
leak:libp11-kit.so
|
||||||
leak:libpixman-1.so
|
leak:libpixman-1.so
|
||||||
leak:libpulse.so
|
leak:libpulse.so
|
||||||
|
|
|
@ -1436,7 +1436,7 @@ nsScriptSecurityManager::InitStatics()
|
||||||
RefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
|
RefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
|
||||||
nsresult rv = ssManager->Init();
|
nsresult rv = ssManager->Init();
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
MOZ_CRASH();
|
MOZ_CRASH("ssManager->Init() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearOnShutdown(&gScriptSecMan);
|
ClearOnShutdown(&gScriptSecMan);
|
||||||
|
|
|
@ -18,8 +18,6 @@ add_task(function* () {
|
||||||
|
|
||||||
reload(target);
|
reload(target);
|
||||||
|
|
||||||
let destroyed = getN(gAudioNodes, "remove", 10);
|
|
||||||
|
|
||||||
let [created] = yield Promise.all([
|
let [created] = yield Promise.all([
|
||||||
getNSpread(gAudioNodes, "add", 13),
|
getNSpread(gAudioNodes, "add", 13),
|
||||||
waitForGraphRendered(panelWin, 13, 2)
|
waitForGraphRendered(panelWin, 13, 2)
|
||||||
|
@ -32,8 +30,10 @@ add_task(function* () {
|
||||||
// Click a soon-to-be dead buffer node
|
// Click a soon-to-be dead buffer node
|
||||||
yield clickGraphNode(panelWin, actorIDs[5]);
|
yield clickGraphNode(panelWin, actorIDs[5]);
|
||||||
|
|
||||||
|
let destroyed = getN(gAudioNodes, "remove", 10);
|
||||||
|
|
||||||
// Force a CC in the child process to collect the orphaned nodes.
|
// Force a CC in the child process to collect the orphaned nodes.
|
||||||
forceCC();
|
forceNodeCollection();
|
||||||
|
|
||||||
// Wait for destruction and graph to re-render
|
// Wait for destruction and graph to re-render
|
||||||
yield Promise.all([destroyed, waitForGraphRendered(panelWin, 3, 2)]);
|
yield Promise.all([destroyed, waitForGraphRendered(panelWin, 3, 2)]);
|
||||||
|
|
|
@ -8,20 +8,18 @@
|
||||||
add_task(function* () {
|
add_task(function* () {
|
||||||
let { target, front } = yield initBackend(DESTROY_NODES_URL);
|
let { target, front } = yield initBackend(DESTROY_NODES_URL);
|
||||||
|
|
||||||
let waitUntilDestroyed = getN(front, "destroy-node", 10);
|
|
||||||
let [, , created] = yield Promise.all([
|
let [, , created] = yield Promise.all([
|
||||||
front.setup({ reload: true }),
|
front.setup({ reload: true }),
|
||||||
once(front, "start-context"),
|
once(front, "start-context"),
|
||||||
// Should create 1 destination node and 10 disposable buffer nodes
|
// Should create dest, gain, and oscillator node and 10
|
||||||
|
// disposable buffer nodes
|
||||||
getN(front, "create-node", 13)
|
getN(front, "create-node", 13)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Wait for a tick before gc to prevent this test from intermittent timeout
|
let waitUntilDestroyed = getN(front, "destroy-node", 10);
|
||||||
// where the node never get collected.
|
|
||||||
yield DevToolsUtils.waitForTick();
|
|
||||||
|
|
||||||
// Force CC so we can ensure it's run to clear out dead AudioNodes
|
// Force CC so we can ensure it's run to clear out dead AudioNodes
|
||||||
forceCC();
|
forceNodeCollection();
|
||||||
|
|
||||||
let destroyed = yield waitUntilDestroyed;
|
let destroyed = yield waitUntilDestroyed;
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,17 @@
|
||||||
|
|
||||||
<script type="text/javascript;version=1.8">
|
<script type="text/javascript;version=1.8">
|
||||||
"use strict";
|
"use strict";
|
||||||
|
// Keep the nodes we want to GC alive until we are ready for them to
|
||||||
|
// be collected. We will zero this reference by force from the devtools
|
||||||
|
// side.
|
||||||
|
var keepAlive = [];
|
||||||
(function () {
|
(function () {
|
||||||
let ctx = new AudioContext();
|
let ctx = new AudioContext();
|
||||||
let osc = ctx.createOscillator();
|
let osc = ctx.createOscillator();
|
||||||
let gain = ctx.createGain();
|
let gain = ctx.createGain();
|
||||||
|
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
ctx.createBufferSource();
|
keepAlive.push(ctx.createBufferSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
osc.connect(gain);
|
osc.connect(gain);
|
||||||
|
|
|
@ -420,8 +420,12 @@ function countGraphObjects(win) {
|
||||||
/**
|
/**
|
||||||
* Forces cycle collection and GC, used in AudioNode destruction tests.
|
* Forces cycle collection and GC, used in AudioNode destruction tests.
|
||||||
*/
|
*/
|
||||||
function forceCC() {
|
function forceNodeCollection() {
|
||||||
ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
|
ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
|
||||||
|
// Kill the reference keeping stuff alive.
|
||||||
|
content.wrappedJSObject.keepAlive = null;
|
||||||
|
|
||||||
|
// Collect the now-deceased nodes.
|
||||||
Cu.forceGC();
|
Cu.forceGC();
|
||||||
Cu.forceCC();
|
Cu.forceCC();
|
||||||
Cu.forceGC();
|
Cu.forceGC();
|
||||||
|
|
|
@ -957,9 +957,6 @@ BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
||||||
std::stable_sort(aEntries.begin(), aEntries.end(),
|
std::stable_sort(aEntries.begin(), aEntries.end(),
|
||||||
&KeyframeValueEntry::PropertyOffsetComparator::LessThan);
|
&KeyframeValueEntry::PropertyOffsetComparator::LessThan);
|
||||||
|
|
||||||
MOZ_ASSERT(aEntries[0].mOffset == 0.0f);
|
|
||||||
MOZ_ASSERT(aEntries.LastElement().mOffset == 1.0f);
|
|
||||||
|
|
||||||
// For a given index i, we want to generate a segment from aEntries[i]
|
// For a given index i, we want to generate a segment from aEntries[i]
|
||||||
// to aEntries[j], if:
|
// to aEntries[j], if:
|
||||||
//
|
//
|
||||||
|
@ -974,13 +971,56 @@ BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
||||||
// offset 1, if we have multiple values for a given property at that offset,
|
// offset 1, if we have multiple values for a given property at that offset,
|
||||||
// since we need to retain the very first and very last value so they can
|
// since we need to retain the very first and very last value so they can
|
||||||
// be used for reverse and forward filling.
|
// be used for reverse and forward filling.
|
||||||
|
//
|
||||||
|
// Typically, for each property in |aEntries|, we expect there to be at least
|
||||||
|
// one KeyframeValueEntry with offset 0.0, and at least one with offset 1.0.
|
||||||
|
// However, since it is possible that when building |aEntries|, the call to
|
||||||
|
// StyleAnimationValue::ComputeValues might fail, this can't be guaranteed.
|
||||||
|
// Furthermore, since we don't yet implement additive animation and hence
|
||||||
|
// don't have sensible fallback behavior when these values are missing, the
|
||||||
|
// following loop takes care to identify properties that lack a value at
|
||||||
|
// offset 0.0/1.0 and drops those properties from |aResult|.
|
||||||
|
|
||||||
nsCSSProperty lastProperty = eCSSProperty_UNKNOWN;
|
nsCSSProperty lastProperty = eCSSProperty_UNKNOWN;
|
||||||
AnimationProperty* animationProperty = nullptr;
|
AnimationProperty* animationProperty = nullptr;
|
||||||
|
|
||||||
size_t i = 0, n = aEntries.Length();
|
size_t i = 0, n = aEntries.Length();
|
||||||
|
|
||||||
while (i + 1 < n) {
|
while (i < n) {
|
||||||
|
// Check that the last property ends with an entry at offset 1.
|
||||||
|
if (i + 1 == n) {
|
||||||
|
if (aEntries[i].mOffset != 1.0f && animationProperty) {
|
||||||
|
aResult.RemoveElementAt(aResult.Length() - 1);
|
||||||
|
animationProperty = nullptr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(aEntries[i].mProperty != eCSSProperty_UNKNOWN &&
|
||||||
|
aEntries[i + 1].mProperty != eCSSProperty_UNKNOWN,
|
||||||
|
"Each entry should specify a valid property");
|
||||||
|
|
||||||
|
// Skip properties that don't have an entry with offset 0.
|
||||||
|
if (aEntries[i].mProperty != lastProperty &&
|
||||||
|
aEntries[i].mOffset != 0.0f) {
|
||||||
|
// Since the entries are sorted by offset for a given property, and
|
||||||
|
// since we don't update |lastProperty|, we will keep hitting this
|
||||||
|
// condition until we change property.
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop properties that don't end with an entry with offset 1.
|
||||||
|
if (aEntries[i].mProperty != aEntries[i + 1].mProperty &&
|
||||||
|
aEntries[i].mOffset != 1.0f) {
|
||||||
|
if (animationProperty) {
|
||||||
|
aResult.RemoveElementAt(aResult.Length() - 1);
|
||||||
|
animationProperty = nullptr;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Starting from i, determine the next [i, j] interval from which to
|
// Starting from i, determine the next [i, j] interval from which to
|
||||||
// generate a segment.
|
// generate a segment.
|
||||||
size_t j;
|
size_t j;
|
||||||
|
@ -988,23 +1028,24 @@ BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
||||||
// We need to generate an initial zero-length segment.
|
// We need to generate an initial zero-length segment.
|
||||||
MOZ_ASSERT(aEntries[i].mProperty == aEntries[i + 1].mProperty);
|
MOZ_ASSERT(aEntries[i].mProperty == aEntries[i + 1].mProperty);
|
||||||
j = i + 1;
|
j = i + 1;
|
||||||
while (aEntries[j + 1].mOffset == 0.0f) {
|
while (aEntries[j + 1].mOffset == 0.0f &&
|
||||||
MOZ_ASSERT(aEntries[j].mProperty == aEntries[j + 1].mProperty);
|
aEntries[j + 1].mProperty == aEntries[j].mProperty) {
|
||||||
++j;
|
++j;
|
||||||
}
|
}
|
||||||
} else if (aEntries[i].mOffset == 1.0f) {
|
} else if (aEntries[i].mOffset == 1.0f) {
|
||||||
if (aEntries[i + 1].mOffset == 1.0f) {
|
if (aEntries[i + 1].mOffset == 1.0f &&
|
||||||
|
aEntries[i + 1].mProperty == aEntries[i].mProperty) {
|
||||||
// We need to generate a final zero-length segment.
|
// We need to generate a final zero-length segment.
|
||||||
MOZ_ASSERT(aEntries[i].mProperty == aEntries[i].mProperty);
|
|
||||||
j = i + 1;
|
j = i + 1;
|
||||||
while (j + 1 < n && aEntries[j + 1].mOffset == 1.0f) {
|
while (j + 1 < n &&
|
||||||
MOZ_ASSERT(aEntries[j].mProperty == aEntries[j + 1].mProperty);
|
aEntries[j + 1].mOffset == 1.0f &&
|
||||||
|
aEntries[j + 1].mProperty == aEntries[j].mProperty) {
|
||||||
++j;
|
++j;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// New property.
|
// New property.
|
||||||
MOZ_ASSERT(aEntries[i + 1].mOffset == 0.0f);
|
|
||||||
MOZ_ASSERT(aEntries[i].mProperty != aEntries[i + 1].mProperty);
|
MOZ_ASSERT(aEntries[i].mProperty != aEntries[i + 1].mProperty);
|
||||||
|
animationProperty = nullptr;
|
||||||
++i;
|
++i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1020,6 +1061,7 @@ BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
||||||
// to insert segments into.
|
// to insert segments into.
|
||||||
if (aEntries[i].mProperty != lastProperty) {
|
if (aEntries[i].mProperty != lastProperty) {
|
||||||
MOZ_ASSERT(aEntries[i].mOffset == 0.0f);
|
MOZ_ASSERT(aEntries[i].mOffset == 0.0f);
|
||||||
|
MOZ_ASSERT(!animationProperty);
|
||||||
animationProperty = aResult.AppendElement();
|
animationProperty = aResult.AppendElement();
|
||||||
animationProperty->mProperty = aEntries[i].mProperty;
|
animationProperty->mProperty = aEntries[i].mProperty;
|
||||||
lastProperty = aEntries[i].mProperty;
|
lastProperty = aEntries[i].mProperty;
|
||||||
|
|
|
@ -112,8 +112,11 @@ enum {
|
||||||
ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS |
|
ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS |
|
||||||
ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR,
|
ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR,
|
||||||
|
|
||||||
|
// Set if this element is marked as 'scrollgrab' (see bug 912666)
|
||||||
|
ELEMENT_HAS_SCROLLGRAB = ELEMENT_FLAG_BIT(5),
|
||||||
|
|
||||||
// Remaining bits are for subclasses
|
// Remaining bits are for subclasses
|
||||||
ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 5
|
ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 6
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef ELEMENT_FLAG_BIT
|
#undef ELEMENT_FLAG_BIT
|
||||||
|
|
|
@ -98,3 +98,4 @@ MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSEXN_TYPEERR, "Invalid easing '{0}'.")
|
||||||
MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
|
MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
|
||||||
MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <{1}> does not define any supported tokens")
|
MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <{1}> does not define any supported tokens")
|
||||||
MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEXN_TYPEERR, "Response body is a cache file stream that has already been closed.")
|
MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEXN_TYPEERR, "Response body is a cache file stream that has already been closed.")
|
||||||
|
MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.")
|
||||||
|
|
|
@ -71,14 +71,12 @@ class InitializeRunnable final : public WorkerMainThreadRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InitializeRunnable(WorkerPrivate* aWorkerPrivate, nsACString& aOrigin,
|
InitializeRunnable(WorkerPrivate* aWorkerPrivate, nsACString& aOrigin,
|
||||||
PrincipalInfo& aPrincipalInfo, bool& aPrivateBrowsing,
|
PrincipalInfo& aPrincipalInfo, ErrorResult& aRv)
|
||||||
ErrorResult& aRv)
|
|
||||||
: WorkerMainThreadRunnable(aWorkerPrivate,
|
: WorkerMainThreadRunnable(aWorkerPrivate,
|
||||||
NS_LITERAL_CSTRING("BroadcastChannel :: Initialize"))
|
NS_LITERAL_CSTRING("BroadcastChannel :: Initialize"))
|
||||||
, mWorkerPrivate(GetCurrentThreadWorkerPrivate())
|
, mWorkerPrivate(GetCurrentThreadWorkerPrivate())
|
||||||
, mOrigin(aOrigin)
|
, mOrigin(aOrigin)
|
||||||
, mPrincipalInfo(aPrincipalInfo)
|
, mPrincipalInfo(aPrincipalInfo)
|
||||||
, mPrivateBrowsing(aPrivateBrowsing)
|
|
||||||
, mRv(aRv)
|
, mRv(aRv)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mWorkerPrivate);
|
MOZ_ASSERT(mWorkerPrivate);
|
||||||
|
@ -127,11 +125,6 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIDocument* doc = window->GetExtantDoc();
|
|
||||||
if (doc) {
|
|
||||||
mPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +132,6 @@ private:
|
||||||
WorkerPrivate* mWorkerPrivate;
|
WorkerPrivate* mWorkerPrivate;
|
||||||
nsACString& mOrigin;
|
nsACString& mOrigin;
|
||||||
PrincipalInfo& mPrincipalInfo;
|
PrincipalInfo& mPrincipalInfo;
|
||||||
bool& mPrivateBrowsing;
|
|
||||||
ErrorResult& mRv;
|
ErrorResult& mRv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -306,14 +298,12 @@ private:
|
||||||
BroadcastChannel::BroadcastChannel(nsPIDOMWindowInner* aWindow,
|
BroadcastChannel::BroadcastChannel(nsPIDOMWindowInner* aWindow,
|
||||||
const PrincipalInfo& aPrincipalInfo,
|
const PrincipalInfo& aPrincipalInfo,
|
||||||
const nsACString& aOrigin,
|
const nsACString& aOrigin,
|
||||||
const nsAString& aChannel,
|
const nsAString& aChannel)
|
||||||
bool aPrivateBrowsing)
|
|
||||||
: DOMEventTargetHelper(aWindow)
|
: DOMEventTargetHelper(aWindow)
|
||||||
, mWorkerFeature(nullptr)
|
, mWorkerFeature(nullptr)
|
||||||
, mPrincipalInfo(new PrincipalInfo(aPrincipalInfo))
|
, mPrincipalInfo(new PrincipalInfo(aPrincipalInfo))
|
||||||
, mOrigin(aOrigin)
|
, mOrigin(aOrigin)
|
||||||
, mChannel(aChannel)
|
, mChannel(aChannel)
|
||||||
, mPrivateBrowsing(aPrivateBrowsing)
|
|
||||||
, mIsKeptAlive(false)
|
, mIsKeptAlive(false)
|
||||||
, mInnerID(0)
|
, mInnerID(0)
|
||||||
, mState(StateActive)
|
, mState(StateActive)
|
||||||
|
@ -344,7 +334,6 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal,
|
||||||
|
|
||||||
nsAutoCString origin;
|
nsAutoCString origin;
|
||||||
PrincipalInfo principalInfo;
|
PrincipalInfo principalInfo;
|
||||||
bool privateBrowsing = false;
|
|
||||||
WorkerPrivate* workerPrivate = nullptr;
|
WorkerPrivate* workerPrivate = nullptr;
|
||||||
|
|
||||||
if (NS_IsMainThread()) {
|
if (NS_IsMainThread()) {
|
||||||
|
@ -381,19 +370,13 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal,
|
||||||
if (NS_WARN_IF(aRv.Failed())) {
|
if (NS_WARN_IF(aRv.Failed())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIDocument* doc = window->GetExtantDoc();
|
|
||||||
if (doc) {
|
|
||||||
privateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
JSContext* cx = aGlobal.Context();
|
JSContext* cx = aGlobal.Context();
|
||||||
workerPrivate = GetWorkerPrivateFromContext(cx);
|
workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||||
MOZ_ASSERT(workerPrivate);
|
MOZ_ASSERT(workerPrivate);
|
||||||
|
|
||||||
RefPtr<InitializeRunnable> runnable =
|
RefPtr<InitializeRunnable> runnable =
|
||||||
new InitializeRunnable(workerPrivate, origin, principalInfo,
|
new InitializeRunnable(workerPrivate, origin, principalInfo, aRv);
|
||||||
privateBrowsing, aRv);
|
|
||||||
runnable->Dispatch(aRv);
|
runnable->Dispatch(aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,8 +385,7 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal,
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<BroadcastChannel> bc =
|
RefPtr<BroadcastChannel> bc =
|
||||||
new BroadcastChannel(window, principalInfo, origin, aChannel,
|
new BroadcastChannel(window, principalInfo, origin, aChannel);
|
||||||
privateBrowsing);
|
|
||||||
|
|
||||||
// Register this component to PBackground.
|
// Register this component to PBackground.
|
||||||
PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
|
PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
|
||||||
|
@ -522,8 +504,7 @@ BroadcastChannel::ActorCreated(PBackgroundChild* aActor)
|
||||||
}
|
}
|
||||||
|
|
||||||
PBroadcastChannelChild* actor =
|
PBroadcastChannelChild* actor =
|
||||||
aActor->SendPBroadcastChannelConstructor(*mPrincipalInfo, mOrigin, mChannel,
|
aActor->SendPBroadcastChannelConstructor(*mPrincipalInfo, mOrigin, mChannel);
|
||||||
mPrivateBrowsing);
|
|
||||||
|
|
||||||
mActor = static_cast<BroadcastChannelChild*>(actor);
|
mActor = static_cast<BroadcastChannelChild*>(actor);
|
||||||
MOZ_ASSERT(mActor);
|
MOZ_ASSERT(mActor);
|
||||||
|
|
|
@ -88,8 +88,7 @@ private:
|
||||||
BroadcastChannel(nsPIDOMWindowInner* aWindow,
|
BroadcastChannel(nsPIDOMWindowInner* aWindow,
|
||||||
const PrincipalInfo& aPrincipalInfo,
|
const PrincipalInfo& aPrincipalInfo,
|
||||||
const nsACString& aOrigin,
|
const nsACString& aOrigin,
|
||||||
const nsAString& aChannel,
|
const nsAString& aChannel);
|
||||||
bool aPrivateBrowsing);
|
|
||||||
|
|
||||||
~BroadcastChannel();
|
~BroadcastChannel();
|
||||||
|
|
||||||
|
@ -116,7 +115,6 @@ private:
|
||||||
|
|
||||||
nsCString mOrigin;
|
nsCString mOrigin;
|
||||||
nsString mChannel;
|
nsString mChannel;
|
||||||
bool mPrivateBrowsing;
|
|
||||||
|
|
||||||
bool mIsKeptAlive;
|
bool mIsKeptAlive;
|
||||||
|
|
||||||
|
|
|
@ -220,7 +220,8 @@ static_assert(int(RequestCache::Default) == 0 &&
|
||||||
int(RequestCache::Reload) == 2 &&
|
int(RequestCache::Reload) == 2 &&
|
||||||
int(RequestCache::No_cache) == 3 &&
|
int(RequestCache::No_cache) == 3 &&
|
||||||
int(RequestCache::Force_cache) == 4 &&
|
int(RequestCache::Force_cache) == 4 &&
|
||||||
int(RequestCache::EndGuard_) == 5,
|
int(RequestCache::Only_if_cached) == 5 &&
|
||||||
|
int(RequestCache::EndGuard_) == 6,
|
||||||
"RequestCache values are as expected");
|
"RequestCache values are as expected");
|
||||||
static_assert(int(RequestRedirect::Follow) == 0 &&
|
static_assert(int(RequestRedirect::Follow) == 0 &&
|
||||||
int(RequestRedirect::Error) == 1 &&
|
int(RequestRedirect::Error) == 1 &&
|
||||||
|
|
|
@ -172,7 +172,7 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
|
||||||
case WebGLExtensionID::EXT_frag_depth:
|
case WebGLExtensionID::EXT_frag_depth:
|
||||||
return WebGLExtensionFragDepth::IsSupported(this);
|
return WebGLExtensionFragDepth::IsSupported(this);
|
||||||
case WebGLExtensionID::EXT_shader_texture_lod:
|
case WebGLExtensionID::EXT_shader_texture_lod:
|
||||||
return gl->IsExtensionSupported(gl::GLContext::EXT_shader_texture_lod);
|
return gl->IsSupported(gl::GLFeature::shader_texture_lod);
|
||||||
case WebGLExtensionID::EXT_sRGB:
|
case WebGLExtensionID::EXT_sRGB:
|
||||||
return WebGLExtensionSRGB::IsSupported(this);
|
return WebGLExtensionSRGB::IsSupported(this);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
// iframe with the right preference setting so that the
|
// iframe with the right preference setting so that the
|
||||||
// createImageBitmap() will be visible.
|
// createImageBitmap() will be visible.
|
||||||
SpecialPowers.pushPrefEnv({'set': [
|
SpecialPowers.pushPrefEnv({'set': [
|
||||||
['canvas.imagebitmap_extensions.enabled', true]
|
['canvas.imagebitmap_extensions.enabled', true],
|
||||||
|
['gfx.ycbcr.accurate-conversion', true]
|
||||||
]}, function() {
|
]}, function() {
|
||||||
var div = document.getElementById("content");
|
var div = document.getElementById("content");
|
||||||
ok(div, "Parent exists");
|
ok(div, "Parent exists");
|
||||||
|
|
|
@ -21,7 +21,7 @@ fail-if = (os == 'android')
|
||||||
[ensure-exts/test_EXT_sRGB.html]
|
[ensure-exts/test_EXT_sRGB.html]
|
||||||
fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6') || (os == 'win')
|
fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6') || (os == 'win')
|
||||||
[ensure-exts/test_EXT_shader_texture_lod.html]
|
[ensure-exts/test_EXT_shader_texture_lod.html]
|
||||||
fail-if = (os == 'android') || (os == 'linux') || (os == 'mac')
|
fail-if = (os == 'android')
|
||||||
[ensure-exts/test_EXT_texture_filter_anisotropic.html]
|
[ensure-exts/test_EXT_texture_filter_anisotropic.html]
|
||||||
fail-if = (os == 'android') || (os == 'linux')
|
fail-if = (os == 'android') || (os == 'linux')
|
||||||
[ensure-exts/test_OES_standard_derivatives.html]
|
[ensure-exts/test_OES_standard_derivatives.html]
|
||||||
|
|
|
@ -267,10 +267,13 @@ TextComposition::DispatchCompositionEvent(
|
||||||
aCompositionEvent->mRanges = nullptr;
|
aCompositionEvent->mRanges = nullptr;
|
||||||
NS_ASSERTION(aCompositionEvent->mData.IsEmpty(),
|
NS_ASSERTION(aCompositionEvent->mData.IsEmpty(),
|
||||||
"mData of eCompositionCommitAsIs should be empty string");
|
"mData of eCompositionCommitAsIs should be empty string");
|
||||||
if (mLastData == IDEOGRAPHIC_SPACE) {
|
bool removePlaceholderCharacter =
|
||||||
// If the last data is an ideographic space (FullWidth space), it must be
|
Preferences::GetBool("intl.ime.remove_placeholder_character_at_commit",
|
||||||
|
false);
|
||||||
|
if (removePlaceholderCharacter && mLastData == IDEOGRAPHIC_SPACE) {
|
||||||
|
// If the last data is an ideographic space (FullWidth space), it might be
|
||||||
// a placeholder character of some Chinese IME. So, committing with
|
// a placeholder character of some Chinese IME. So, committing with
|
||||||
// this data must not be expected by users. Let's use empty string.
|
// this data might not be expected by users. Let's use empty string.
|
||||||
aCompositionEvent->mData.Truncate();
|
aCompositionEvent->mData.Truncate();
|
||||||
} else {
|
} else {
|
||||||
aCompositionEvent->mData = mLastData;
|
aCompositionEvent->mData = mLastData;
|
||||||
|
|
|
@ -428,6 +428,14 @@ Request::Constructor(const GlobalObject& aGlobal,
|
||||||
RequestCache cache = aInit.mCache.WasPassed() ?
|
RequestCache cache = aInit.mCache.WasPassed() ?
|
||||||
aInit.mCache.Value() : fallbackCache;
|
aInit.mCache.Value() : fallbackCache;
|
||||||
if (cache != RequestCache::EndGuard_) {
|
if (cache != RequestCache::EndGuard_) {
|
||||||
|
if (cache == RequestCache::Only_if_cached &&
|
||||||
|
request->Mode() != RequestMode::Same_origin) {
|
||||||
|
uint32_t t = static_cast<uint32_t>(request->Mode());
|
||||||
|
NS_ConvertASCIItoUTF16 modeString(RequestModeValues::strings[t].value,
|
||||||
|
RequestModeValues::strings[t].length);
|
||||||
|
aRv.ThrowTypeError<MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN>(modeString);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
request->ClearCreatedByFetchEvent();
|
request->ClearCreatedByFetchEvent();
|
||||||
request->SetCacheMode(cache);
|
request->SetCacheMode(cache);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,7 @@ class nsGenericHTMLElement : public nsGenericHTMLElementBase,
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit nsGenericHTMLElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
explicit nsGenericHTMLElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||||
: nsGenericHTMLElementBase(aNodeInfo),
|
: nsGenericHTMLElementBase(aNodeInfo)
|
||||||
mScrollgrab(false)
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mNodeInfo->NamespaceID() == kNameSpaceID_XHTML,
|
NS_ASSERTION(mNodeInfo->NamespaceID() == kNameSpaceID_XHTML,
|
||||||
"Unexpected namespace");
|
"Unexpected namespace");
|
||||||
|
@ -194,11 +193,15 @@ public:
|
||||||
}
|
}
|
||||||
bool Scrollgrab() const
|
bool Scrollgrab() const
|
||||||
{
|
{
|
||||||
return mScrollgrab;
|
return HasFlag(ELEMENT_HAS_SCROLLGRAB);
|
||||||
}
|
}
|
||||||
void SetScrollgrab(bool aValue)
|
void SetScrollgrab(bool aValue)
|
||||||
{
|
{
|
||||||
mScrollgrab = aValue;
|
if (aValue) {
|
||||||
|
SetFlags(ELEMENT_HAS_SCROLLGRAB);
|
||||||
|
} else {
|
||||||
|
UnsetFlags(ELEMENT_HAS_SCROLLGRAB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetInnerText(mozilla::dom::DOMString& aValue, mozilla::ErrorResult& aError);
|
void GetInnerText(mozilla::dom::DOMString& aValue, mozilla::ErrorResult& aError);
|
||||||
|
@ -1167,8 +1170,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ChangeEditableState(int32_t aChange);
|
void ChangeEditableState(int32_t aChange);
|
||||||
|
|
||||||
bool mScrollgrab;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
|
@ -1037,18 +1037,19 @@ ContentChild::InitXPCOM()
|
||||||
if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
|
if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
|
||||||
NS_WARNING("Couldn't register console listener for child process");
|
NS_WARNING("Couldn't register console listener for child process");
|
||||||
|
|
||||||
bool isOffline, isLangRTL;
|
bool isOffline, isLangRTL, haveBidiKeyboards;
|
||||||
bool isConnected;
|
bool isConnected;
|
||||||
ClipboardCapabilities clipboardCaps;
|
ClipboardCapabilities clipboardCaps;
|
||||||
DomainPolicyClone domainPolicy;
|
DomainPolicyClone domainPolicy;
|
||||||
StructuredCloneData initialData;
|
StructuredCloneData initialData;
|
||||||
|
|
||||||
SendGetXPCOMProcessAttributes(&isOffline, &isConnected,
|
SendGetXPCOMProcessAttributes(&isOffline, &isConnected,
|
||||||
&isLangRTL, &mAvailableDictionaries,
|
&isLangRTL, &haveBidiKeyboards,
|
||||||
|
&mAvailableDictionaries,
|
||||||
&clipboardCaps, &domainPolicy, &initialData);
|
&clipboardCaps, &domainPolicy, &initialData);
|
||||||
RecvSetOffline(isOffline);
|
RecvSetOffline(isOffline);
|
||||||
RecvSetConnectivity(isConnected);
|
RecvSetConnectivity(isConnected);
|
||||||
RecvBidiKeyboardNotify(isLangRTL);
|
RecvBidiKeyboardNotify(isLangRTL, haveBidiKeyboards);
|
||||||
|
|
||||||
// Create the CPOW manager as soon as possible.
|
// Create the CPOW manager as soon as possible.
|
||||||
SendPJavaScriptConstructor();
|
SendPJavaScriptConstructor();
|
||||||
|
@ -1511,13 +1512,14 @@ ContentChild::RecvSpeakerManagerNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL)
|
ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL,
|
||||||
|
const bool& aHaveBidiKeyboards)
|
||||||
{
|
{
|
||||||
// bidi is always of type PuppetBidiKeyboard* (because in the child, the only
|
// bidi is always of type PuppetBidiKeyboard* (because in the child, the only
|
||||||
// possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
|
// possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
|
||||||
PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
|
PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
|
||||||
if (bidi) {
|
if (bidi) {
|
||||||
bidi->SetIsLangRTL(aIsLangRTL);
|
bidi->SetBidiKeyboardInfo(aIsLangRTL, aHaveBidiKeyboards);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3075,6 +3077,24 @@ ContentChild::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure)
|
||||||
bool
|
bool
|
||||||
ContentChild::RecvShutdown()
|
ContentChild::RecvShutdown()
|
||||||
{
|
{
|
||||||
|
// If we receive the shutdown message from within a nested event loop, we want
|
||||||
|
// to wait for that event loop to finish. Otherwise we could prematurely
|
||||||
|
// terminate an "unload" or "pagehide" event handler (which might be doing a
|
||||||
|
// sync XHR, for example).
|
||||||
|
nsCOMPtr<nsIThread> thread;
|
||||||
|
nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
|
||||||
|
if (NS_SUCCEEDED(rv) && thread) {
|
||||||
|
RefPtr<nsThread> mainThread(thread.forget().downcast<nsThread>());
|
||||||
|
if (mainThread->RecursionDepth() > 1) {
|
||||||
|
// We're in a nested event loop. Let's delay for an arbitrary period of
|
||||||
|
// time (100ms) in the hopes that the event loop will have finished by
|
||||||
|
// then.
|
||||||
|
MessageLoop::current()->PostDelayedTask(
|
||||||
|
NewRunnableMethod(this, &ContentChild::RecvShutdown), 100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mPolicy) {
|
if (mPolicy) {
|
||||||
mPolicy->Deactivate();
|
mPolicy->Deactivate();
|
||||||
mPolicy = nullptr;
|
mPolicy = nullptr;
|
||||||
|
|
|
@ -401,7 +401,8 @@ public:
|
||||||
|
|
||||||
virtual bool RecvSpeakerManagerNotify() override;
|
virtual bool RecvSpeakerManagerNotify() override;
|
||||||
|
|
||||||
virtual bool RecvBidiKeyboardNotify(const bool& isLangRTL) override;
|
virtual bool RecvBidiKeyboardNotify(const bool& isLangRTL,
|
||||||
|
const bool& haveBidiKeyboards) override;
|
||||||
|
|
||||||
virtual bool RecvNotifyVisited(const URIParams& aURI) override;
|
virtual bool RecvNotifyVisited(const URIParams& aURI) override;
|
||||||
|
|
||||||
|
|
|
@ -3388,6 +3388,7 @@ bool
|
||||||
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||||
bool* aIsConnected,
|
bool* aIsConnected,
|
||||||
bool* aIsLangRTL,
|
bool* aIsLangRTL,
|
||||||
|
bool* aHaveBidiKeyboards,
|
||||||
InfallibleTArray<nsString>* dictionaries,
|
InfallibleTArray<nsString>* dictionaries,
|
||||||
ClipboardCapabilities* clipboardCaps,
|
ClipboardCapabilities* clipboardCaps,
|
||||||
DomainPolicyClone* domainPolicy,
|
DomainPolicyClone* domainPolicy,
|
||||||
|
@ -3404,8 +3405,10 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||||
nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
|
nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
|
||||||
|
|
||||||
*aIsLangRTL = false;
|
*aIsLangRTL = false;
|
||||||
|
*aHaveBidiKeyboards = false;
|
||||||
if (bidi) {
|
if (bidi) {
|
||||||
bidi->IsLangRTL(aIsLangRTL);
|
bidi->IsLangRTL(aIsLangRTL);
|
||||||
|
bidi->GetHaveBidiKeyboards(aHaveBidiKeyboards);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
|
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
|
||||||
|
|
|
@ -707,6 +707,7 @@ private:
|
||||||
RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||||
bool* aIsConnected,
|
bool* aIsConnected,
|
||||||
bool* aIsLangRTL,
|
bool* aIsLangRTL,
|
||||||
|
bool* aHaveBidiKeyboards,
|
||||||
InfallibleTArray<nsString>* dictionaries,
|
InfallibleTArray<nsString>* dictionaries,
|
||||||
ClipboardCapabilities* clipboardCaps,
|
ClipboardCapabilities* clipboardCaps,
|
||||||
DomainPolicyClone* domainPolicy,
|
DomainPolicyClone* domainPolicy,
|
||||||
|
|
|
@ -494,7 +494,7 @@ child:
|
||||||
* Communication between the PuppetBidiKeyboard and the actual
|
* Communication between the PuppetBidiKeyboard and the actual
|
||||||
* BidiKeyboard hosted by the parent
|
* BidiKeyboard hosted by the parent
|
||||||
*/
|
*/
|
||||||
async BidiKeyboardNotify(bool isLangRTL);
|
async BidiKeyboardNotify(bool isLangRTL, bool haveBidiKeyboards);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump this process's GC and CC logs to the provided files.
|
* Dump this process's GC and CC logs to the provided files.
|
||||||
|
@ -702,7 +702,8 @@ parent:
|
||||||
sync GetProcessAttributes()
|
sync GetProcessAttributes()
|
||||||
returns (ContentParentId cpId, bool isForApp, bool isForBrowser);
|
returns (ContentParentId cpId, bool isForApp, bool isForBrowser);
|
||||||
sync GetXPCOMProcessAttributes()
|
sync GetXPCOMProcessAttributes()
|
||||||
returns (bool isOffline, bool isConnected, bool isLangRTL, nsString[] dictionaries,
|
returns (bool isOffline, bool isConnected, bool isLangRTL,
|
||||||
|
bool haveBidiKeyboards, nsString[] dictionaries,
|
||||||
ClipboardCapabilities clipboardCaps,
|
ClipboardCapabilities clipboardCaps,
|
||||||
DomainPolicyClone domainPolicy,
|
DomainPolicyClone domainPolicy,
|
||||||
StructuredCloneData initialData);
|
StructuredCloneData initialData);
|
||||||
|
|
|
@ -340,6 +340,27 @@ TabParent::CacheFrameLoader(nsFrameLoader* aFrameLoader)
|
||||||
mFrameLoader = aFrameLoader;
|
mFrameLoader = aFrameLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will return nullptr if there is no outer window available for the
|
||||||
|
* document hosting the owner element of this TabParent. Also will return
|
||||||
|
* nullptr if that outer window is in the process of closing.
|
||||||
|
*/
|
||||||
|
already_AddRefed<nsPIDOMWindowOuter>
|
||||||
|
TabParent::GetParentWindowOuter()
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIContent> frame = do_QueryInterface(GetOwnerElement());
|
||||||
|
if (!frame) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsPIDOMWindowOuter> parent = frame->OwnerDoc()->GetWindow();
|
||||||
|
if (!parent || parent->Closed()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent.forget();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TabParent::SetOwnerElement(Element* aElement)
|
TabParent::SetOwnerElement(Element* aElement)
|
||||||
{
|
{
|
||||||
|
|
|
@ -143,6 +143,7 @@ public:
|
||||||
uint32_t aChromeFlags);
|
uint32_t aChromeFlags);
|
||||||
|
|
||||||
Element* GetOwnerElement() const { return mFrameElement; }
|
Element* GetOwnerElement() const { return mFrameElement; }
|
||||||
|
already_AddRefed<nsPIDOMWindowOuter> GetParentWindowOuter();
|
||||||
|
|
||||||
void SetOwnerElement(Element* aElement);
|
void SetOwnerElement(Element* aElement);
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
#include "MediaResource.h"
|
#include "MediaResource.h"
|
||||||
#include "VideoUtils.h"
|
#include "VideoUtils.h"
|
||||||
#include "ImageContainer.h"
|
#include "ImageContainer.h"
|
||||||
|
#include "MediaPrefs.h"
|
||||||
|
|
||||||
#include "nsPrintfCString.h"
|
#include "nsPrintfCString.h"
|
||||||
#include "mozilla/mozalloc.h"
|
#include "mozilla/mozalloc.h"
|
||||||
|
#include "mozilla/Mutex.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
using namespace mozilla::media;
|
using namespace mozilla::media;
|
||||||
|
|
||||||
|
@ -62,6 +65,154 @@ public:
|
||||||
size_t mSize;
|
size_t mSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The ReaderQueue is used to keep track of the numer of active readers to
|
||||||
|
// enforce a given limit on the number of simultaneous active decoders.
|
||||||
|
// Readers are added/removed during construction/destruction and are
|
||||||
|
// suspended and resumed by the queue. The max number of active decoders is
|
||||||
|
// controlled by the "media.decoder.limit" pref.
|
||||||
|
class ReaderQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static ReaderQueue& Instance()
|
||||||
|
{
|
||||||
|
static StaticMutex sMutex;
|
||||||
|
StaticMutexAutoLock lock(sMutex);
|
||||||
|
|
||||||
|
if (!sInstance) {
|
||||||
|
sInstance = new ReaderQueue;
|
||||||
|
sInstance->MaxNumActive(MediaPrefs::MediaDecoderLimit());
|
||||||
|
ClearOnShutdown(&sInstance);
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(sInstance);
|
||||||
|
return *sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaxNumActive(int32_t aNumActive)
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
if (aNumActive < 0) {
|
||||||
|
mNumMaxActive = std::numeric_limits<uint32_t>::max();
|
||||||
|
} else {
|
||||||
|
mNumMaxActive = aNumActive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add(MediaDecoderReader* aReader)
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
if (mActive.Length() < mNumMaxActive) {
|
||||||
|
// Below active limit, resume the new reader.
|
||||||
|
mActive.AppendElement(aReader);
|
||||||
|
DispatchResume(aReader);
|
||||||
|
} else if (mActive.IsEmpty()) {
|
||||||
|
MOZ_ASSERT(mNumMaxActive == 0);
|
||||||
|
mSuspended.AppendElement(aReader);
|
||||||
|
} else {
|
||||||
|
// We're past the active limit, suspend an old reader and resume the new.
|
||||||
|
mActive.AppendElement(aReader);
|
||||||
|
MediaDecoderReader* suspendReader = mActive.ElementAt(0);
|
||||||
|
mSuspended.AppendElement(suspendReader);
|
||||||
|
mActive.RemoveElementAt(0);
|
||||||
|
DispatchSuspendResume(suspendReader, aReader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Remove(MediaDecoderReader* aReader)
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
if (aReader->IsSuspended()) {
|
||||||
|
// Removing suspended readers has no immediate side-effects.
|
||||||
|
DebugOnly<bool> result = mSuspended.RemoveElement(aReader);
|
||||||
|
MOZ_ASSERT(result, "Suspended reader must be in mSuspended");
|
||||||
|
} else {
|
||||||
|
// For each removed active reader, we resume a suspended one.
|
||||||
|
DebugOnly<bool> result = mActive.RemoveElement(aReader);
|
||||||
|
MOZ_ASSERT(result, "Non-suspended reader must be in mActive");
|
||||||
|
if (mSuspended.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MediaDecoderReader* resumeReader = mSuspended.LastElement();
|
||||||
|
mActive.AppendElement(resumeReader);
|
||||||
|
mSuspended.RemoveElementAt(mSuspended.Length() - 1);
|
||||||
|
DispatchResume(resumeReader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ReaderQueue()
|
||||||
|
: mNumMaxActive(std::numeric_limits<uint32_t>::max())
|
||||||
|
, mMutex("ReaderQueue:mMutex")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Resume(MediaDecoderReader* aReader)
|
||||||
|
{
|
||||||
|
if (!aReader->IsSuspended()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aReader->SetIsSuspended(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Suspend(MediaDecoderReader* aReader)
|
||||||
|
{
|
||||||
|
if (aReader->IsSuspended()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aReader->SetIsSuspended(true);
|
||||||
|
|
||||||
|
aReader->ReleaseMediaResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DispatchResume(MediaDecoderReader* aReader)
|
||||||
|
{
|
||||||
|
RefPtr<MediaDecoderReader> reader = aReader;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
|
||||||
|
[reader]() {
|
||||||
|
Resume(reader);
|
||||||
|
});
|
||||||
|
reader->OwnerThread()->Dispatch(task.forget());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DispatchSuspend(MediaDecoderReader* aReader)
|
||||||
|
{
|
||||||
|
RefPtr<MediaDecoderReader> reader = aReader;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
|
||||||
|
[reader]() {
|
||||||
|
Suspend(reader);
|
||||||
|
});
|
||||||
|
reader->OwnerThread()->Dispatch(task.forget());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DispatchSuspendResume(MediaDecoderReader* aSuspend,
|
||||||
|
MediaDecoderReader* aResume)
|
||||||
|
{
|
||||||
|
RefPtr<MediaDecoderReader> suspend = aSuspend;
|
||||||
|
RefPtr<MediaDecoderReader> resume = aResume;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
|
||||||
|
[suspend, resume] () {
|
||||||
|
Suspend(suspend);
|
||||||
|
DispatchResume(resume);
|
||||||
|
});
|
||||||
|
suspend->OwnerThread()->Dispatch(task.forget());
|
||||||
|
}
|
||||||
|
|
||||||
|
static StaticAutoPtr<ReaderQueue> sInstance;
|
||||||
|
|
||||||
|
nsTArray<RefPtr<MediaDecoderReader>> mActive;
|
||||||
|
nsTArray<RefPtr<MediaDecoderReader>> mSuspended;
|
||||||
|
uint32_t mNumMaxActive;
|
||||||
|
|
||||||
|
mutable Mutex mMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticAutoPtr<ReaderQueue> ReaderQueue::sInstance;
|
||||||
|
|
||||||
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
||||||
: mAudioCompactor(mAudioQueue)
|
: mAudioCompactor(mAudioQueue)
|
||||||
, mDecoder(aDecoder)
|
, mDecoder(aDecoder)
|
||||||
|
@ -75,6 +226,8 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
||||||
, mShutdown(false)
|
, mShutdown(false)
|
||||||
, mAudioDiscontinuity(false)
|
, mAudioDiscontinuity(false)
|
||||||
, mVideoDiscontinuity(false)
|
, mVideoDiscontinuity(false)
|
||||||
|
, mIsSuspended(mTaskQueue, true,
|
||||||
|
"MediaDecoderReader::mIsSuspended (Canonical)")
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(MediaDecoderReader);
|
MOZ_COUNT_CTOR(MediaDecoderReader);
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
@ -84,6 +237,8 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
||||||
mTaskQueue, this, &MediaDecoderReader::NotifyDataArrived);
|
mTaskQueue, this, &MediaDecoderReader::NotifyDataArrived);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReaderQueue::Instance().Add(this);
|
||||||
|
|
||||||
// Dispatch initialization that needs to happen on that task queue.
|
// Dispatch initialization that needs to happen on that task queue.
|
||||||
mTaskQueue->Dispatch(NewRunnableMethod(this, &MediaDecoderReader::InitializationTask));
|
mTaskQueue->Dispatch(NewRunnableMethod(this, &MediaDecoderReader::InitializationTask));
|
||||||
}
|
}
|
||||||
|
@ -368,6 +523,7 @@ MediaDecoderReader::Shutdown()
|
||||||
ReleaseMediaResources();
|
ReleaseMediaResources();
|
||||||
mDuration.DisconnectIfConnected();
|
mDuration.DisconnectIfConnected();
|
||||||
mBuffered.DisconnectAll();
|
mBuffered.DisconnectAll();
|
||||||
|
mIsSuspended.DisconnectAll();
|
||||||
|
|
||||||
// Shut down the watch manager before shutting down our task queue.
|
// Shut down the watch manager before shutting down our task queue.
|
||||||
mWatchManager.Shutdown();
|
mWatchManager.Shutdown();
|
||||||
|
@ -376,6 +532,8 @@ MediaDecoderReader::Shutdown()
|
||||||
|
|
||||||
mDecoder = nullptr;
|
mDecoder = nullptr;
|
||||||
|
|
||||||
|
ReaderQueue::Instance().Remove(this);
|
||||||
|
|
||||||
return mTaskQueue->BeginShutdown();
|
return mTaskQueue->BeginShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,22 @@ public:
|
||||||
// Notified by the OggReader during playback when chained ogg is detected.
|
// Notified by the OggReader during playback when chained ogg is detected.
|
||||||
MediaEventSource<void>& OnMediaNotSeekable() { return mOnMediaNotSeekable; }
|
MediaEventSource<void>& OnMediaNotSeekable() { return mOnMediaNotSeekable; }
|
||||||
|
|
||||||
|
bool IsSuspended() const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
|
return mIsSuspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIsSuspended(bool aState)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
|
mIsSuspended = aState;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractCanonical<bool>* CanonicalIsSuspended() {
|
||||||
|
return &mIsSuspended;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~MediaDecoderReader();
|
virtual ~MediaDecoderReader();
|
||||||
|
|
||||||
|
@ -435,6 +451,7 @@ private:
|
||||||
// "discontinuity" in the stream. For example after a seek.
|
// "discontinuity" in the stream. For example after a seek.
|
||||||
bool mAudioDiscontinuity;
|
bool mAudioDiscontinuity;
|
||||||
bool mVideoDiscontinuity;
|
bool mVideoDiscontinuity;
|
||||||
|
Canonical<bool> mIsSuspended;
|
||||||
|
|
||||||
MediaEventListener mDataArrivedListener;
|
MediaEventListener mDataArrivedListener;
|
||||||
};
|
};
|
||||||
|
|
|
@ -415,6 +415,9 @@ public:
|
||||||
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() {
|
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() {
|
||||||
return mReader->CanonicalBuffered();
|
return mReader->CanonicalBuffered();
|
||||||
}
|
}
|
||||||
|
AbstractCanonical<bool>* CanonicalIsSuspended() {
|
||||||
|
return mReader->CanonicalIsSuspended();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MOZ_EME
|
#ifdef MOZ_EME
|
||||||
void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); }
|
void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); }
|
||||||
|
|
|
@ -262,6 +262,8 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||||
mAudioOffloading(false),
|
mAudioOffloading(false),
|
||||||
mBuffered(mTaskQueue, TimeIntervals(),
|
mBuffered(mTaskQueue, TimeIntervals(),
|
||||||
"MediaDecoderStateMachine::mBuffered (Mirror)"),
|
"MediaDecoderStateMachine::mBuffered (Mirror)"),
|
||||||
|
mIsReaderSuspended(mTaskQueue, true,
|
||||||
|
"MediaDecoderStateMachine::mIsReaderSuspended (Mirror)"),
|
||||||
mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
|
mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
|
||||||
"MediaDecoderStateMachine::mEstimatedDuration (Mirror)"),
|
"MediaDecoderStateMachine::mEstimatedDuration (Mirror)"),
|
||||||
mExplicitDuration(mTaskQueue, Maybe<double>(),
|
mExplicitDuration(mTaskQueue, Maybe<double>(),
|
||||||
|
@ -339,6 +341,7 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
|
||||||
|
|
||||||
// Connect mirrors.
|
// Connect mirrors.
|
||||||
mBuffered.Connect(mReader->CanonicalBuffered());
|
mBuffered.Connect(mReader->CanonicalBuffered());
|
||||||
|
mIsReaderSuspended.Connect(mReader->CanonicalIsSuspended());
|
||||||
mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration());
|
mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration());
|
||||||
mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
|
mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
|
||||||
mPlayState.Connect(aDecoder->CanonicalPlayState());
|
mPlayState.Connect(aDecoder->CanonicalPlayState());
|
||||||
|
@ -357,6 +360,7 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
|
||||||
|
|
||||||
// Initialize watchers.
|
// Initialize watchers.
|
||||||
mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
|
mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
|
||||||
|
mWatchManager.Watch(mIsReaderSuspended, &MediaDecoderStateMachine::ReaderSuspendedChanged);
|
||||||
mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||||
mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||||
mWatchManager.Watch(mVideoCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
mWatchManager.Watch(mVideoCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||||
|
@ -1342,8 +1346,9 @@ void MediaDecoderStateMachine::PlayStateChanged()
|
||||||
void MediaDecoderStateMachine::VisibilityChanged()
|
void MediaDecoderStateMachine::VisibilityChanged()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
DECODER_LOG("VisibilityChanged: is visible = %d, video decode suspended = %d",
|
DECODER_LOG("VisibilityChanged: is visible = %d, video decode suspended = %d, "
|
||||||
mIsVisible.Ref(), mVideoDecodeSuspended);
|
"reader suspended = %d",
|
||||||
|
mIsVisible.Ref(), mVideoDecodeSuspended, mIsReaderSuspended.Ref());
|
||||||
|
|
||||||
// Not suspending background videos so there's nothing to do.
|
// Not suspending background videos so there's nothing to do.
|
||||||
if (!MediaPrefs::MDSMSuspendBackgroundVideoEnabled()) {
|
if (!MediaPrefs::MDSMSuspendBackgroundVideoEnabled()) {
|
||||||
|
@ -1378,6 +1383,10 @@ void MediaDecoderStateMachine::VisibilityChanged()
|
||||||
if (mVideoDecodeSuspended) {
|
if (mVideoDecodeSuspended) {
|
||||||
mVideoDecodeSuspended = false;
|
mVideoDecodeSuspended = false;
|
||||||
|
|
||||||
|
if (mIsReaderSuspended) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If an existing seek is in flight don't bother creating a new
|
// If an existing seek is in flight don't bother creating a new
|
||||||
// one to catch up.
|
// one to catch up.
|
||||||
if (mSeekTask || mQueuedSeek.Exists()) {
|
if (mSeekTask || mQueuedSeek.Exists()) {
|
||||||
|
@ -1385,23 +1394,31 @@ void MediaDecoderStateMachine::VisibilityChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start video-only seek to the current time...
|
// Start video-only seek to the current time...
|
||||||
InitiateVideoDecodeRecoverySeek();
|
InitiateDecodeRecoverySeek(TrackSet(TrackInfo::kVideoTrack));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitiateVideoDecodeRecoverySeek is responsible for setting up a video-only
|
// InitiateDecodeRecoverySeek is responsible for setting up a seek using the
|
||||||
// seek using the seek task. When suspension of decoding for videos that are in
|
// seek task for the following situations:
|
||||||
|
// 1. When suspension of decoding for videos that are in
|
||||||
// background tabs (ie. invisible) is enabled, the audio keeps playing and when
|
// background tabs (ie. invisible) is enabled, the audio keeps playing and when
|
||||||
// switching back to decoding video, it is highly desirable to not cause the
|
// switching back to decoding video, it is highly desirable to not cause the
|
||||||
// audio to pause as the video is seeked else there be a noticeable audio glitch
|
// audio to pause as the video is seeked else there be a noticeable audio glitch
|
||||||
// as the tab becomes visible.
|
// as the tab becomes visible.
|
||||||
void MediaDecoderStateMachine::InitiateVideoDecodeRecoverySeek()
|
// 2. When there is a decoder limit set, suspended videos may be resumed and
|
||||||
|
// require the seek to recover the original seek position for both audio and
|
||||||
|
// video.
|
||||||
|
void MediaDecoderStateMachine::InitiateDecodeRecoverySeek(TrackSet aTracks)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
|
|
||||||
|
DECODER_LOG("InitiateDecodeRecoverySeek");
|
||||||
|
|
||||||
SeekJob seekJob;
|
SeekJob seekJob;
|
||||||
seekJob.mTarget = SeekTarget(GetMediaTime(),
|
SeekTarget::Type seekTargetType = aTracks.contains(TrackInfo::kAudioTrack)
|
||||||
SeekTarget::Type::AccurateVideoOnly,
|
? SeekTarget::Type::Accurate
|
||||||
|
: SeekTarget::Type::AccurateVideoOnly;
|
||||||
|
seekJob.mTarget = SeekTarget(GetMediaTime(), seekTargetType,
|
||||||
MediaDecoderEventVisibility::Suppressed);
|
MediaDecoderEventVisibility::Suppressed);
|
||||||
|
|
||||||
SetState(DECODER_STATE_SEEKING);
|
SetState(DECODER_STATE_SEEKING);
|
||||||
|
@ -1423,7 +1440,7 @@ void MediaDecoderStateMachine::InitiateVideoDecodeRecoverySeek()
|
||||||
|
|
||||||
// Reset our state machine and decoding pipeline before seeking.
|
// Reset our state machine and decoding pipeline before seeking.
|
||||||
if (mSeekTask->NeedToResetMDSM()) {
|
if (mSeekTask->NeedToResetMDSM()) {
|
||||||
Reset(TrackInfo::kVideoTrack);
|
Reset(aTracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the seek.
|
// Do the seek.
|
||||||
|
@ -1456,6 +1473,19 @@ void MediaDecoderStateMachine::BufferedRangeUpdated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MediaDecoderStateMachine::ReaderSuspendedChanged()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
|
DECODER_LOG("ReaderSuspendedChanged: suspended = %d", mIsReaderSuspended.Ref());
|
||||||
|
|
||||||
|
if (!HasVideo() || mIsReaderSuspended || IsDecodingFirstFrame()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitiateDecodeRecoverySeek(TrackSet(TrackInfo::kAudioTrack,
|
||||||
|
TrackInfo::kVideoTrack));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaDecoderStateMachine::ReadMetadata()
|
MediaDecoderStateMachine::ReadMetadata()
|
||||||
{
|
{
|
||||||
|
@ -2222,6 +2252,7 @@ MediaDecoderStateMachine::FinishShutdown()
|
||||||
|
|
||||||
// Disconnect canonicals and mirrors before shutting down our task queue.
|
// Disconnect canonicals and mirrors before shutting down our task queue.
|
||||||
mBuffered.DisconnectIfConnected();
|
mBuffered.DisconnectIfConnected();
|
||||||
|
mIsReaderSuspended.DisconnectIfConnected();
|
||||||
mEstimatedDuration.DisconnectIfConnected();
|
mEstimatedDuration.DisconnectIfConnected();
|
||||||
mExplicitDuration.DisconnectIfConnected();
|
mExplicitDuration.DisconnectIfConnected();
|
||||||
mPlayState.DisconnectIfConnected();
|
mPlayState.DisconnectIfConnected();
|
||||||
|
@ -2636,7 +2667,8 @@ bool MediaDecoderStateMachine::IsStateMachineScheduled() const
|
||||||
bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const
|
bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
return MediaPrefs::MDSMSuspendBackgroundVideoEnabled() && mVideoDecodeSuspended;
|
return (MediaPrefs::MDSMSuspendBackgroundVideoEnabled() && mVideoDecodeSuspended) ||
|
||||||
|
mIsReaderSuspended;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -381,6 +381,8 @@ protected:
|
||||||
|
|
||||||
void BufferedRangeUpdated();
|
void BufferedRangeUpdated();
|
||||||
|
|
||||||
|
void ReaderSuspendedChanged();
|
||||||
|
|
||||||
// Inserts MediaData* samples into their respective MediaQueues.
|
// Inserts MediaData* samples into their respective MediaQueues.
|
||||||
// aSample must not be null.
|
// aSample must not be null.
|
||||||
|
|
||||||
|
@ -518,10 +520,11 @@ protected:
|
||||||
// The decoder monitor must be held.
|
// The decoder monitor must be held.
|
||||||
void InitiateSeek(SeekJob aSeekJob);
|
void InitiateSeek(SeekJob aSeekJob);
|
||||||
|
|
||||||
// Clears any previous seeking state and initiates a video-only seek on the
|
// Clears any previous seeking state and initiates a seek on the decoder to
|
||||||
// decoder to catch up the video to the current audio position, when recovering
|
// resync the video and audio positions, when recovering from video decoding
|
||||||
// from video decoding being suspended in background.
|
// being suspended in background or from audio and video decoding being
|
||||||
void InitiateVideoDecodeRecoverySeek();
|
// suspended due to the decoder limit.
|
||||||
|
void InitiateDecodeRecoverySeek(TrackSet aTracks);
|
||||||
|
|
||||||
nsresult DispatchAudioDecodeTaskIfNeeded();
|
nsresult DispatchAudioDecodeTaskIfNeeded();
|
||||||
|
|
||||||
|
@ -975,6 +978,8 @@ private:
|
||||||
// The buffered range. Mirrored from the decoder thread.
|
// The buffered range. Mirrored from the decoder thread.
|
||||||
Mirror<media::TimeIntervals> mBuffered;
|
Mirror<media::TimeIntervals> mBuffered;
|
||||||
|
|
||||||
|
Mirror<bool> mIsReaderSuspended;
|
||||||
|
|
||||||
// The duration according to the demuxer's current estimate, mirrored from the main thread.
|
// The duration according to the demuxer's current estimate, mirrored from the main thread.
|
||||||
Mirror<media::NullableTimeUnit> mEstimatedDuration;
|
Mirror<media::NullableTimeUnit> mEstimatedDuration;
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,6 @@ MediaFormatReader::Shutdown()
|
||||||
MOZ_ASSERT(!mVideo.HasPromise());
|
MOZ_ASSERT(!mVideo.HasPromise());
|
||||||
|
|
||||||
mDemuxer = nullptr;
|
mDemuxer = nullptr;
|
||||||
|
|
||||||
mPlatform = nullptr;
|
mPlatform = nullptr;
|
||||||
|
|
||||||
return MediaDecoderReader::Shutdown();
|
return MediaDecoderReader::Shutdown();
|
||||||
|
@ -459,6 +458,11 @@ MediaFormatReader::EnsureDecoderInitialized(TrackType aTrack)
|
||||||
[self] (TrackType aTrack) {
|
[self] (TrackType aTrack) {
|
||||||
auto& decoder = self->GetDecoderData(aTrack);
|
auto& decoder = self->GetDecoderData(aTrack);
|
||||||
decoder.mInitPromise.Complete();
|
decoder.mInitPromise.Complete();
|
||||||
|
|
||||||
|
if (self->IsSuspended()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
decoder.mDecoderInitialized = true;
|
decoder.mDecoderInitialized = true;
|
||||||
MonitorAutoLock mon(decoder.mMonitor);
|
MonitorAutoLock mon(decoder.mMonitor);
|
||||||
decoder.mDescription = decoder.mDecoder->GetDescriptionName();
|
decoder.mDescription = decoder.mDecoder->GetDescriptionName();
|
||||||
|
@ -533,6 +537,10 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||||
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSuspended()) {
|
||||||
|
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
media::TimeUnit timeThreshold{media::TimeUnit::FromMicroseconds(aTimeThreshold)};
|
media::TimeUnit timeThreshold{media::TimeUnit::FromMicroseconds(aTimeThreshold)};
|
||||||
// Ensure we have no pending seek going as ShouldSkip could return out of date
|
// Ensure we have no pending seek going as ShouldSkip could return out of date
|
||||||
// information.
|
// information.
|
||||||
|
@ -624,6 +632,10 @@ MediaFormatReader::RequestAudioData()
|
||||||
return MediaDataPromise::CreateAndReject(DECODE_ERROR, __func__);
|
return MediaDataPromise::CreateAndReject(DECODE_ERROR, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSuspended()) {
|
||||||
|
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
||||||
|
}
|
||||||
|
|
||||||
if (IsSeeking()) {
|
if (IsSeeking()) {
|
||||||
LOG("called mid-seek. Rejecting.");
|
LOG("called mid-seek. Rejecting.");
|
||||||
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
||||||
|
@ -918,6 +930,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
|
||||||
AbstractMediaDecoder::AutoNotifyDecoded& aA)
|
AbstractMediaDecoder::AutoNotifyDecoded& aA)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
|
|
||||||
auto& decoder = GetDecoderData(aTrack);
|
auto& decoder = GetDecoderData(aTrack);
|
||||||
|
|
||||||
if (decoder.mQueuedSamples.IsEmpty()) {
|
if (decoder.mQueuedSamples.IsEmpty()) {
|
||||||
|
@ -1884,6 +1897,9 @@ void MediaFormatReader::ReleaseMediaResources()
|
||||||
}
|
}
|
||||||
mVideo.mInitPromise.DisconnectIfExists();
|
mVideo.mInitPromise.DisconnectIfExists();
|
||||||
mVideo.ShutdownDecoder();
|
mVideo.ShutdownDecoder();
|
||||||
|
|
||||||
|
mAudio.mInitPromise.DisconnectIfExists();
|
||||||
|
mAudio.ShutdownDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -101,6 +101,7 @@ public:
|
||||||
void GetMozDebugReaderData(nsAString& aString);
|
void GetMozDebugReaderData(nsAString& aString);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool HasVideo() { return mVideo.mTrackDemuxer; }
|
bool HasVideo() { return mVideo.mTrackDemuxer; }
|
||||||
bool HasAudio() { return mAudio.mTrackDemuxer; }
|
bool HasAudio() { return mAudio.mTrackDemuxer; }
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,7 @@ private:
|
||||||
DECL_MEDIA_PREF("media.webspeech.recognition.force_enable", WebSpeechRecognitionForceEnabled, bool, false);
|
DECL_MEDIA_PREF("media.webspeech.recognition.force_enable", WebSpeechRecognitionForceEnabled, bool, false);
|
||||||
|
|
||||||
DECL_MEDIA_PREF("media.num-decode-threads", MediaThreadPoolDefaultCount, uint32_t, 4);
|
DECL_MEDIA_PREF("media.num-decode-threads", MediaThreadPoolDefaultCount, uint32_t, 4);
|
||||||
|
DECL_MEDIA_PREF("media.decoder.limit", MediaDecoderLimit, uint32_t, -1);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Manage the singleton:
|
// Manage the singleton:
|
||||||
|
|
|
@ -59,6 +59,6 @@ enum RequestContext {
|
||||||
|
|
||||||
enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
|
enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
|
||||||
enum RequestCredentials { "omit", "same-origin", "include" };
|
enum RequestCredentials { "omit", "same-origin", "include" };
|
||||||
enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache" };
|
enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" };
|
||||||
enum RequestRedirect { "follow", "error", "manual" };
|
enum RequestRedirect { "follow", "error", "manual" };
|
||||||
enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" };
|
enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" };
|
||||||
|
|
|
@ -126,7 +126,9 @@ static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE == static_cast<u
|
||||||
"RequestCache enumeration value should match Necko Cache mode value.");
|
"RequestCache enumeration value should match Necko Cache mode value.");
|
||||||
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == static_cast<uint32_t>(RequestCache::Force_cache),
|
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == static_cast<uint32_t>(RequestCache::Force_cache),
|
||||||
"RequestCache enumeration value should match Necko Cache mode value.");
|
"RequestCache enumeration value should match Necko Cache mode value.");
|
||||||
static_assert(5 == static_cast<uint32_t>(RequestCache::EndGuard_),
|
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED == static_cast<uint32_t>(RequestCache::Only_if_cached),
|
||||||
|
"RequestCache enumeration value should match Necko Cache mode value.");
|
||||||
|
static_assert(6 == static_cast<uint32_t>(RequestCache::EndGuard_),
|
||||||
"RequestCache enumeration value should match Necko Cache mode value.");
|
"RequestCache enumeration value should match Necko Cache mode value.");
|
||||||
|
|
||||||
static StaticRefPtr<ServiceWorkerManager> gInstance;
|
static StaticRefPtr<ServiceWorkerManager> gInstance;
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "nscore.h"
|
#include "nscore.h"
|
||||||
|
|
||||||
|
// Workaround for windows headers
|
||||||
|
#ifdef SetProp
|
||||||
|
#undef SetProp
|
||||||
|
#endif
|
||||||
|
|
||||||
class nsIAtom;
|
class nsIAtom;
|
||||||
class nsIDOMNode;
|
class nsIDOMNode;
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
|
@ -498,25 +498,11 @@ nsEditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
|
#ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
|
||||||
#include <windows.h>
|
|
||||||
// Undo the windows.h damage
|
|
||||||
#undef GetMessage
|
|
||||||
#undef CreateEvent
|
|
||||||
#undef GetClassName
|
|
||||||
#undef GetBinaryType
|
|
||||||
#undef RemoveDirectory
|
|
||||||
#undef SetProp
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// This function is borrowed from Chromium's ImeInput::IsCtrlShiftPressed
|
// This function is borrowed from Chromium's ImeInput::IsCtrlShiftPressed
|
||||||
bool IsCtrlShiftPressed(bool& isRTL)
|
bool IsCtrlShiftPressed(nsIDOMKeyEvent* aEvent, bool& isRTL)
|
||||||
{
|
{
|
||||||
BYTE keystate[256];
|
|
||||||
if (!::GetKeyboardState(keystate)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// To check if a user is pressing only a control key and a right-shift key
|
// To check if a user is pressing only a control key and a right-shift key
|
||||||
// (or a left-shift key), we use the steps below:
|
// (or a left-shift key), we use the steps below:
|
||||||
// 1. Check if a user is pressing a control key and a right-shift key (or
|
// 1. Check if a user is pressing a control key and a right-shift key (or
|
||||||
|
@ -525,16 +511,19 @@ bool IsCtrlShiftPressed(bool& isRTL)
|
||||||
// keys pressed at the same time.
|
// keys pressed at the same time.
|
||||||
// To ignore the keys checked in 1, we set their status to 0 before
|
// To ignore the keys checked in 1, we set their status to 0 before
|
||||||
// checking the key status.
|
// checking the key status.
|
||||||
const int kKeyDownMask = 0x80;
|
WidgetKeyboardEvent* keyboardEvent =
|
||||||
if ((keystate[VK_CONTROL] & kKeyDownMask) == 0) {
|
aEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
|
||||||
|
MOZ_ASSERT(keyboardEvent,
|
||||||
|
"DOM key event's internal event must be WidgetKeyboardEvent");
|
||||||
|
|
||||||
|
if (!keyboardEvent->IsControl()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keystate[VK_RSHIFT] & kKeyDownMask) {
|
uint32_t location = keyboardEvent->mLocation;
|
||||||
keystate[VK_RSHIFT] = 0;
|
if (location == nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT) {
|
||||||
isRTL = true;
|
isRTL = true;
|
||||||
} else if (keystate[VK_LSHIFT] & kKeyDownMask) {
|
} else if (location == nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT) {
|
||||||
keystate[VK_LSHIFT] = 0;
|
|
||||||
isRTL = false;
|
isRTL = false;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -542,19 +531,10 @@ bool IsCtrlShiftPressed(bool& isRTL)
|
||||||
|
|
||||||
// Scan the key status to find pressed keys. We should abandon changing the
|
// Scan the key status to find pressed keys. We should abandon changing the
|
||||||
// text direction when there are other pressed keys.
|
// text direction when there are other pressed keys.
|
||||||
// This code is executed only when a user is pressing a control key and a
|
if (keyboardEvent->IsAlt() || keyboardEvent->IsOS()) {
|
||||||
// right-shift key (or a left-shift key), i.e. we should ignore the status of
|
return false;
|
||||||
// the keys: VK_SHIFT, VK_CONTROL, VK_RCONTROL, and VK_LCONTROL.
|
|
||||||
// So, we reset their status to 0 and ignore them.
|
|
||||||
keystate[VK_SHIFT] = 0;
|
|
||||||
keystate[VK_CONTROL] = 0;
|
|
||||||
keystate[VK_RCONTROL] = 0;
|
|
||||||
keystate[VK_LCONTROL] = 0;
|
|
||||||
for (int i = 0; i <= VK_PACKET; ++i) {
|
|
||||||
if (keystate[i] & kKeyDownMask) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,7 +578,7 @@ nsEditorEventListener::KeyDown(nsIDOMKeyEvent* aKeyEvent)
|
||||||
aKeyEvent->GetKeyCode(&keyCode);
|
aKeyEvent->GetKeyCode(&keyCode);
|
||||||
if (keyCode == nsIDOMKeyEvent::DOM_VK_SHIFT) {
|
if (keyCode == nsIDOMKeyEvent::DOM_VK_SHIFT) {
|
||||||
bool switchToRTL;
|
bool switchToRTL;
|
||||||
if (IsCtrlShiftPressed(switchToRTL)) {
|
if (IsCtrlShiftPressed(aKeyEvent, switchToRTL)) {
|
||||||
mShouldSwitchTextDirection = true;
|
mShouldSwitchTextDirection = true;
|
||||||
mSwitchToRTL = switchToRTL;
|
mSwitchToRTL = switchToRTL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,11 @@
|
||||||
#include "nsWSRunObject.h"
|
#include "nsWSRunObject.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
// Workaround for windows headers
|
||||||
|
#ifdef SetProp
|
||||||
|
#undef SetProp
|
||||||
|
#endif
|
||||||
|
|
||||||
class nsISupports;
|
class nsISupports;
|
||||||
class nsRulesInfo;
|
class nsRulesInfo;
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ static const char* const sExtensionNames[] = {
|
||||||
"GL_ARB_robustness",
|
"GL_ARB_robustness",
|
||||||
"GL_ARB_sampler_objects",
|
"GL_ARB_sampler_objects",
|
||||||
"GL_ARB_seamless_cube_map",
|
"GL_ARB_seamless_cube_map",
|
||||||
|
"GL_ARB_shader_texture_lod",
|
||||||
"GL_ARB_sync",
|
"GL_ARB_sync",
|
||||||
"GL_ARB_texture_compression",
|
"GL_ARB_texture_compression",
|
||||||
"GL_ARB_texture_float",
|
"GL_ARB_texture_float",
|
||||||
|
|
|
@ -128,6 +128,7 @@ enum class GLFeature {
|
||||||
sRGB_texture,
|
sRGB_texture,
|
||||||
sampler_objects,
|
sampler_objects,
|
||||||
seamless_cube_map_opt_in,
|
seamless_cube_map_opt_in,
|
||||||
|
shader_texture_lod,
|
||||||
split_framebuffer,
|
split_framebuffer,
|
||||||
standard_derivatives,
|
standard_derivatives,
|
||||||
sync,
|
sync,
|
||||||
|
@ -423,6 +424,7 @@ public:
|
||||||
ARB_robustness,
|
ARB_robustness,
|
||||||
ARB_sampler_objects,
|
ARB_sampler_objects,
|
||||||
ARB_seamless_cube_map,
|
ARB_seamless_cube_map,
|
||||||
|
ARB_shader_texture_lod,
|
||||||
ARB_sync,
|
ARB_sync,
|
||||||
ARB_texture_compression,
|
ARB_texture_compression,
|
||||||
ARB_texture_float,
|
ARB_texture_float,
|
||||||
|
|
|
@ -567,6 +567,17 @@ static const FeatureInfo sFeatureInfoArr[] = {
|
||||||
GLContext::Extensions_End
|
GLContext::Extensions_End
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"shader_texture_lod",
|
||||||
|
GLVersion::NONE,
|
||||||
|
GLESVersion::NONE,
|
||||||
|
GLContext::Extension_None,
|
||||||
|
{
|
||||||
|
GLContext::ARB_shader_texture_lod,
|
||||||
|
GLContext::EXT_shader_texture_lod,
|
||||||
|
GLContext::Extensions_End
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// Do we have separate DRAW and READ framebuffer bind points?
|
// Do we have separate DRAW and READ framebuffer bind points?
|
||||||
"split_framebuffer",
|
"split_framebuffer",
|
||||||
|
|
|
@ -169,6 +169,12 @@ GLXLibrary::EnsureInitialized()
|
||||||
{ nullptr, { nullptr } }
|
{ nullptr, { nullptr } }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
GLLibraryLoader::SymLoadStruct symbols_videosync[] = {
|
||||||
|
{ (PRFuncPtr*) &xGetVideoSyncInternal, { "glXGetVideoSyncSGI", nullptr } },
|
||||||
|
{ (PRFuncPtr*) &xWaitVideoSyncInternal, { "glXWaitVideoSyncSGI", nullptr } },
|
||||||
|
{ nullptr, { nullptr } }
|
||||||
|
};
|
||||||
|
|
||||||
if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &symbols[0])) {
|
if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &symbols[0])) {
|
||||||
NS_WARNING("Couldn't find required entry point in OpenGL shared library");
|
NS_WARNING("Couldn't find required entry point in OpenGL shared library");
|
||||||
return false;
|
return false;
|
||||||
|
@ -246,6 +252,13 @@ GLXLibrary::EnsureInitialized()
|
||||||
mHasRobustness = true;
|
mHasRobustness = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HasExtension(extensionsStr, "GLX_SGI_video_sync") &&
|
||||||
|
GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_videosync,
|
||||||
|
(GLLibraryLoader::PlatformLookupFunction)&xGetProcAddress))
|
||||||
|
{
|
||||||
|
mHasVideoSync = true;
|
||||||
|
}
|
||||||
|
|
||||||
mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI");
|
mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI");
|
||||||
mIsNVIDIA = serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation");
|
mIsNVIDIA = serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation");
|
||||||
mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa");
|
mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa");
|
||||||
|
@ -269,6 +282,16 @@ GLXLibrary::SupportsTextureFromPixmap(gfxASurface* aSurface)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
GLXLibrary::SupportsVideoSync()
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mHasVideoSync;
|
||||||
|
}
|
||||||
|
|
||||||
GLXPixmap
|
GLXPixmap
|
||||||
GLXLibrary::CreatePixmap(gfxASurface* aSurface)
|
GLXLibrary::CreatePixmap(gfxASurface* aSurface)
|
||||||
{
|
{
|
||||||
|
@ -737,6 +760,24 @@ GLXLibrary::xCreateContextAttribs(Display* display,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
GLXLibrary::xGetVideoSync(unsigned int* count)
|
||||||
|
{
|
||||||
|
BEFORE_GLX_CALL;
|
||||||
|
int result = xGetVideoSyncInternal(count);
|
||||||
|
AFTER_GLX_CALL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
GLXLibrary::xWaitVideoSync(int divisor, int remainder, unsigned int* count)
|
||||||
|
{
|
||||||
|
BEFORE_GLX_CALL;
|
||||||
|
int result = xWaitVideoSyncInternal(divisor, remainder, count);
|
||||||
|
AFTER_GLX_CALL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
already_AddRefed<GLContextGLX>
|
already_AddRefed<GLContextGLX>
|
||||||
GLContextGLX::CreateGLContext(
|
GLContextGLX::CreateGLContext(
|
||||||
const SurfaceCaps& caps,
|
const SurfaceCaps& caps,
|
||||||
|
|
|
@ -54,9 +54,12 @@ public:
|
||||||
, xWaitGLInternal(nullptr)
|
, xWaitGLInternal(nullptr)
|
||||||
, xWaitXInternal(nullptr)
|
, xWaitXInternal(nullptr)
|
||||||
, xCreateContextAttribsInternal(nullptr)
|
, xCreateContextAttribsInternal(nullptr)
|
||||||
|
, xGetVideoSyncInternal(nullptr)
|
||||||
|
, xWaitVideoSyncInternal(nullptr)
|
||||||
, mInitialized(false), mTriedInitializing(false)
|
, mInitialized(false), mTriedInitializing(false)
|
||||||
, mUseTextureFromPixmap(false), mDebug(false)
|
, mUseTextureFromPixmap(false), mDebug(false)
|
||||||
, mHasRobustness(false), mHasCreateContextAttribs(false)
|
, mHasRobustness(false), mHasCreateContextAttribs(false)
|
||||||
|
, mHasVideoSync(false)
|
||||||
, mIsATI(false), mIsNVIDIA(false)
|
, mIsATI(false), mIsNVIDIA(false)
|
||||||
, mClientIsMesa(false), mGLXMajorVersion(0)
|
, mClientIsMesa(false), mGLXMajorVersion(0)
|
||||||
, mGLXMinorVersion(0)
|
, mGLXMinorVersion(0)
|
||||||
|
@ -120,6 +123,9 @@ public:
|
||||||
Bool direct,
|
Bool direct,
|
||||||
const int* attrib_list);
|
const int* attrib_list);
|
||||||
|
|
||||||
|
int xGetVideoSync(unsigned int* count);
|
||||||
|
int xWaitVideoSync(int divisor, int remainder, unsigned int* count);
|
||||||
|
|
||||||
bool EnsureInitialized();
|
bool EnsureInitialized();
|
||||||
|
|
||||||
GLXPixmap CreatePixmap(gfxASurface* aSurface);
|
GLXPixmap CreatePixmap(gfxASurface* aSurface);
|
||||||
|
@ -132,6 +138,7 @@ public:
|
||||||
bool HasRobustness() { return mHasRobustness; }
|
bool HasRobustness() { return mHasRobustness; }
|
||||||
bool HasCreateContextAttribs() { return mHasCreateContextAttribs; }
|
bool HasCreateContextAttribs() { return mHasCreateContextAttribs; }
|
||||||
bool SupportsTextureFromPixmap(gfxASurface* aSurface);
|
bool SupportsTextureFromPixmap(gfxASurface* aSurface);
|
||||||
|
bool SupportsVideoSync();
|
||||||
bool IsATI() { return mIsATI; }
|
bool IsATI() { return mIsATI; }
|
||||||
bool GLXVersionCheck(int aMajor, int aMinor);
|
bool GLXVersionCheck(int aMajor, int aMinor);
|
||||||
|
|
||||||
|
@ -225,6 +232,12 @@ private:
|
||||||
const int *);
|
const int *);
|
||||||
PFNGLXCREATECONTEXTATTRIBS xCreateContextAttribsInternal;
|
PFNGLXCREATECONTEXTATTRIBS xCreateContextAttribsInternal;
|
||||||
|
|
||||||
|
typedef int (GLAPIENTRY *PFNGLXGETVIDEOSYNCSGI) (unsigned int *count);
|
||||||
|
PFNGLXGETVIDEOSYNCSGI xGetVideoSyncInternal;
|
||||||
|
|
||||||
|
typedef int (GLAPIENTRY *PFNGLXWAITVIDEOSYNCSGI) (int divisor, int remainder, unsigned int *count);
|
||||||
|
PFNGLXWAITVIDEOSYNCSGI xWaitVideoSyncInternal;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void BeforeGLXCall();
|
void BeforeGLXCall();
|
||||||
void AfterGLXCall();
|
void AfterGLXCall();
|
||||||
|
@ -236,6 +249,7 @@ private:
|
||||||
bool mDebug;
|
bool mDebug;
|
||||||
bool mHasRobustness;
|
bool mHasRobustness;
|
||||||
bool mHasCreateContextAttribs;
|
bool mHasCreateContextAttribs;
|
||||||
|
bool mHasVideoSync;
|
||||||
bool mIsATI;
|
bool mIsATI;
|
||||||
bool mIsNVIDIA;
|
bool mIsNVIDIA;
|
||||||
bool mClientIsMesa;
|
bool mClientIsMesa;
|
||||||
|
|
|
@ -732,25 +732,6 @@ gfxContext::Mask(SourceSurface* aSurface, Float aAlpha, const Matrix& aTransform
|
||||||
ChangeTransform(old);
|
ChangeTransform(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
gfxContext::Mask(gfxASurface *surface, const gfxPoint& offset)
|
|
||||||
{
|
|
||||||
PROFILER_LABEL("gfxContext", "Mask",
|
|
||||||
js::ProfileEntry::Category::GRAPHICS);
|
|
||||||
|
|
||||||
// Lifetime needs to be limited here as we may simply wrap surface's data.
|
|
||||||
RefPtr<SourceSurface> sourceSurf =
|
|
||||||
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
|
|
||||||
|
|
||||||
if (!sourceSurf) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfxPoint pt = surface->GetDeviceOffset();
|
|
||||||
|
|
||||||
Mask(sourceSurf, 1.0f, Point(offset.x - pt.x, offset.y - pt.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
gfxContext::Mask(SourceSurface *surface, float alpha, const Point& offset)
|
gfxContext::Mask(SourceSurface *surface, float alpha, const Point& offset)
|
||||||
{
|
{
|
||||||
|
|
|
@ -304,13 +304,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void Mask(mozilla::gfx::SourceSurface *aSurface, mozilla::gfx::Float aAlpha, const mozilla::gfx::Matrix& aTransform);
|
void Mask(mozilla::gfx::SourceSurface *aSurface, mozilla::gfx::Float aAlpha, const mozilla::gfx::Matrix& aTransform);
|
||||||
void Mask(mozilla::gfx::SourceSurface *aSurface, const mozilla::gfx::Matrix& aTransform) { Mask(aSurface, 1.0f, aTransform); }
|
void Mask(mozilla::gfx::SourceSurface *aSurface, const mozilla::gfx::Matrix& aTransform) { Mask(aSurface, 1.0f, aTransform); }
|
||||||
|
|
||||||
/**
|
|
||||||
* Shorthand for creating a pattern and calling the pattern-taking
|
|
||||||
* variant of Mask.
|
|
||||||
*/
|
|
||||||
void Mask(gfxASurface *surface, const gfxPoint& offset = gfxPoint(0.0, 0.0));
|
|
||||||
|
|
||||||
void Mask(mozilla::gfx::SourceSurface *surface, float alpha = 1.0f, const mozilla::gfx::Point& offset = mozilla::gfx::Point());
|
void Mask(mozilla::gfx::SourceSurface *surface, float alpha = 1.0f, const mozilla::gfx::Point& offset = mozilla::gfx::Point());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace mozilla {
|
||||||
* between unquoted and quoted names for serializaiton
|
* between unquoted and quoted names for serializaiton
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum FontFamilyType {
|
enum FontFamilyType : uint32_t {
|
||||||
eFamily_none = 0, // used when finding generics
|
eFamily_none = 0, // used when finding generics
|
||||||
|
|
||||||
// explicitly named font family (e.g. Helvetica)
|
// explicitly named font family (e.g. Helvetica)
|
||||||
|
|
|
@ -20,6 +20,13 @@
|
||||||
#include "gfxUtils.h"
|
#include "gfxUtils.h"
|
||||||
#include "gfxFT2FontBase.h"
|
#include "gfxFT2FontBase.h"
|
||||||
#include "gfxPrefs.h"
|
#include "gfxPrefs.h"
|
||||||
|
#include "VsyncSource.h"
|
||||||
|
#include "mozilla/Atomics.h"
|
||||||
|
#include "mozilla/Monitor.h"
|
||||||
|
#include "base/task.h"
|
||||||
|
#include "base/thread.h"
|
||||||
|
#include "base/message_loop.h"
|
||||||
|
#include "mozilla/gfx/Logging.h"
|
||||||
|
|
||||||
#include "mozilla/gfx/2D.h"
|
#include "mozilla/gfx/2D.h"
|
||||||
|
|
||||||
|
@ -34,6 +41,12 @@
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/X11Util.h"
|
#include "mozilla/X11Util.h"
|
||||||
|
|
||||||
|
#ifdef GL_PROVIDER_GLX
|
||||||
|
#include "GLContextProvider.h"
|
||||||
|
#include "GLContextGLX.h"
|
||||||
|
#include "GLXLibrary.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Undefine the Status from Xlib since it will conflict with system headers on OSX */
|
/* Undefine the Status from Xlib since it will conflict with system headers on OSX */
|
||||||
#if defined(__APPLE__) && defined(Status)
|
#if defined(__APPLE__) && defined(Status)
|
||||||
#undef Status
|
#undef Status
|
||||||
|
@ -583,3 +596,230 @@ gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
|
||||||
{
|
{
|
||||||
return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
|
return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef GL_PROVIDER_GLX
|
||||||
|
|
||||||
|
class GLXVsyncSource final : public VsyncSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GLXVsyncSource()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
mGlobalDisplay = new GLXDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~GLXVsyncSource()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Display& GetGlobalDisplay() override
|
||||||
|
{
|
||||||
|
return *mGlobalDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GLXDisplay final : public VsyncSource::Display
|
||||||
|
{
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GLXDisplay)
|
||||||
|
|
||||||
|
public:
|
||||||
|
GLXDisplay() : mGLContext(nullptr)
|
||||||
|
, mSetupLock("GLXVsyncSetupLock")
|
||||||
|
, mVsyncThread("GLXVsyncThread")
|
||||||
|
, mVsyncTask(nullptr)
|
||||||
|
, mVsyncEnabledLock("GLXVsyncEnabledLock")
|
||||||
|
, mVsyncEnabled(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets up the display's GL context on a worker thread.
|
||||||
|
// Required as GLContexts may only be used by the creating thread.
|
||||||
|
// Returns true if setup was a success.
|
||||||
|
bool Setup()
|
||||||
|
{
|
||||||
|
MonitorAutoLock lock(mSetupLock);
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
if (!mVsyncThread.Start())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RefPtr<Runnable> vsyncSetup = NewRunnableMethod(this, &GLXDisplay::SetupGLContext);
|
||||||
|
mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
|
||||||
|
// Wait until the setup has completed.
|
||||||
|
lock.Wait();
|
||||||
|
return mGLContext != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called on the Vsync thread to setup the GL context.
|
||||||
|
void SetupGLContext()
|
||||||
|
{
|
||||||
|
MonitorAutoLock lock(mSetupLock);
|
||||||
|
MOZ_ASSERT(!NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(!mGLContext, "GLContext already setup!");
|
||||||
|
|
||||||
|
// Create video sync timer on a separate Display to prevent locking the
|
||||||
|
// main thread X display.
|
||||||
|
mXDisplay = XOpenDisplay(nullptr);
|
||||||
|
if (!mXDisplay) {
|
||||||
|
lock.NotifyAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most compositors wait for vsync events on the root window.
|
||||||
|
Window root = DefaultRootWindow(mXDisplay);
|
||||||
|
int screen = DefaultScreen(mXDisplay);
|
||||||
|
|
||||||
|
ScopedXFree<GLXFBConfig> cfgs;
|
||||||
|
GLXFBConfig config;
|
||||||
|
int visid;
|
||||||
|
if (!gl::GLContextGLX::FindFBConfigForWindow(mXDisplay, screen, root,
|
||||||
|
&cfgs, &config, &visid)) {
|
||||||
|
lock.NotifyAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGLContext = gl::GLContextGLX::CreateGLContext(
|
||||||
|
gl::SurfaceCaps::Any(),
|
||||||
|
nullptr,
|
||||||
|
false,
|
||||||
|
mXDisplay,
|
||||||
|
root,
|
||||||
|
config,
|
||||||
|
false);
|
||||||
|
|
||||||
|
mGLContext->MakeCurrent();
|
||||||
|
|
||||||
|
// Test that SGI_video_sync lets us get the counter.
|
||||||
|
unsigned int syncCounter = 0;
|
||||||
|
if (gl::sGLXLibrary.xGetVideoSync(&syncCounter) != 0) {
|
||||||
|
mGLContext = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.NotifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void EnableVsync() override
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(mGLContext, "GLContext not setup!");
|
||||||
|
|
||||||
|
MonitorAutoLock lock(mVsyncEnabledLock);
|
||||||
|
if (mVsyncEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mVsyncEnabled = true;
|
||||||
|
|
||||||
|
// If the task has not nulled itself out, it hasn't yet realized
|
||||||
|
// that vsync was disabled earlier, so continue its execution.
|
||||||
|
if (!mVsyncTask) {
|
||||||
|
mVsyncTask = NewRunnableMethod(this, &GLXDisplay::RunVsync);
|
||||||
|
RefPtr<Runnable> addrefedTask = mVsyncTask;
|
||||||
|
mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void DisableVsync() override
|
||||||
|
{
|
||||||
|
MonitorAutoLock lock(mVsyncEnabledLock);
|
||||||
|
mVsyncEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool IsVsyncEnabled() override
|
||||||
|
{
|
||||||
|
MonitorAutoLock lock(mVsyncEnabledLock);
|
||||||
|
return mVsyncEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Shutdown() override
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
DisableVsync();
|
||||||
|
|
||||||
|
// Cleanup thread-specific resources before shutting down.
|
||||||
|
RefPtr<Runnable> shutdownTask = NewRunnableMethod(this, &GLXDisplay::Cleanup);
|
||||||
|
mVsyncThread.message_loop()->PostTask(shutdownTask.forget());
|
||||||
|
|
||||||
|
// Stop, waiting for the cleanup task to finish execution.
|
||||||
|
mVsyncThread.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual ~GLXDisplay()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunVsync()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!NS_IsMainThread());
|
||||||
|
|
||||||
|
mGLContext->MakeCurrent();
|
||||||
|
|
||||||
|
unsigned int syncCounter = 0;
|
||||||
|
gl::sGLXLibrary.xGetVideoSync(&syncCounter);
|
||||||
|
for (;;) {
|
||||||
|
{
|
||||||
|
MonitorAutoLock lock(mVsyncEnabledLock);
|
||||||
|
if (!mVsyncEnabled) {
|
||||||
|
mVsyncTask = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeStamp lastVsync = TimeStamp::Now();
|
||||||
|
// Wait until the video sync counter reaches the next value by waiting
|
||||||
|
// until the parity of the counter value changes.
|
||||||
|
unsigned int nextSync = syncCounter + 1;
|
||||||
|
if (gl::sGLXLibrary.xWaitVideoSync(2, nextSync % 2,
|
||||||
|
&syncCounter) == 0) {
|
||||||
|
if (syncCounter == (nextSync - 1)) {
|
||||||
|
gfxWarning() << "GLX sync counter failed to increment after glXWaitVideoSync!\n";
|
||||||
|
// If we failed to block until the next sync, fallback to software.
|
||||||
|
double remaining = (1000.f / 60.f) -
|
||||||
|
(TimeStamp::Now() - lastVsync).ToMilliseconds();
|
||||||
|
if (remaining > 0) {
|
||||||
|
PlatformThread::Sleep(remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastVsync = TimeStamp::Now();
|
||||||
|
NotifyVsync(lastVsync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cleanup() {
|
||||||
|
MOZ_ASSERT(!NS_IsMainThread());
|
||||||
|
|
||||||
|
mGLContext = nullptr;
|
||||||
|
XCloseDisplay(mXDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owned by the vsync thread.
|
||||||
|
RefPtr<gl::GLContextGLX> mGLContext;
|
||||||
|
_XDisplay* mXDisplay;
|
||||||
|
Monitor mSetupLock;
|
||||||
|
base::Thread mVsyncThread;
|
||||||
|
RefPtr<Runnable> mVsyncTask;
|
||||||
|
Monitor mVsyncEnabledLock;
|
||||||
|
bool mVsyncEnabled;
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
// We need a refcounted VsyncSource::Display to use chromium IPC runnables.
|
||||||
|
RefPtr<GLXDisplay> mGlobalDisplay;
|
||||||
|
};
|
||||||
|
|
||||||
|
already_AddRefed<gfx::VsyncSource>
|
||||||
|
gfxPlatformGtk::CreateHardwareVsyncSource()
|
||||||
|
{
|
||||||
|
if (gl::sGLXLibrary.SupportsVideoSync()) {
|
||||||
|
RefPtr<VsyncSource> vsyncSource = new GLXVsyncSource();
|
||||||
|
VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
|
||||||
|
if (!static_cast<GLXVsyncSource::GLXDisplay&>(display).Setup()) {
|
||||||
|
NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
|
||||||
|
return gfxPlatform::CreateHardwareVsyncSource();
|
||||||
|
}
|
||||||
|
return vsyncSource.forget();
|
||||||
|
}
|
||||||
|
NS_WARNING("SGI_video_sync unsupported. Falling back to software vsync.");
|
||||||
|
return gfxPlatform::CreateHardwareVsyncSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -132,6 +132,10 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef GL_PROVIDER_GLX
|
||||||
|
already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static gfxFontconfigUtils *sFontconfigUtils;
|
static gfxFontconfigUtils *sFontconfigUtils;
|
||||||
|
|
||||||
|
|
|
@ -273,6 +273,7 @@ private:
|
||||||
DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled", SurfaceTextureDetachEnabled, bool, true);
|
DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled", SurfaceTextureDetachEnabled, bool, true);
|
||||||
DECL_GFX_PREF(Live, "gfx.testing.device-reset", DeviceResetForTesting, int32_t, 0);
|
DECL_GFX_PREF(Live, "gfx.testing.device-reset", DeviceResetForTesting, int32_t, 0);
|
||||||
DECL_GFX_PREF(Live, "gfx.testing.device-fail", DeviceFailForTesting, bool, false);
|
DECL_GFX_PREF(Live, "gfx.testing.device-fail", DeviceFailForTesting, bool, false);
|
||||||
|
DECL_GFX_PREF(Live, "gfx.ycbcr.accurate-conversion", YCbCrAccurateConversion, bool, false);
|
||||||
|
|
||||||
DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer", UseNativePushLayer, bool, false);
|
DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer", UseNativePushLayer, bool, false);
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,6 @@
|
||||||
#include "nsPresContext.h"
|
#include "nsPresContext.h"
|
||||||
#include "nsRegion.h"
|
#include "nsRegion.h"
|
||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "yuv_convert.h"
|
|
||||||
#include "ycbcr_to_rgb565.h"
|
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
#include "ImageContainer.h"
|
#include "ImageContainer.h"
|
||||||
#include "ImageRegion.h"
|
#include "ImageRegion.h"
|
||||||
|
|
|
@ -7,14 +7,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// On Windows, protypes.h is #included, which defines these types. This sucks!
|
#include "libyuv/basic_types.h"
|
||||||
#ifndef PROTYPES_H
|
|
||||||
typedef uint8_t uint8;
|
|
||||||
typedef int8_t int8;
|
|
||||||
typedef int16_t int16;
|
|
||||||
typedef uint16_t uint16;
|
|
||||||
typedef uint32_t uint32;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// From Chromium build_config.h:
|
// From Chromium build_config.h:
|
||||||
// Processor architecture detection. For more info on what's defined, see:
|
// Processor architecture detection. For more info on what's defined, see:
|
||||||
|
|
|
@ -5,11 +5,7 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
EXPORTS += [
|
EXPORTS += [
|
||||||
'chromium_types.h',
|
|
||||||
'ycbcr_to_rgb565.h',
|
|
||||||
'YCbCrUtils.h',
|
'YCbCrUtils.h',
|
||||||
'yuv_convert.h',
|
|
||||||
'yuv_row.h',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
|
@ -63,4 +59,6 @@ if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['HAVE_ARM_NEON']:
|
||||||
'yuv_convert_arm.cpp',
|
'yuv_convert_arm.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
LOCAL_INCLUDES += ['/media/libyuv/include']
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include "yuv_convert.h"
|
#include "yuv_convert.h"
|
||||||
|
|
||||||
|
#include "gfxPrefs.h"
|
||||||
|
#include "libyuv.h"
|
||||||
// Header for low level row functions.
|
// Header for low level row functions.
|
||||||
#include "yuv_row.h"
|
#include "yuv_row.h"
|
||||||
#include "mozilla/SSE.h"
|
#include "mozilla/SSE.h"
|
||||||
|
@ -60,6 +62,72 @@ void ConvertYCbCrToRGB32(const uint8* y_buf,
|
||||||
int uv_pitch,
|
int uv_pitch,
|
||||||
int rgb_pitch,
|
int rgb_pitch,
|
||||||
YUVType yuv_type) {
|
YUVType yuv_type) {
|
||||||
|
|
||||||
|
|
||||||
|
// Deprecated function's conversion is accurate.
|
||||||
|
// libyuv converion is a bit inaccurate to get performance. It dynamically
|
||||||
|
// calculates RGB from YUV to use simd. In it, signed byte is used for conversion's
|
||||||
|
// coefficient, but it requests 129. libyuv cut 129 to 127. And only 6 bits are
|
||||||
|
// used for a decimal part during the dynamic calculation.
|
||||||
|
//
|
||||||
|
// The function is still fast on some old intel chips.
|
||||||
|
// See Bug 1256475.
|
||||||
|
bool use_deprecated = gfxPrefs::YCbCrAccurateConversion() ||
|
||||||
|
(supports_mmx() && supports_sse() && !supports_sse3());
|
||||||
|
if (use_deprecated) {
|
||||||
|
ConvertYCbCrToRGB32_deprecated(y_buf, u_buf, v_buf, rgb_buf,
|
||||||
|
pic_x, pic_y, pic_width, pic_height,
|
||||||
|
y_pitch, uv_pitch, rgb_pitch, yuv_type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yuv_type == YV24) {
|
||||||
|
const uint8* src_y = y_buf + y_pitch * pic_y + pic_x;
|
||||||
|
const uint8* src_u = u_buf + uv_pitch * pic_y + pic_x;
|
||||||
|
const uint8* src_v = v_buf + uv_pitch * pic_y + pic_x;
|
||||||
|
DebugOnly<int> err = libyuv::I444ToARGB(src_y, y_pitch,
|
||||||
|
src_u, uv_pitch,
|
||||||
|
src_v, uv_pitch,
|
||||||
|
rgb_buf, rgb_pitch,
|
||||||
|
pic_width, pic_height);
|
||||||
|
MOZ_ASSERT(!err);
|
||||||
|
} else if (yuv_type == YV16) {
|
||||||
|
const uint8* src_y = y_buf + y_pitch * pic_y + pic_x;
|
||||||
|
const uint8* src_u = u_buf + uv_pitch * pic_y + pic_x / 2;
|
||||||
|
const uint8* src_v = v_buf + uv_pitch * pic_y + pic_x / 2;
|
||||||
|
DebugOnly<int> err = libyuv::I422ToARGB(src_y, y_pitch,
|
||||||
|
src_u, uv_pitch,
|
||||||
|
src_v, uv_pitch,
|
||||||
|
rgb_buf, rgb_pitch,
|
||||||
|
pic_width, pic_height);
|
||||||
|
MOZ_ASSERT(!err);
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(yuv_type == YV12);
|
||||||
|
const uint8* src_y = y_buf + y_pitch * pic_y + pic_x;
|
||||||
|
const uint8* src_u = u_buf + (uv_pitch * pic_y + pic_x) / 2;
|
||||||
|
const uint8* src_v = v_buf + (uv_pitch * pic_y + pic_x) / 2;
|
||||||
|
DebugOnly<int> err = libyuv::I420ToARGB(src_y, y_pitch,
|
||||||
|
src_u, uv_pitch,
|
||||||
|
src_v, uv_pitch,
|
||||||
|
rgb_buf, rgb_pitch,
|
||||||
|
pic_width, pic_height);
|
||||||
|
MOZ_ASSERT(!err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a frame of YUV to 32 bit ARGB.
|
||||||
|
void ConvertYCbCrToRGB32_deprecated(const uint8* y_buf,
|
||||||
|
const uint8* u_buf,
|
||||||
|
const uint8* v_buf,
|
||||||
|
uint8* rgb_buf,
|
||||||
|
int pic_x,
|
||||||
|
int pic_y,
|
||||||
|
int pic_width,
|
||||||
|
int pic_height,
|
||||||
|
int y_pitch,
|
||||||
|
int uv_pitch,
|
||||||
|
int rgb_pitch,
|
||||||
|
YUVType yuv_type) {
|
||||||
unsigned int y_shift = yuv_type == YV12 ? 1 : 0;
|
unsigned int y_shift = yuv_type == YV12 ? 1 : 0;
|
||||||
unsigned int x_shift = yuv_type == YV24 ? 0 : 1;
|
unsigned int x_shift = yuv_type == YV24 ? 0 : 1;
|
||||||
// Test for SSE because the optimized code uses movntq, which is not part of MMX.
|
// Test for SSE because the optimized code uses movntq, which is not part of MMX.
|
||||||
|
|
|
@ -57,6 +57,19 @@ void ConvertYCbCrToRGB32(const uint8* yplane,
|
||||||
int rgbstride,
|
int rgbstride,
|
||||||
YUVType yuv_type);
|
YUVType yuv_type);
|
||||||
|
|
||||||
|
void ConvertYCbCrToRGB32_deprecated(const uint8* yplane,
|
||||||
|
const uint8* uplane,
|
||||||
|
const uint8* vplane,
|
||||||
|
uint8* rgbframe,
|
||||||
|
int pic_x,
|
||||||
|
int pic_y,
|
||||||
|
int pic_width,
|
||||||
|
int pic_height,
|
||||||
|
int ystride,
|
||||||
|
int uvstride,
|
||||||
|
int rgbstride,
|
||||||
|
YUVType yuv_type);
|
||||||
|
|
||||||
// Scale a frame of YUV to 32 bit ARGB.
|
// Scale a frame of YUV to 32 bit ARGB.
|
||||||
// Supports rotation and mirroring.
|
// Supports rotation and mirroring.
|
||||||
void ScaleYCbCrToRGB32(const uint8* yplane,
|
void ScaleYCbCrToRGB32(const uint8* yplane,
|
||||||
|
|
|
@ -15,44 +15,10 @@ enum { GIF_TRAILER = 0x3B }; // ';'
|
||||||
enum { GIF_IMAGE_SEPARATOR = 0x2C }; // ','
|
enum { GIF_IMAGE_SEPARATOR = 0x2C }; // ','
|
||||||
enum { GIF_EXTENSION_INTRODUCER = 0x21 }; // '!'
|
enum { GIF_EXTENSION_INTRODUCER = 0x21 }; // '!'
|
||||||
enum { GIF_GRAPHIC_CONTROL_LABEL = 0xF9 };
|
enum { GIF_GRAPHIC_CONTROL_LABEL = 0xF9 };
|
||||||
enum { GIF_COMMENT_LABEL = 0xFE };
|
|
||||||
enum { GIF_PLAIN_TEXT_LABEL = 0x01 };
|
|
||||||
enum { GIF_APPLICATION_EXTENSION_LABEL = 0xFF };
|
enum { GIF_APPLICATION_EXTENSION_LABEL = 0xFF };
|
||||||
|
|
||||||
// gif2.h
|
|
||||||
// The interface for the GIF87/89a decoder.
|
|
||||||
|
|
||||||
// List of possible parsing states
|
|
||||||
typedef enum {
|
|
||||||
gif_type,
|
|
||||||
gif_global_header,
|
|
||||||
gif_global_colormap,
|
|
||||||
gif_image_start,
|
|
||||||
gif_image_header,
|
|
||||||
gif_image_colormap,
|
|
||||||
gif_lzw_start,
|
|
||||||
gif_lzw,
|
|
||||||
gif_sub_block,
|
|
||||||
gif_extension,
|
|
||||||
gif_control_extension,
|
|
||||||
gif_consume_block,
|
|
||||||
gif_skip_block,
|
|
||||||
gif_done,
|
|
||||||
gif_error,
|
|
||||||
gif_comment_extension,
|
|
||||||
gif_application_extension,
|
|
||||||
gif_netscape_extension_block,
|
|
||||||
gif_consume_netscape_extension,
|
|
||||||
gif_consume_comment
|
|
||||||
} gstate;
|
|
||||||
|
|
||||||
// A GIF decoder's state
|
// A GIF decoder's state
|
||||||
typedef struct gif_struct {
|
typedef struct gif_struct {
|
||||||
// Parsing state machine
|
|
||||||
gstate state; // Current decoder master state
|
|
||||||
uint32_t bytes_to_consume; // Number of bytes to accumulate
|
|
||||||
uint32_t bytes_in_hold; // bytes accumulated so far
|
|
||||||
|
|
||||||
// LZW decoder state machine
|
// LZW decoder state machine
|
||||||
uint8_t* stackp; // Current stack pointer
|
uint8_t* stackp; // Current stack pointer
|
||||||
int datasize;
|
int datasize;
|
||||||
|
@ -61,7 +27,6 @@ typedef struct gif_struct {
|
||||||
int avail; // Index of next available slot in dictionary
|
int avail; // Index of next available slot in dictionary
|
||||||
int oldcode;
|
int oldcode;
|
||||||
uint8_t firstchar;
|
uint8_t firstchar;
|
||||||
int count; // Remaining # bytes in sub-block
|
|
||||||
int bits; // Number of unread bits in "datum"
|
int bits; // Number of unread bits in "datum"
|
||||||
int32_t datum; // 32-bit input buffer
|
int32_t datum; // 32-bit input buffer
|
||||||
|
|
||||||
|
@ -80,7 +45,8 @@ typedef struct gif_struct {
|
||||||
int version; // Either 89 for GIF89 or 87 for GIF87
|
int version; // Either 89 for GIF89 or 87 for GIF87
|
||||||
int32_t screen_width; // Logical screen width & height
|
int32_t screen_width; // Logical screen width & height
|
||||||
int32_t screen_height;
|
int32_t screen_height;
|
||||||
uint32_t global_colormap_depth; // Depth of global colormap array
|
uint8_t global_colormap_depth; // Depth of global colormap array
|
||||||
|
uint16_t global_colormap_count; // Number of colors in global colormap
|
||||||
int images_decoded; // Counts images for multi-part GIFs
|
int images_decoded; // Counts images for multi-part GIFs
|
||||||
int loop_count; // Netscape specific extension block to control
|
int loop_count; // Netscape specific extension block to control
|
||||||
// the number of animation loops a GIF
|
// the number of animation loops a GIF
|
||||||
|
@ -89,7 +55,6 @@ typedef struct gif_struct {
|
||||||
bool is_transparent; // TRUE, if tpixel is valid
|
bool is_transparent; // TRUE, if tpixel is valid
|
||||||
|
|
||||||
uint16_t prefix[MAX_BITS]; // LZW decoding tables
|
uint16_t prefix[MAX_BITS]; // LZW decoding tables
|
||||||
uint8_t* hold; // Accumulation buffer
|
|
||||||
uint32_t global_colormap[MAX_COLORS]; // Default colormap if local not
|
uint32_t global_colormap[MAX_COLORS]; // Default colormap if local not
|
||||||
// supplied
|
// supplied
|
||||||
uint8_t suffix[MAX_BITS]; // LZW decoding tables
|
uint8_t suffix[MAX_BITS]; // LZW decoding tables
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "Decoder.h"
|
#include "Decoder.h"
|
||||||
#include "GIF2.h"
|
#include "GIF2.h"
|
||||||
|
#include "StreamingLexer.h"
|
||||||
#include "SurfacePipe.h"
|
#include "SurfacePipe.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
@ -54,25 +55,70 @@ private:
|
||||||
/// Called when we finish decoding the entire image.
|
/// Called when we finish decoding the entire image.
|
||||||
void FlushImageData();
|
void FlushImageData();
|
||||||
|
|
||||||
nsresult GifWrite(const uint8_t* buf, uint32_t numbytes);
|
|
||||||
|
|
||||||
/// Transforms a palette index into a pixel.
|
/// Transforms a palette index into a pixel.
|
||||||
template <typename PixelSize> PixelSize
|
template <typename PixelSize> PixelSize
|
||||||
ColormapIndexToPixel(uint8_t aIndex);
|
ColormapIndexToPixel(uint8_t aIndex);
|
||||||
|
|
||||||
/// A generator function that performs LZW decompression and yields pixels.
|
/// A generator function that performs LZW decompression and yields pixels.
|
||||||
template <typename PixelSize> NextPixel<PixelSize>
|
template <typename PixelSize> NextPixel<PixelSize>
|
||||||
YieldPixel(const uint8_t*& aCurrentByte);
|
YieldPixel(const uint8_t* aData, size_t aLength, size_t* aBytesReadOut);
|
||||||
|
|
||||||
/// The entry point for LZW decompression.
|
/// Checks if we have transparency, either because the header indicates that
|
||||||
bool DoLzw(const uint8_t* aData);
|
/// there's alpha, or because the frame rect doesn't cover the entire image.
|
||||||
|
bool CheckForTransparency(const gfx::IntRect& aFrameRect);
|
||||||
|
|
||||||
bool SetHold(const uint8_t* buf, uint32_t count,
|
// @return the clear code used for LZW decompression.
|
||||||
const uint8_t* buf2 = nullptr, uint32_t count2 = 0);
|
int ClearCode() const { return 1 << mGIFStruct.datasize; }
|
||||||
bool CheckForTransparency(const gfx::IntRect& aFrameRect);
|
|
||||||
gfx::IntRect ClampToImageRect(const gfx::IntRect& aFrameRect);
|
|
||||||
|
|
||||||
inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
|
enum class State
|
||||||
|
{
|
||||||
|
FAILURE,
|
||||||
|
SUCCESS,
|
||||||
|
GIF_HEADER,
|
||||||
|
SCREEN_DESCRIPTOR,
|
||||||
|
GLOBAL_COLOR_TABLE,
|
||||||
|
FINISHED_GLOBAL_COLOR_TABLE,
|
||||||
|
BLOCK_HEADER,
|
||||||
|
EXTENSION_HEADER,
|
||||||
|
GRAPHIC_CONTROL_EXTENSION,
|
||||||
|
APPLICATION_IDENTIFIER,
|
||||||
|
NETSCAPE_EXTENSION_SUB_BLOCK,
|
||||||
|
NETSCAPE_EXTENSION_DATA,
|
||||||
|
IMAGE_DESCRIPTOR,
|
||||||
|
LOCAL_COLOR_TABLE,
|
||||||
|
FINISHED_LOCAL_COLOR_TABLE,
|
||||||
|
IMAGE_DATA_BLOCK,
|
||||||
|
IMAGE_DATA_SUB_BLOCK,
|
||||||
|
LZW_DATA,
|
||||||
|
FINISHED_LZW_DATA,
|
||||||
|
SKIP_SUB_BLOCKS,
|
||||||
|
SKIP_DATA_THEN_SKIP_SUB_BLOCKS,
|
||||||
|
FINISHED_SKIPPING_DATA
|
||||||
|
};
|
||||||
|
|
||||||
|
LexerTransition<State> ReadGIFHeader(const char* aData);
|
||||||
|
LexerTransition<State> ReadScreenDescriptor(const char* aData);
|
||||||
|
LexerTransition<State> ReadGlobalColorTable(const char* aData, size_t aLength);
|
||||||
|
LexerTransition<State> FinishedGlobalColorTable();
|
||||||
|
LexerTransition<State> ReadBlockHeader(const char* aData);
|
||||||
|
LexerTransition<State> ReadExtensionHeader(const char* aData);
|
||||||
|
LexerTransition<State> ReadGraphicControlExtension(const char* aData);
|
||||||
|
LexerTransition<State> ReadApplicationIdentifier(const char* aData);
|
||||||
|
LexerTransition<State> ReadNetscapeExtensionSubBlock(const char* aData);
|
||||||
|
LexerTransition<State> ReadNetscapeExtensionData(const char* aData);
|
||||||
|
LexerTransition<State> ReadImageDescriptor(const char* aData);
|
||||||
|
LexerTransition<State> ReadLocalColorTable(const char* aData, size_t aLength);
|
||||||
|
LexerTransition<State> FinishedLocalColorTable();
|
||||||
|
LexerTransition<State> ReadImageDataBlock(const char* aData);
|
||||||
|
LexerTransition<State> ReadImageDataSubBlock(const char* aData);
|
||||||
|
LexerTransition<State> ReadLZWData(const char* aData, size_t aLength);
|
||||||
|
LexerTransition<State> SkipSubBlocks(const char* aData);
|
||||||
|
|
||||||
|
// The StreamingLexer used to manage input. The initial size of the buffer is
|
||||||
|
// chosen as a little larger than the maximum size of any fixed-length data we
|
||||||
|
// have to read for a state. We read variable-length data in unbuffered mode
|
||||||
|
// so the buffer shouldn't have to be resized during decoding.
|
||||||
|
StreamingLexer<State, 16> mLexer;
|
||||||
|
|
||||||
uint32_t mOldColor; // The old value of the transparent pixel
|
uint32_t mOldColor; // The old value of the transparent pixel
|
||||||
|
|
||||||
|
@ -80,6 +126,11 @@ private:
|
||||||
// of decoding it, and -1 otherwise.
|
// of decoding it, and -1 otherwise.
|
||||||
int32_t mCurrentFrameIndex;
|
int32_t mCurrentFrameIndex;
|
||||||
|
|
||||||
|
// When we're reading in the global or local color table, this records our
|
||||||
|
// current position - i.e., the offset into which the next byte should be
|
||||||
|
// written.
|
||||||
|
size_t mColorTablePos;
|
||||||
|
|
||||||
uint8_t mColorMask; // Apply this to the pixel to keep within colormap
|
uint8_t mColorMask; // Apply this to the pixel to keep within colormap
|
||||||
bool mGIFOpen;
|
bool mGIFOpen;
|
||||||
bool mSawTransparency;
|
bool mSawTransparency;
|
||||||
|
|
|
@ -29,8 +29,6 @@ namespace base {
|
||||||
typedef ::Lock Lock;
|
typedef ::Lock Lock;
|
||||||
typedef ::AutoLock AutoLock;
|
typedef ::AutoLock AutoLock;
|
||||||
|
|
||||||
using mozilla::OffTheBooksMutexAutoLock;
|
|
||||||
|
|
||||||
// Static table of checksums for all possible 8 bit bytes.
|
// Static table of checksums for all possible 8 bit bytes.
|
||||||
const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL,
|
const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL,
|
||||||
0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
|
0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
|
||||||
|
@ -177,24 +175,20 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
|
||||||
SampleSet snapshot;
|
SampleSet snapshot;
|
||||||
SnapshotSample(&snapshot);
|
SnapshotSample(&snapshot);
|
||||||
|
|
||||||
// For the rest of the routine, we hold |snapshot|'s lock so as to
|
Count sample_count = snapshot.TotalCount();
|
||||||
// be able to examine it atomically.
|
|
||||||
OffTheBooksMutexAutoLock locker(snapshot.mutex());
|
|
||||||
|
|
||||||
Count sample_count = snapshot.TotalCount(locker);
|
WriteAsciiHeader(snapshot, sample_count, output);
|
||||||
|
|
||||||
WriteAsciiHeader(snapshot, locker, sample_count, output);
|
|
||||||
output->append(newline);
|
output->append(newline);
|
||||||
|
|
||||||
// Prepare to normalize graphical rendering of bucket contents.
|
// Prepare to normalize graphical rendering of bucket contents.
|
||||||
double max_size = 0;
|
double max_size = 0;
|
||||||
if (graph_it)
|
if (graph_it)
|
||||||
max_size = GetPeakBucketSize(snapshot, locker);
|
max_size = GetPeakBucketSize(snapshot);
|
||||||
|
|
||||||
// Calculate space needed to print bucket range numbers. Leave room to print
|
// Calculate space needed to print bucket range numbers. Leave room to print
|
||||||
// nearly the largest bucket range without sliding over the histogram.
|
// nearly the largest bucket range without sliding over the histogram.
|
||||||
size_t largest_non_empty_bucket = bucket_count() - 1;
|
size_t largest_non_empty_bucket = bucket_count() - 1;
|
||||||
while (0 == snapshot.counts(locker, largest_non_empty_bucket)) {
|
while (0 == snapshot.counts(largest_non_empty_bucket)) {
|
||||||
if (0 == largest_non_empty_bucket)
|
if (0 == largest_non_empty_bucket)
|
||||||
break; // All buckets are empty.
|
break; // All buckets are empty.
|
||||||
--largest_non_empty_bucket;
|
--largest_non_empty_bucket;
|
||||||
|
@ -203,7 +197,7 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
|
||||||
// Calculate largest print width needed for any of our bucket range displays.
|
// Calculate largest print width needed for any of our bucket range displays.
|
||||||
size_t print_width = 1;
|
size_t print_width = 1;
|
||||||
for (size_t i = 0; i < bucket_count(); ++i) {
|
for (size_t i = 0; i < bucket_count(); ++i) {
|
||||||
if (snapshot.counts(locker, i)) {
|
if (snapshot.counts(i)) {
|
||||||
size_t width = GetAsciiBucketRange(i).size() + 1;
|
size_t width = GetAsciiBucketRange(i).size() + 1;
|
||||||
if (width > print_width)
|
if (width > print_width)
|
||||||
print_width = width;
|
print_width = width;
|
||||||
|
@ -214,7 +208,7 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
|
||||||
int64_t past = 0;
|
int64_t past = 0;
|
||||||
// Output the actual histogram graph.
|
// Output the actual histogram graph.
|
||||||
for (size_t i = 0; i < bucket_count(); ++i) {
|
for (size_t i = 0; i < bucket_count(); ++i) {
|
||||||
Count current = snapshot.counts(locker, i);
|
Count current = snapshot.counts(i);
|
||||||
if (!current && !PrintEmptyBucket(i))
|
if (!current && !PrintEmptyBucket(i))
|
||||||
continue;
|
continue;
|
||||||
remaining -= current;
|
remaining -= current;
|
||||||
|
@ -223,8 +217,8 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
|
||||||
for (size_t j = 0; range.size() + j < print_width + 1; ++j)
|
for (size_t j = 0; range.size() + j < print_width + 1; ++j)
|
||||||
output->push_back(' ');
|
output->push_back(' ');
|
||||||
if (0 == current &&
|
if (0 == current &&
|
||||||
i < bucket_count() - 1 && 0 == snapshot.counts(locker, i + 1)) {
|
i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
|
||||||
while (i < bucket_count() - 1 && 0 == snapshot.counts(locker, i + 1))
|
while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
|
||||||
++i;
|
++i;
|
||||||
output->append("... ");
|
output->append("... ");
|
||||||
output->append(newline);
|
output->append(newline);
|
||||||
|
@ -244,14 +238,14 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
|
||||||
// Methods for the validating a sample and a related histogram.
|
// Methods for the validating a sample and a related histogram.
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
Histogram::Inconsistencies Histogram::FindCorruption(
|
Histogram::Inconsistencies
|
||||||
const SampleSet& snapshot,
|
Histogram::FindCorruption(const SampleSet& snapshot) const
|
||||||
const OffTheBooksMutexAutoLock& snapshotLockEvidence) const {
|
{
|
||||||
int inconsistencies = NO_INCONSISTENCIES;
|
int inconsistencies = NO_INCONSISTENCIES;
|
||||||
Sample previous_range = -1; // Bottom range is always 0.
|
Sample previous_range = -1; // Bottom range is always 0.
|
||||||
int64_t count = 0;
|
int64_t count = 0;
|
||||||
for (size_t index = 0; index < bucket_count(); ++index) {
|
for (size_t index = 0; index < bucket_count(); ++index) {
|
||||||
count += snapshot.counts(snapshotLockEvidence, index);
|
count += snapshot.counts(index);
|
||||||
int new_range = ranges(index);
|
int new_range = ranges(index);
|
||||||
if (previous_range >= new_range)
|
if (previous_range >= new_range)
|
||||||
inconsistencies |= BUCKET_ORDER_ERROR;
|
inconsistencies |= BUCKET_ORDER_ERROR;
|
||||||
|
@ -261,7 +255,7 @@ Histogram::Inconsistencies Histogram::FindCorruption(
|
||||||
if (!HasValidRangeChecksum())
|
if (!HasValidRangeChecksum())
|
||||||
inconsistencies |= RANGE_CHECKSUM_ERROR;
|
inconsistencies |= RANGE_CHECKSUM_ERROR;
|
||||||
|
|
||||||
int64_t delta64 = snapshot.redundant_count(snapshotLockEvidence) - count;
|
int64_t delta64 = snapshot.redundant_count() - count;
|
||||||
if (delta64 != 0) {
|
if (delta64 != 0) {
|
||||||
int delta = static_cast<int>(delta64);
|
int delta = static_cast<int>(delta64);
|
||||||
if (delta != delta64)
|
if (delta != delta64)
|
||||||
|
@ -302,7 +296,6 @@ size_t Histogram::bucket_count() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Histogram::SnapshotSample(SampleSet* sample) const {
|
void Histogram::SnapshotSample(SampleSet* sample) const {
|
||||||
OffTheBooksMutexAutoLock locker(sample_.mutex());
|
|
||||||
*sample = sample_;
|
*sample = sample_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,9 +329,9 @@ size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
size_t
|
||||||
|
Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||||
{
|
{
|
||||||
OffTheBooksMutexAutoLock locker(mutex_);
|
|
||||||
// We're not allowed to do deep dives into STL data structures. This
|
// We're not allowed to do deep dives into STL data structures. This
|
||||||
// is as close as we can get to measuring this array.
|
// is as close as we can get to measuring this array.
|
||||||
return aMallocSizeOf(&counts_[0]);
|
return aMallocSizeOf(&counts_[0]);
|
||||||
|
@ -556,13 +549,11 @@ uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) {
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Private methods
|
// Private methods
|
||||||
|
|
||||||
double Histogram::GetPeakBucketSize(const SampleSet& snapshot,
|
double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
|
||||||
const OffTheBooksMutexAutoLock&
|
|
||||||
snapshotLockEvidence) const {
|
|
||||||
double max = 0;
|
double max = 0;
|
||||||
for (size_t i = 0; i < bucket_count() ; ++i) {
|
for (size_t i = 0; i < bucket_count() ; ++i) {
|
||||||
double current_size
|
double current_size
|
||||||
= GetBucketSize(snapshot.counts(snapshotLockEvidence, i), i);
|
= GetBucketSize(snapshot.counts(i), i);
|
||||||
if (current_size > max)
|
if (current_size > max)
|
||||||
max = current_size;
|
max = current_size;
|
||||||
}
|
}
|
||||||
|
@ -570,15 +561,13 @@ double Histogram::GetPeakBucketSize(const SampleSet& snapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
|
void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
|
||||||
const OffTheBooksMutexAutoLock&
|
|
||||||
snapshotLockEvidence,
|
|
||||||
Count sample_count,
|
Count sample_count,
|
||||||
std::string* output) const {
|
std::string* output) const {
|
||||||
StringAppendF(output,
|
StringAppendF(output,
|
||||||
"Histogram: %s recorded %d samples",
|
"Histogram: %s recorded %d samples",
|
||||||
histogram_name().c_str(),
|
histogram_name().c_str(),
|
||||||
sample_count);
|
sample_count);
|
||||||
int64_t snapshot_sum = snapshot.sum(snapshotLockEvidence);
|
int64_t snapshot_sum = snapshot.sum();
|
||||||
if (0 == sample_count) {
|
if (0 == sample_count) {
|
||||||
DCHECK_EQ(snapshot_sum, 0);
|
DCHECK_EQ(snapshot_sum, 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -629,20 +618,17 @@ void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
|
||||||
Histogram::SampleSet::SampleSet()
|
Histogram::SampleSet::SampleSet()
|
||||||
: counts_(),
|
: counts_(),
|
||||||
sum_(0),
|
sum_(0),
|
||||||
redundant_count_(0),
|
redundant_count_(0) {
|
||||||
mutex_("Histogram::SampleSet::SampleSet") {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Histogram::SampleSet::~SampleSet() {
|
Histogram::SampleSet::~SampleSet() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Histogram::SampleSet::Resize(const Histogram& histogram) {
|
void Histogram::SampleSet::Resize(const Histogram& histogram) {
|
||||||
OffTheBooksMutexAutoLock locker(mutex_);
|
|
||||||
counts_.resize(histogram.bucket_count(), 0);
|
counts_.resize(histogram.bucket_count(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Histogram::SampleSet::Accumulate(const OffTheBooksMutexAutoLock& ev,
|
void Histogram::SampleSet::Accumulate(Sample value, Count count,
|
||||||
Sample value, Count count,
|
|
||||||
size_t index) {
|
size_t index) {
|
||||||
DCHECK(count == 1 || count == -1);
|
DCHECK(count == 1 || count == -1);
|
||||||
counts_[index] += count;
|
counts_[index] += count;
|
||||||
|
@ -653,15 +639,7 @@ void Histogram::SampleSet::Accumulate(const OffTheBooksMutexAutoLock& ev,
|
||||||
DCHECK_GE(redundant_count_, 0);
|
DCHECK_GE(redundant_count_, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Histogram::SampleSet::Accumulate(Sample value,
|
Count Histogram::SampleSet::TotalCount() const {
|
||||||
Count count,
|
|
||||||
size_t index) {
|
|
||||||
OffTheBooksMutexAutoLock locker(mutex_);
|
|
||||||
Accumulate(locker, value, count, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Count Histogram::SampleSet::TotalCount(const OffTheBooksMutexAutoLock& ev)
|
|
||||||
const {
|
|
||||||
Count total = 0;
|
Count total = 0;
|
||||||
for (Counts::const_iterator it = counts_.begin();
|
for (Counts::const_iterator it = counts_.begin();
|
||||||
it != counts_.end();
|
it != counts_.end();
|
||||||
|
@ -672,7 +650,6 @@ Count Histogram::SampleSet::TotalCount(const OffTheBooksMutexAutoLock& ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Histogram::SampleSet::Add(const SampleSet& other) {
|
void Histogram::SampleSet::Add(const SampleSet& other) {
|
||||||
OffTheBooksMutexAutoLock locker(mutex_);
|
|
||||||
DCHECK_EQ(counts_.size(), other.counts_.size());
|
DCHECK_EQ(counts_.size(), other.counts_.size());
|
||||||
sum_ += other.sum_;
|
sum_ += other.sum_;
|
||||||
redundant_count_ += other.redundant_count_;
|
redundant_count_ += other.redundant_count_;
|
||||||
|
@ -874,8 +851,7 @@ FlagHistogram::Accumulate(Sample value, Count count, size_t index)
|
||||||
|
|
||||||
void
|
void
|
||||||
FlagHistogram::AddSampleSet(const SampleSet& sample) {
|
FlagHistogram::AddSampleSet(const SampleSet& sample) {
|
||||||
OffTheBooksMutexAutoLock locker(sample.mutex());
|
DCHECK_EQ(bucket_count(), sample.size());
|
||||||
DCHECK_EQ(bucket_count(), sample.size(locker));
|
|
||||||
// We can't be sure the SampleSet provided came from another FlagHistogram,
|
// We can't be sure the SampleSet provided came from another FlagHistogram,
|
||||||
// so we take the following steps:
|
// so we take the following steps:
|
||||||
// - If our flag has already been set do nothing.
|
// - If our flag has already been set do nothing.
|
||||||
|
@ -889,12 +865,12 @@ FlagHistogram::AddSampleSet(const SampleSet& sample) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample.sum(locker) != 1) {
|
if (sample.sum() != 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t one_index = BucketIndex(1);
|
size_t one_index = BucketIndex(1);
|
||||||
if (sample.counts(locker, one_index) == 1) {
|
if (sample.counts(one_index) == 1) {
|
||||||
Accumulate(1, 1, one_index);
|
Accumulate(1, 1, one_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -946,20 +922,18 @@ CountHistogram::Accumulate(Sample value, Count count, size_t index)
|
||||||
|
|
||||||
void
|
void
|
||||||
CountHistogram::AddSampleSet(const SampleSet& sample) {
|
CountHistogram::AddSampleSet(const SampleSet& sample) {
|
||||||
OffTheBooksMutexAutoLock locker(sample.mutex());
|
DCHECK_EQ(bucket_count(), sample.size());
|
||||||
DCHECK_EQ(bucket_count(), sample.size(locker));
|
|
||||||
// We can't be sure the SampleSet provided came from another CountHistogram,
|
// We can't be sure the SampleSet provided came from another CountHistogram,
|
||||||
// so we at least check that the unused buckets are empty.
|
// so we at least check that the unused buckets are empty.
|
||||||
|
|
||||||
const size_t indices[] = { BucketIndex(0), BucketIndex(1), BucketIndex(2) };
|
const size_t indices[] = { BucketIndex(0), BucketIndex(1), BucketIndex(2) };
|
||||||
|
|
||||||
if (sample.counts(locker, indices[1]) != 0 ||
|
if (sample.counts(indices[1]) != 0 || sample.counts(indices[2]) != 0) {
|
||||||
sample.counts(locker, indices[2]) != 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample.counts(locker, indices[0]) != 0) {
|
if (sample.counts(indices[0]) != 0) {
|
||||||
Accumulate(1, sample.counts(locker, indices[0]), indices[0]);
|
Accumulate(1, sample.counts(indices[0]), indices[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
|
|
||||||
#include "mozilla/Atomics.h"
|
#include "mozilla/Atomics.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
#include "mozilla/Mutex.h"
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -56,9 +55,6 @@
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
using mozilla::OffTheBooksMutex;
|
|
||||||
using mozilla::OffTheBooksMutexAutoLock;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Provide easy general purpose histogram in a macro, just like stats counters.
|
// Provide easy general purpose histogram in a macro, just like stats counters.
|
||||||
// The first four macros use 50 buckets.
|
// The first four macros use 50 buckets.
|
||||||
|
@ -327,20 +323,8 @@ class Histogram {
|
||||||
explicit SampleSet();
|
explicit SampleSet();
|
||||||
~SampleSet();
|
~SampleSet();
|
||||||
|
|
||||||
// This class contains a mozilla::OffTheBooksMutex, |mutex_|.
|
// None of the methods in this class are thread-safe. Callers
|
||||||
// Most of the methods are thread-safe: they acquire and release
|
// must deal with locking themselves.
|
||||||
// the mutex themselves. A few are not thread-safe, and require
|
|
||||||
// the caller to provide evidence that the object is locked, by
|
|
||||||
// supplying a const OffTheBooksMutexAutoLock& parameter. The
|
|
||||||
// parameter is ignored but must be present. |mutex_| must be an
|
|
||||||
// OffTheBooks variant because some of the containing SampleSet
|
|
||||||
// objects are leaked until shutdown, so a standard Mutex can't be
|
|
||||||
// used, since that does leak checking, and causes test failures.
|
|
||||||
|
|
||||||
//---------------- THREAD SAFE METHODS ----------------//
|
|
||||||
//
|
|
||||||
// The caller must not already hold |this.mutex_|, otherwise we
|
|
||||||
// will end up deadlocking.
|
|
||||||
|
|
||||||
// Adjust size of counts_ for use with given histogram.
|
// Adjust size of counts_ for use with given histogram.
|
||||||
void Resize(const Histogram& histogram);
|
void Resize(const Histogram& histogram);
|
||||||
|
@ -353,38 +337,20 @@ class Histogram {
|
||||||
|
|
||||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||||
|
|
||||||
//---------------- THREAD UNSAFE METHODS ----------------//
|
Count counts(size_t i) const {
|
||||||
//
|
|
||||||
// The caller must hold |this.mutex_|, and must supply evidence by passing
|
|
||||||
// a const reference to the relevant OffTheBooksMutexAutoLock used.
|
|
||||||
|
|
||||||
Count counts(const OffTheBooksMutexAutoLock& ev, size_t i) const {
|
|
||||||
return counts_[i];
|
return counts_[i];
|
||||||
}
|
}
|
||||||
Count TotalCount(const OffTheBooksMutexAutoLock& ev) const;
|
Count TotalCount() const;
|
||||||
int64_t sum(const OffTheBooksMutexAutoLock& ev) const {
|
int64_t sum() const {
|
||||||
return sum_;
|
return sum_;
|
||||||
}
|
}
|
||||||
int64_t redundant_count(const OffTheBooksMutexAutoLock& ev) const {
|
int64_t redundant_count() const {
|
||||||
return redundant_count_;
|
return redundant_count_;
|
||||||
}
|
}
|
||||||
size_t size(const OffTheBooksMutexAutoLock& ev) const {
|
size_t size() const {
|
||||||
return counts_.size();
|
return counts_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// An assignment operator. The presence of mozilla::OffTheBooksMutex
|
|
||||||
// in this class causes the default assignment operator to be deleted.
|
|
||||||
const SampleSet& operator=(const SampleSet& other) {
|
|
||||||
counts_ = other.counts_;
|
|
||||||
sum_ = other.sum_;
|
|
||||||
redundant_count_ = other.redundant_count_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void Accumulate(const OffTheBooksMutexAutoLock& ev,
|
|
||||||
Sample value, Count count, size_t index);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Actual histogram data is stored in buckets, showing the count of values
|
// Actual histogram data is stored in buckets, showing the count of values
|
||||||
// that fit into each bucket.
|
// that fit into each bucket.
|
||||||
|
@ -402,13 +368,6 @@ class Histogram {
|
||||||
// and also the snapshotting code may asynchronously get a mismatch (though
|
// and also the snapshotting code may asynchronously get a mismatch (though
|
||||||
// generally either race based mismatch cause is VERY rare).
|
// generally either race based mismatch cause is VERY rare).
|
||||||
int64_t redundant_count_;
|
int64_t redundant_count_;
|
||||||
|
|
||||||
private:
|
|
||||||
// Protects all data fields.
|
|
||||||
mutable OffTheBooksMutex mutex_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
OffTheBooksMutex& mutex() const { return mutex_; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -466,9 +425,7 @@ class Histogram {
|
||||||
// produce a false-alarm if a race occurred in the reading of the data during
|
// produce a false-alarm if a race occurred in the reading of the data during
|
||||||
// a SnapShot process, but should otherwise be false at all times (unless we
|
// a SnapShot process, but should otherwise be false at all times (unless we
|
||||||
// have memory over-writes, or DRAM failures).
|
// have memory over-writes, or DRAM failures).
|
||||||
virtual Inconsistencies FindCorruption(const SampleSet& snapshot,
|
virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const;
|
||||||
const OffTheBooksMutexAutoLock&
|
|
||||||
snapshotLockEvidence) const;
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// Accessors for factory constuction, serialization and testing.
|
// Accessors for factory constuction, serialization and testing.
|
||||||
|
@ -483,7 +440,7 @@ class Histogram {
|
||||||
|
|
||||||
// Do a safe atomic snapshot of sample data. The caller is assumed to
|
// Do a safe atomic snapshot of sample data. The caller is assumed to
|
||||||
// have exclusive access to the destination, |*sample|, and no locking
|
// have exclusive access to the destination, |*sample|, and no locking
|
||||||
// of it is done here. This routine does lock the source sample though.
|
// of it is done here.
|
||||||
virtual void SnapshotSample(SampleSet* sample) const;
|
virtual void SnapshotSample(SampleSet* sample) const;
|
||||||
|
|
||||||
virtual bool HasConstructorArguments(Sample minimum, Sample maximum,
|
virtual bool HasConstructorArguments(Sample minimum, Sample maximum,
|
||||||
|
@ -559,13 +516,10 @@ class Histogram {
|
||||||
// Helpers for emitting Ascii graphic. Each method appends data to output.
|
// Helpers for emitting Ascii graphic. Each method appends data to output.
|
||||||
|
|
||||||
// Find out how large the (graphically) the largest bucket will appear to be.
|
// Find out how large the (graphically) the largest bucket will appear to be.
|
||||||
double GetPeakBucketSize(const SampleSet& snapshot,
|
double GetPeakBucketSize(const SampleSet& snapshot) const;
|
||||||
const OffTheBooksMutexAutoLock&
|
|
||||||
snapshotLockEvidence) const;
|
|
||||||
|
|
||||||
// Write a common header message describing this histogram.
|
// Write a common header message describing this histogram.
|
||||||
void WriteAsciiHeader(const SampleSet& snapshot,
|
void WriteAsciiHeader(const SampleSet& snapshot,
|
||||||
const OffTheBooksMutexAutoLock& snapshotLockEvidence,
|
|
||||||
Count sample_count, std::string* output) const;
|
Count sample_count, std::string* output) const;
|
||||||
|
|
||||||
// Write information about previous, current, and next buckets.
|
// Write information about previous, current, and next buckets.
|
||||||
|
|
|
@ -276,8 +276,7 @@ BackgroundChildImpl::DeallocPUDPSocketChild(PUDPSocketChild* child)
|
||||||
dom::PBroadcastChannelChild*
|
dom::PBroadcastChannelChild*
|
||||||
BackgroundChildImpl::AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
|
BackgroundChildImpl::AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
|
||||||
const nsCString& aOrigin,
|
const nsCString& aOrigin,
|
||||||
const nsString& aChannel,
|
const nsString& aChannel)
|
||||||
const bool& aPrivateBrowsing)
|
|
||||||
{
|
{
|
||||||
RefPtr<dom::BroadcastChannelChild> agent =
|
RefPtr<dom::BroadcastChannelChild> agent =
|
||||||
new dom::BroadcastChannelChild(aOrigin);
|
new dom::BroadcastChannelChild(aOrigin);
|
||||||
|
|
|
@ -104,8 +104,7 @@ protected:
|
||||||
virtual PBroadcastChannelChild*
|
virtual PBroadcastChannelChild*
|
||||||
AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
|
AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
|
||||||
const nsCString& aOrigin,
|
const nsCString& aOrigin,
|
||||||
const nsString& aChannel,
|
const nsString& aChannel) override;
|
||||||
const bool& aPrivateBrowsing) override;
|
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
DeallocPBroadcastChannelChild(PBroadcastChannelChild* aActor) override;
|
DeallocPBroadcastChannelChild(PBroadcastChannelChild* aActor) override;
|
||||||
|
|
|
@ -467,8 +467,7 @@ mozilla::dom::PBroadcastChannelParent*
|
||||||
BackgroundParentImpl::AllocPBroadcastChannelParent(
|
BackgroundParentImpl::AllocPBroadcastChannelParent(
|
||||||
const PrincipalInfo& aPrincipalInfo,
|
const PrincipalInfo& aPrincipalInfo,
|
||||||
const nsCString& aOrigin,
|
const nsCString& aOrigin,
|
||||||
const nsString& aChannel,
|
const nsString& aChannel)
|
||||||
const bool& aPrivateBrowsing)
|
|
||||||
{
|
{
|
||||||
AssertIsInMainProcess();
|
AssertIsInMainProcess();
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
@ -476,15 +475,11 @@ BackgroundParentImpl::AllocPBroadcastChannelParent(
|
||||||
nsString originChannelKey;
|
nsString originChannelKey;
|
||||||
|
|
||||||
// The format of originChannelKey is:
|
// The format of originChannelKey is:
|
||||||
// <channelName>|pb={true,false}|<origin+OriginAttributes>
|
// <channelName>|<origin+OriginAttributes>
|
||||||
|
|
||||||
originChannelKey.Assign(aChannel);
|
originChannelKey.Assign(aChannel);
|
||||||
|
|
||||||
if (aPrivateBrowsing) {
|
originChannelKey.AppendLiteral("|");
|
||||||
originChannelKey.AppendLiteral("|pb=true|");
|
|
||||||
} else {
|
|
||||||
originChannelKey.AppendLiteral("|pb=false|");
|
|
||||||
}
|
|
||||||
|
|
||||||
originChannelKey.Append(NS_ConvertUTF8toUTF16(aOrigin));
|
originChannelKey.Append(NS_ConvertUTF8toUTF16(aOrigin));
|
||||||
|
|
||||||
|
@ -648,8 +643,7 @@ BackgroundParentImpl::RecvPBroadcastChannelConstructor(
|
||||||
PBroadcastChannelParent* actor,
|
PBroadcastChannelParent* actor,
|
||||||
const PrincipalInfo& aPrincipalInfo,
|
const PrincipalInfo& aPrincipalInfo,
|
||||||
const nsCString& aOrigin,
|
const nsCString& aOrigin,
|
||||||
const nsString& aChannel,
|
const nsString& aChannel)
|
||||||
const bool& aPrivateBrowsing)
|
|
||||||
{
|
{
|
||||||
AssertIsInMainProcess();
|
AssertIsInMainProcess();
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
|
@ -90,15 +90,13 @@ protected:
|
||||||
virtual PBroadcastChannelParent*
|
virtual PBroadcastChannelParent*
|
||||||
AllocPBroadcastChannelParent(const PrincipalInfo& aPrincipalInfo,
|
AllocPBroadcastChannelParent(const PrincipalInfo& aPrincipalInfo,
|
||||||
const nsCString& aOrigin,
|
const nsCString& aOrigin,
|
||||||
const nsString& aChannel,
|
const nsString& aChannel) override;
|
||||||
const bool& aPrivateBrowsing) override;
|
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
|
RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
|
||||||
const PrincipalInfo& aPrincipalInfo,
|
const PrincipalInfo& aPrincipalInfo,
|
||||||
const nsCString& origin,
|
const nsCString& origin,
|
||||||
const nsString& channel,
|
const nsString& channel) override;
|
||||||
const bool& aPrivateBrowsing) override;
|
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
|
DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
|
||||||
|
|
|
@ -79,8 +79,7 @@ parent:
|
||||||
async PCameras();
|
async PCameras();
|
||||||
|
|
||||||
async PUDPSocket(OptionalPrincipalInfo pInfo, nsCString filter);
|
async PUDPSocket(OptionalPrincipalInfo pInfo, nsCString filter);
|
||||||
async PBroadcastChannel(PrincipalInfo pInfo, nsCString origin, nsString channel,
|
async PBroadcastChannel(PrincipalInfo pInfo, nsCString origin, nsString channel);
|
||||||
bool privateBrowsing);
|
|
||||||
|
|
||||||
async PServiceWorkerManager();
|
async PServiceWorkerManager();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
g = newGlobal();
|
||||||
|
g.parent = this;
|
||||||
|
g.eval("(" + function() {
|
||||||
|
Debugger(parent).onExceptionUnwind = function(frame) {
|
||||||
|
frame.older
|
||||||
|
}
|
||||||
|
} + ")()")
|
||||||
|
function check_one(expected, f, err) {
|
||||||
|
try {
|
||||||
|
f()
|
||||||
|
} catch (ex) {
|
||||||
|
s = ex.toString()
|
||||||
|
assertEq(s.slice(11, -err.length), expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ieval = eval
|
||||||
|
function check(expr, expected = expr) {
|
||||||
|
var end, err
|
||||||
|
for ([end, err] of[[".random_prop", " is undefined" ]])
|
||||||
|
statement = "o = {};" + expr + end;
|
||||||
|
cases = [
|
||||||
|
function() ieval("var undef;" + statement),
|
||||||
|
Function(statement)
|
||||||
|
]
|
||||||
|
for (f of cases)
|
||||||
|
check_one(expected, f, err)
|
||||||
|
}
|
||||||
|
check("undef");
|
||||||
|
check("o.b");
|
|
@ -7,6 +7,8 @@
|
||||||
#ifndef jit_RematerializedFrame_h
|
#ifndef jit_RematerializedFrame_h
|
||||||
#define jit_RematerializedFrame_h
|
#define jit_RematerializedFrame_h
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "jsfun.h"
|
#include "jsfun.h"
|
||||||
|
|
||||||
#include "jit/JitFrameIterator.h"
|
#include "jit/JitFrameIterator.h"
|
||||||
|
@ -180,12 +182,15 @@ class RematerializedFrame
|
||||||
unsigned numActualArgs() const {
|
unsigned numActualArgs() const {
|
||||||
return numActualArgs_;
|
return numActualArgs_;
|
||||||
}
|
}
|
||||||
|
unsigned numArgSlots() const {
|
||||||
|
return (std::max)(numFormalArgs(), numActualArgs());
|
||||||
|
}
|
||||||
|
|
||||||
Value* argv() {
|
Value* argv() {
|
||||||
return slots_;
|
return slots_;
|
||||||
}
|
}
|
||||||
Value* locals() {
|
Value* locals() {
|
||||||
return slots_ + numActualArgs_ + isConstructing_;
|
return slots_ + numArgSlots() + isConstructing_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value& unaliasedLocal(unsigned i) {
|
Value& unaliasedLocal(unsigned i) {
|
||||||
|
|
|
@ -458,14 +458,27 @@ ShellInterruptCallback(JSContext* cx)
|
||||||
|
|
||||||
bool result;
|
bool result;
|
||||||
if (sr->haveInterruptFunc) {
|
if (sr->haveInterruptFunc) {
|
||||||
|
bool wasAlreadyThrowing = cx->isExceptionPending();
|
||||||
JS::AutoSaveExceptionState savedExc(cx);
|
JS::AutoSaveExceptionState savedExc(cx);
|
||||||
JSAutoCompartment ac(cx, &sr->interruptFunc.toObject());
|
JSAutoCompartment ac(cx, &sr->interruptFunc.toObject());
|
||||||
RootedValue rval(cx);
|
RootedValue rval(cx);
|
||||||
if (!JS_CallFunctionValue(cx, nullptr, sr->interruptFunc,
|
|
||||||
JS::HandleValueArray::empty(), &rval))
|
// Report any exceptions thrown by the JS interrupt callback, but do
|
||||||
|
// *not* keep it on the cx. The interrupt handler is invoked at points
|
||||||
|
// that are not expected to throw catchable exceptions, like at
|
||||||
|
// JSOP_RETRVAL.
|
||||||
|
//
|
||||||
|
// If the interrupted JS code was already throwing, any exceptions
|
||||||
|
// thrown by the interrupt handler are silently swallowed.
|
||||||
{
|
{
|
||||||
return false;
|
Maybe<AutoReportException> are;
|
||||||
|
if (!wasAlreadyThrowing)
|
||||||
|
are.emplace(cx);
|
||||||
|
result = JS_CallFunctionValue(cx, nullptr, sr->interruptFunc,
|
||||||
|
JS::HandleValueArray::empty(), &rval);
|
||||||
}
|
}
|
||||||
|
savedExc.restore();
|
||||||
|
|
||||||
if (rval.isBoolean())
|
if (rval.isBoolean())
|
||||||
result = rval.toBoolean();
|
result = rval.toBoolean();
|
||||||
else
|
else
|
||||||
|
|