merge mozilla-inbound to mozilla-central a=merge
|
@ -271,7 +271,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
if (text.mString.IsEmpty()) {
|
||||
#ifdef A11Y_LOG
|
||||
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("content", textNode);
|
||||
logging::MsgEnd();
|
||||
|
@ -285,7 +285,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
// Update text of the accessible and fire text change events.
|
||||
#ifdef A11Y_LOG
|
||||
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("content", textNode);
|
||||
logging::MsgEntry("old text '%s'",
|
||||
|
@ -304,7 +304,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
if (!text.mString.IsEmpty()) {
|
||||
#ifdef A11Y_LOG
|
||||
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("content", textNode);
|
||||
logging::MsgEnd();
|
||||
|
|
|
@ -558,9 +558,10 @@ nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
|
|||
nsIContent* aStartChild,
|
||||
nsIContent* aEndChild)
|
||||
{
|
||||
DocAccessible* document = GetDocAccessible(aPresShell);
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree)) {
|
||||
logging::MsgBegin("TREE", "content inserted");
|
||||
logging::MsgBegin("TREE", "content inserted; doc: %p", document);
|
||||
logging::Node("container", aContainer);
|
||||
for (nsIContent* child = aStartChild; child != aEndChild;
|
||||
child = child->GetNextSibling()) {
|
||||
|
@ -571,25 +572,25 @@ nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
|
|||
}
|
||||
#endif
|
||||
|
||||
DocAccessible* docAccessible = GetDocAccessible(aPresShell);
|
||||
if (docAccessible)
|
||||
docAccessible->ContentInserted(aContainer, aStartChild, aEndChild);
|
||||
if (document) {
|
||||
document->ContentInserted(aContainer, aStartChild, aEndChild);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
|
||||
nsIContent* aChildNode)
|
||||
{
|
||||
DocAccessible* document = GetDocAccessible(aPresShell);
|
||||
#ifdef A11Y_LOG
|
||||
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("content node", aChildNode);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
DocAccessible* document = GetDocAccessible(aPresShell);
|
||||
if (document) {
|
||||
// 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
|
||||
|
|
|
@ -1814,6 +1814,15 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer, nsIContent* aNode)
|
|||
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);
|
||||
if (aContainer->IsAcceptableChild(aNode) && walker.Seek(aNode)) {
|
||||
Accessible* child = GetAccessible(aNode);
|
||||
|
@ -1831,6 +1840,10 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer, nsIContent* aNode)
|
|||
FireEventsOnInsertion(aContainer);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("children after insertion", logging::eVerbose, aContainer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -138,7 +138,7 @@ GetAppIni(int argc, const char *argv[])
|
|||
|
||||
char appEnv[MAXPATHLEN];
|
||||
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
||||
if (putenv(appEnv)) {
|
||||
if (putenv(strdup(appEnv))) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ static int do_main(int argc, char* argv[])
|
|||
|
||||
char appEnv[MAXPATHLEN];
|
||||
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
||||
if (putenv(appEnv)) {
|
||||
if (putenv(strdup(appEnv))) {
|
||||
Output("Couldn't set %s.\n", appEnv);
|
||||
return 255;
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ static int do_main(int argc, char* argv[], char* envp[], nsIFile *xreDirectory)
|
|||
|
||||
char appEnv[MAXPATHLEN];
|
||||
snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
|
||||
if (putenv(appEnv)) {
|
||||
if (putenv(strdup(appEnv))) {
|
||||
Output("Couldn't set %s.\n", appEnv);
|
||||
return 255;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ LOCAL_INCLUDES += [
|
|||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
OS_LIBS += [
|
||||
'esent',
|
||||
'netapi32',
|
||||
'ole32',
|
||||
'shell32',
|
||||
'shlwapi',
|
||||
|
@ -32,6 +33,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||
]
|
||||
DELAYLOAD_DLLS += [
|
||||
'esent.dll',
|
||||
'netapi32.dll',
|
||||
]
|
||||
|
||||
# Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code)
|
||||
|
|
|
@ -873,6 +873,7 @@ BrowserGlue.prototype = {
|
|||
label: win.gNavigatorBundle.getString("pendingCrashReports.viewAll"),
|
||||
callback: function() {
|
||||
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',
|
||||
'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:libGL.so
|
||||
leak:libglib-2.0.so
|
||||
leak:libglsl.so
|
||||
leak:libp11-kit.so
|
||||
leak:libpixman-1.so
|
||||
leak:libpulse.so
|
||||
|
|
|
@ -1436,7 +1436,7 @@ nsScriptSecurityManager::InitStatics()
|
|||
RefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
|
||||
nsresult rv = ssManager->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_CRASH();
|
||||
MOZ_CRASH("ssManager->Init() failed");
|
||||
}
|
||||
|
||||
ClearOnShutdown(&gScriptSecMan);
|
||||
|
|
|
@ -18,8 +18,6 @@ add_task(function* () {
|
|||
|
||||
reload(target);
|
||||
|
||||
let destroyed = getN(gAudioNodes, "remove", 10);
|
||||
|
||||
let [created] = yield Promise.all([
|
||||
getNSpread(gAudioNodes, "add", 13),
|
||||
waitForGraphRendered(panelWin, 13, 2)
|
||||
|
@ -32,8 +30,10 @@ add_task(function* () {
|
|||
// Click a soon-to-be dead buffer node
|
||||
yield clickGraphNode(panelWin, actorIDs[5]);
|
||||
|
||||
let destroyed = getN(gAudioNodes, "remove", 10);
|
||||
|
||||
// Force a CC in the child process to collect the orphaned nodes.
|
||||
forceCC();
|
||||
forceNodeCollection();
|
||||
|
||||
// Wait for destruction and graph to re-render
|
||||
yield Promise.all([destroyed, waitForGraphRendered(panelWin, 3, 2)]);
|
||||
|
|
|
@ -8,20 +8,18 @@
|
|||
add_task(function* () {
|
||||
let { target, front } = yield initBackend(DESTROY_NODES_URL);
|
||||
|
||||
let waitUntilDestroyed = getN(front, "destroy-node", 10);
|
||||
let [, , created] = yield Promise.all([
|
||||
front.setup({ reload: true }),
|
||||
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)
|
||||
]);
|
||||
|
||||
// Wait for a tick before gc to prevent this test from intermittent timeout
|
||||
// where the node never get collected.
|
||||
yield DevToolsUtils.waitForTick();
|
||||
let waitUntilDestroyed = getN(front, "destroy-node", 10);
|
||||
|
||||
// Force CC so we can ensure it's run to clear out dead AudioNodes
|
||||
forceCC();
|
||||
forceNodeCollection();
|
||||
|
||||
let destroyed = yield waitUntilDestroyed;
|
||||
|
||||
|
|
|
@ -12,13 +12,17 @@
|
|||
|
||||
<script type="text/javascript;version=1.8">
|
||||
"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 () {
|
||||
let ctx = new AudioContext();
|
||||
let osc = ctx.createOscillator();
|
||||
let gain = ctx.createGain();
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.createBufferSource();
|
||||
keepAlive.push(ctx.createBufferSource());
|
||||
}
|
||||
|
||||
osc.connect(gain);
|
||||
|
|
|
@ -420,8 +420,12 @@ function countGraphObjects(win) {
|
|||
/**
|
||||
* Forces cycle collection and GC, used in AudioNode destruction tests.
|
||||
*/
|
||||
function forceCC() {
|
||||
function forceNodeCollection() {
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
|
||||
// Kill the reference keeping stuff alive.
|
||||
content.wrappedJSObject.keepAlive = null;
|
||||
|
||||
// Collect the now-deceased nodes.
|
||||
Cu.forceGC();
|
||||
Cu.forceCC();
|
||||
Cu.forceGC();
|
||||
|
|
|
@ -957,9 +957,6 @@ BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
|||
std::stable_sort(aEntries.begin(), aEntries.end(),
|
||||
&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]
|
||||
// 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,
|
||||
// since we need to retain the very first and very last value so they can
|
||||
// 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;
|
||||
AnimationProperty* animationProperty = nullptr;
|
||||
|
||||
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
|
||||
// generate a segment.
|
||||
size_t j;
|
||||
|
@ -988,23 +1028,24 @@ BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
|||
// We need to generate an initial zero-length segment.
|
||||
MOZ_ASSERT(aEntries[i].mProperty == aEntries[i + 1].mProperty);
|
||||
j = i + 1;
|
||||
while (aEntries[j + 1].mOffset == 0.0f) {
|
||||
MOZ_ASSERT(aEntries[j].mProperty == aEntries[j + 1].mProperty);
|
||||
while (aEntries[j + 1].mOffset == 0.0f &&
|
||||
aEntries[j + 1].mProperty == aEntries[j].mProperty) {
|
||||
++j;
|
||||
}
|
||||
} 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.
|
||||
MOZ_ASSERT(aEntries[i].mProperty == aEntries[i].mProperty);
|
||||
j = i + 1;
|
||||
while (j + 1 < n && aEntries[j + 1].mOffset == 1.0f) {
|
||||
MOZ_ASSERT(aEntries[j].mProperty == aEntries[j + 1].mProperty);
|
||||
while (j + 1 < n &&
|
||||
aEntries[j + 1].mOffset == 1.0f &&
|
||||
aEntries[j + 1].mProperty == aEntries[j].mProperty) {
|
||||
++j;
|
||||
}
|
||||
} else {
|
||||
// New property.
|
||||
MOZ_ASSERT(aEntries[i + 1].mOffset == 0.0f);
|
||||
MOZ_ASSERT(aEntries[i].mProperty != aEntries[i + 1].mProperty);
|
||||
animationProperty = nullptr;
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
@ -1020,6 +1061,7 @@ BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
|||
// to insert segments into.
|
||||
if (aEntries[i].mProperty != lastProperty) {
|
||||
MOZ_ASSERT(aEntries[i].mOffset == 0.0f);
|
||||
MOZ_ASSERT(!animationProperty);
|
||||
animationProperty = aResult.AppendElement();
|
||||
animationProperty->mProperty = aEntries[i].mProperty;
|
||||
lastProperty = aEntries[i].mProperty;
|
||||
|
|
|
@ -112,8 +112,11 @@ enum {
|
|||
ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS |
|
||||
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
|
||||
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
|
||||
|
|
|
@ -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_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_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:
|
||||
InitializeRunnable(WorkerPrivate* aWorkerPrivate, nsACString& aOrigin,
|
||||
PrincipalInfo& aPrincipalInfo, bool& aPrivateBrowsing,
|
||||
ErrorResult& aRv)
|
||||
PrincipalInfo& aPrincipalInfo, ErrorResult& aRv)
|
||||
: WorkerMainThreadRunnable(aWorkerPrivate,
|
||||
NS_LITERAL_CSTRING("BroadcastChannel :: Initialize"))
|
||||
, mWorkerPrivate(GetCurrentThreadWorkerPrivate())
|
||||
, mOrigin(aOrigin)
|
||||
, mPrincipalInfo(aPrincipalInfo)
|
||||
, mPrivateBrowsing(aPrivateBrowsing)
|
||||
, mRv(aRv)
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
|
@ -127,11 +125,6 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
if (doc) {
|
||||
mPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -139,7 +132,6 @@ private:
|
|||
WorkerPrivate* mWorkerPrivate;
|
||||
nsACString& mOrigin;
|
||||
PrincipalInfo& mPrincipalInfo;
|
||||
bool& mPrivateBrowsing;
|
||||
ErrorResult& mRv;
|
||||
};
|
||||
|
||||
|
@ -306,14 +298,12 @@ private:
|
|||
BroadcastChannel::BroadcastChannel(nsPIDOMWindowInner* aWindow,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const nsACString& aOrigin,
|
||||
const nsAString& aChannel,
|
||||
bool aPrivateBrowsing)
|
||||
const nsAString& aChannel)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mWorkerFeature(nullptr)
|
||||
, mPrincipalInfo(new PrincipalInfo(aPrincipalInfo))
|
||||
, mOrigin(aOrigin)
|
||||
, mChannel(aChannel)
|
||||
, mPrivateBrowsing(aPrivateBrowsing)
|
||||
, mIsKeptAlive(false)
|
||||
, mInnerID(0)
|
||||
, mState(StateActive)
|
||||
|
@ -344,7 +334,6 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal,
|
|||
|
||||
nsAutoCString origin;
|
||||
PrincipalInfo principalInfo;
|
||||
bool privateBrowsing = false;
|
||||
WorkerPrivate* workerPrivate = nullptr;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
|
@ -381,19 +370,13 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal,
|
|||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
if (doc) {
|
||||
privateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
|
||||
}
|
||||
} else {
|
||||
JSContext* cx = aGlobal.Context();
|
||||
workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<InitializeRunnable> runnable =
|
||||
new InitializeRunnable(workerPrivate, origin, principalInfo,
|
||||
privateBrowsing, aRv);
|
||||
new InitializeRunnable(workerPrivate, origin, principalInfo, aRv);
|
||||
runnable->Dispatch(aRv);
|
||||
}
|
||||
|
||||
|
@ -402,8 +385,7 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal,
|
|||
}
|
||||
|
||||
RefPtr<BroadcastChannel> bc =
|
||||
new BroadcastChannel(window, principalInfo, origin, aChannel,
|
||||
privateBrowsing);
|
||||
new BroadcastChannel(window, principalInfo, origin, aChannel);
|
||||
|
||||
// Register this component to PBackground.
|
||||
PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
|
||||
|
@ -522,8 +504,7 @@ BroadcastChannel::ActorCreated(PBackgroundChild* aActor)
|
|||
}
|
||||
|
||||
PBroadcastChannelChild* actor =
|
||||
aActor->SendPBroadcastChannelConstructor(*mPrincipalInfo, mOrigin, mChannel,
|
||||
mPrivateBrowsing);
|
||||
aActor->SendPBroadcastChannelConstructor(*mPrincipalInfo, mOrigin, mChannel);
|
||||
|
||||
mActor = static_cast<BroadcastChannelChild*>(actor);
|
||||
MOZ_ASSERT(mActor);
|
||||
|
|
|
@ -88,8 +88,7 @@ private:
|
|||
BroadcastChannel(nsPIDOMWindowInner* aWindow,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const nsACString& aOrigin,
|
||||
const nsAString& aChannel,
|
||||
bool aPrivateBrowsing);
|
||||
const nsAString& aChannel);
|
||||
|
||||
~BroadcastChannel();
|
||||
|
||||
|
@ -116,7 +115,6 @@ private:
|
|||
|
||||
nsCString mOrigin;
|
||||
nsString mChannel;
|
||||
bool mPrivateBrowsing;
|
||||
|
||||
bool mIsKeptAlive;
|
||||
|
||||
|
|
|
@ -220,7 +220,8 @@ static_assert(int(RequestCache::Default) == 0 &&
|
|||
int(RequestCache::Reload) == 2 &&
|
||||
int(RequestCache::No_cache) == 3 &&
|
||||
int(RequestCache::Force_cache) == 4 &&
|
||||
int(RequestCache::EndGuard_) == 5,
|
||||
int(RequestCache::Only_if_cached) == 5 &&
|
||||
int(RequestCache::EndGuard_) == 6,
|
||||
"RequestCache values are as expected");
|
||||
static_assert(int(RequestRedirect::Follow) == 0 &&
|
||||
int(RequestRedirect::Error) == 1 &&
|
||||
|
|
|
@ -172,7 +172,7 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
|
|||
case WebGLExtensionID::EXT_frag_depth:
|
||||
return WebGLExtensionFragDepth::IsSupported(this);
|
||||
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:
|
||||
return WebGLExtensionSRGB::IsSupported(this);
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
// iframe with the right preference setting so that the
|
||||
// createImageBitmap() will be visible.
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['canvas.imagebitmap_extensions.enabled', true]
|
||||
['canvas.imagebitmap_extensions.enabled', true],
|
||||
['gfx.ycbcr.accurate-conversion', true]
|
||||
]}, function() {
|
||||
var div = document.getElementById("content");
|
||||
ok(div, "Parent exists");
|
||||
|
|
|
@ -21,7 +21,7 @@ fail-if = (os == 'android')
|
|||
[ensure-exts/test_EXT_sRGB.html]
|
||||
fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6') || (os == 'win')
|
||||
[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]
|
||||
fail-if = (os == 'android') || (os == 'linux')
|
||||
[ensure-exts/test_OES_standard_derivatives.html]
|
||||
|
|
|
@ -267,10 +267,13 @@ TextComposition::DispatchCompositionEvent(
|
|||
aCompositionEvent->mRanges = nullptr;
|
||||
NS_ASSERTION(aCompositionEvent->mData.IsEmpty(),
|
||||
"mData of eCompositionCommitAsIs should be empty string");
|
||||
if (mLastData == IDEOGRAPHIC_SPACE) {
|
||||
// If the last data is an ideographic space (FullWidth space), it must be
|
||||
bool removePlaceholderCharacter =
|
||||
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
|
||||
// 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();
|
||||
} else {
|
||||
aCompositionEvent->mData = mLastData;
|
||||
|
|
|
@ -428,6 +428,14 @@ Request::Constructor(const GlobalObject& aGlobal,
|
|||
RequestCache cache = aInit.mCache.WasPassed() ?
|
||||
aInit.mCache.Value() : fallbackCache;
|
||||
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->SetCacheMode(cache);
|
||||
}
|
||||
|
|
|
@ -52,8 +52,7 @@ class nsGenericHTMLElement : public nsGenericHTMLElementBase,
|
|||
{
|
||||
public:
|
||||
explicit nsGenericHTMLElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElementBase(aNodeInfo),
|
||||
mScrollgrab(false)
|
||||
: nsGenericHTMLElementBase(aNodeInfo)
|
||||
{
|
||||
NS_ASSERTION(mNodeInfo->NamespaceID() == kNameSpaceID_XHTML,
|
||||
"Unexpected namespace");
|
||||
|
@ -194,11 +193,15 @@ public:
|
|||
}
|
||||
bool Scrollgrab() const
|
||||
{
|
||||
return mScrollgrab;
|
||||
return HasFlag(ELEMENT_HAS_SCROLLGRAB);
|
||||
}
|
||||
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);
|
||||
|
@ -1167,8 +1170,6 @@ protected:
|
|||
|
||||
private:
|
||||
void ChangeEditableState(int32_t aChange);
|
||||
|
||||
bool mScrollgrab;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
|
|
|
@ -1037,18 +1037,19 @@ ContentChild::InitXPCOM()
|
|||
if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
|
||||
NS_WARNING("Couldn't register console listener for child process");
|
||||
|
||||
bool isOffline, isLangRTL;
|
||||
bool isOffline, isLangRTL, haveBidiKeyboards;
|
||||
bool isConnected;
|
||||
ClipboardCapabilities clipboardCaps;
|
||||
DomainPolicyClone domainPolicy;
|
||||
StructuredCloneData initialData;
|
||||
|
||||
SendGetXPCOMProcessAttributes(&isOffline, &isConnected,
|
||||
&isLangRTL, &mAvailableDictionaries,
|
||||
&isLangRTL, &haveBidiKeyboards,
|
||||
&mAvailableDictionaries,
|
||||
&clipboardCaps, &domainPolicy, &initialData);
|
||||
RecvSetOffline(isOffline);
|
||||
RecvSetConnectivity(isConnected);
|
||||
RecvBidiKeyboardNotify(isLangRTL);
|
||||
RecvBidiKeyboardNotify(isLangRTL, haveBidiKeyboards);
|
||||
|
||||
// Create the CPOW manager as soon as possible.
|
||||
SendPJavaScriptConstructor();
|
||||
|
@ -1511,13 +1512,14 @@ ContentChild::RecvSpeakerManagerNotify()
|
|||
}
|
||||
|
||||
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
|
||||
// possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
|
||||
PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
|
||||
if (bidi) {
|
||||
bidi->SetIsLangRTL(aIsLangRTL);
|
||||
bidi->SetBidiKeyboardInfo(aIsLangRTL, aHaveBidiKeyboards);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -3075,6 +3077,24 @@ ContentChild::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure)
|
|||
bool
|
||||
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) {
|
||||
mPolicy->Deactivate();
|
||||
mPolicy = nullptr;
|
||||
|
|
|
@ -401,7 +401,8 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -3388,6 +3388,7 @@ bool
|
|||
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||
bool* aIsConnected,
|
||||
bool* aIsLangRTL,
|
||||
bool* aHaveBidiKeyboards,
|
||||
InfallibleTArray<nsString>* dictionaries,
|
||||
ClipboardCapabilities* clipboardCaps,
|
||||
DomainPolicyClone* domainPolicy,
|
||||
|
@ -3404,8 +3405,10 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
|||
nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
|
||||
|
||||
*aIsLangRTL = false;
|
||||
*aHaveBidiKeyboards = false;
|
||||
if (bidi) {
|
||||
bidi->IsLangRTL(aIsLangRTL);
|
||||
bidi->GetHaveBidiKeyboards(aHaveBidiKeyboards);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
|
||||
|
|
|
@ -707,6 +707,7 @@ private:
|
|||
RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||
bool* aIsConnected,
|
||||
bool* aIsLangRTL,
|
||||
bool* aHaveBidiKeyboards,
|
||||
InfallibleTArray<nsString>* dictionaries,
|
||||
ClipboardCapabilities* clipboardCaps,
|
||||
DomainPolicyClone* domainPolicy,
|
||||
|
|
|
@ -494,7 +494,7 @@ child:
|
|||
* Communication between the PuppetBidiKeyboard and the actual
|
||||
* 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.
|
||||
|
@ -702,7 +702,8 @@ parent:
|
|||
sync GetProcessAttributes()
|
||||
returns (ContentParentId cpId, bool isForApp, bool isForBrowser);
|
||||
sync GetXPCOMProcessAttributes()
|
||||
returns (bool isOffline, bool isConnected, bool isLangRTL, nsString[] dictionaries,
|
||||
returns (bool isOffline, bool isConnected, bool isLangRTL,
|
||||
bool haveBidiKeyboards, nsString[] dictionaries,
|
||||
ClipboardCapabilities clipboardCaps,
|
||||
DomainPolicyClone domainPolicy,
|
||||
StructuredCloneData initialData);
|
||||
|
|
|
@ -340,6 +340,27 @@ TabParent::CacheFrameLoader(nsFrameLoader* 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
|
||||
TabParent::SetOwnerElement(Element* aElement)
|
||||
{
|
||||
|
|
|
@ -143,6 +143,7 @@ public:
|
|||
uint32_t aChromeFlags);
|
||||
|
||||
Element* GetOwnerElement() const { return mFrameElement; }
|
||||
already_AddRefed<nsPIDOMWindowOuter> GetParentWindowOuter();
|
||||
|
||||
void SetOwnerElement(Element* aElement);
|
||||
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
#include "MediaResource.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaPrefs.h"
|
||||
|
||||
#include "nsPrintfCString.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
|
||||
using namespace mozilla::media;
|
||||
|
||||
|
@ -62,6 +65,154 @@ public:
|
|||
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)
|
||||
: mAudioCompactor(mAudioQueue)
|
||||
, mDecoder(aDecoder)
|
||||
|
@ -75,6 +226,8 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
|||
, mShutdown(false)
|
||||
, mAudioDiscontinuity(false)
|
||||
, mVideoDiscontinuity(false)
|
||||
, mIsSuspended(mTaskQueue, true,
|
||||
"MediaDecoderReader::mIsSuspended (Canonical)")
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaDecoderReader);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -84,6 +237,8 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
|||
mTaskQueue, this, &MediaDecoderReader::NotifyDataArrived);
|
||||
}
|
||||
|
||||
ReaderQueue::Instance().Add(this);
|
||||
|
||||
// Dispatch initialization that needs to happen on that task queue.
|
||||
mTaskQueue->Dispatch(NewRunnableMethod(this, &MediaDecoderReader::InitializationTask));
|
||||
}
|
||||
|
@ -368,6 +523,7 @@ MediaDecoderReader::Shutdown()
|
|||
ReleaseMediaResources();
|
||||
mDuration.DisconnectIfConnected();
|
||||
mBuffered.DisconnectAll();
|
||||
mIsSuspended.DisconnectAll();
|
||||
|
||||
// Shut down the watch manager before shutting down our task queue.
|
||||
mWatchManager.Shutdown();
|
||||
|
@ -376,6 +532,8 @@ MediaDecoderReader::Shutdown()
|
|||
|
||||
mDecoder = nullptr;
|
||||
|
||||
ReaderQueue::Instance().Remove(this);
|
||||
|
||||
return mTaskQueue->BeginShutdown();
|
||||
}
|
||||
|
||||
|
|
|
@ -290,6 +290,22 @@ public:
|
|||
// Notified by the OggReader during playback when chained ogg is detected.
|
||||
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:
|
||||
virtual ~MediaDecoderReader();
|
||||
|
||||
|
@ -435,6 +451,7 @@ private:
|
|||
// "discontinuity" in the stream. For example after a seek.
|
||||
bool mAudioDiscontinuity;
|
||||
bool mVideoDiscontinuity;
|
||||
Canonical<bool> mIsSuspended;
|
||||
|
||||
MediaEventListener mDataArrivedListener;
|
||||
};
|
||||
|
|
|
@ -415,6 +415,9 @@ public:
|
|||
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() {
|
||||
return mReader->CanonicalBuffered();
|
||||
}
|
||||
AbstractCanonical<bool>* CanonicalIsSuspended() {
|
||||
return mReader->CanonicalIsSuspended();
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); }
|
||||
|
|
|
@ -262,6 +262,8 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||
mAudioOffloading(false),
|
||||
mBuffered(mTaskQueue, TimeIntervals(),
|
||||
"MediaDecoderStateMachine::mBuffered (Mirror)"),
|
||||
mIsReaderSuspended(mTaskQueue, true,
|
||||
"MediaDecoderStateMachine::mIsReaderSuspended (Mirror)"),
|
||||
mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
|
||||
"MediaDecoderStateMachine::mEstimatedDuration (Mirror)"),
|
||||
mExplicitDuration(mTaskQueue, Maybe<double>(),
|
||||
|
@ -339,6 +341,7 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
|
|||
|
||||
// Connect mirrors.
|
||||
mBuffered.Connect(mReader->CanonicalBuffered());
|
||||
mIsReaderSuspended.Connect(mReader->CanonicalIsSuspended());
|
||||
mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration());
|
||||
mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
|
||||
mPlayState.Connect(aDecoder->CanonicalPlayState());
|
||||
|
@ -357,6 +360,7 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
|
|||
|
||||
// Initialize watchers.
|
||||
mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
|
||||
mWatchManager.Watch(mIsReaderSuspended, &MediaDecoderStateMachine::ReaderSuspendedChanged);
|
||||
mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||
mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||
mWatchManager.Watch(mVideoCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||
|
@ -1342,8 +1346,9 @@ void MediaDecoderStateMachine::PlayStateChanged()
|
|||
void MediaDecoderStateMachine::VisibilityChanged()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
DECODER_LOG("VisibilityChanged: is visible = %d, video decode suspended = %d",
|
||||
mIsVisible.Ref(), mVideoDecodeSuspended);
|
||||
DECODER_LOG("VisibilityChanged: is visible = %d, video decode suspended = %d, "
|
||||
"reader suspended = %d",
|
||||
mIsVisible.Ref(), mVideoDecodeSuspended, mIsReaderSuspended.Ref());
|
||||
|
||||
// Not suspending background videos so there's nothing to do.
|
||||
if (!MediaPrefs::MDSMSuspendBackgroundVideoEnabled()) {
|
||||
|
@ -1378,6 +1383,10 @@ void MediaDecoderStateMachine::VisibilityChanged()
|
|||
if (mVideoDecodeSuspended) {
|
||||
mVideoDecodeSuspended = false;
|
||||
|
||||
if (mIsReaderSuspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If an existing seek is in flight don't bother creating a new
|
||||
// one to catch up.
|
||||
if (mSeekTask || mQueuedSeek.Exists()) {
|
||||
|
@ -1385,23 +1394,31 @@ void MediaDecoderStateMachine::VisibilityChanged()
|
|||
}
|
||||
|
||||
// Start video-only seek to the current time...
|
||||
InitiateVideoDecodeRecoverySeek();
|
||||
InitiateDecodeRecoverySeek(TrackSet(TrackInfo::kVideoTrack));
|
||||
}
|
||||
}
|
||||
|
||||
// InitiateVideoDecodeRecoverySeek is responsible for setting up a video-only
|
||||
// seek using the seek task. When suspension of decoding for videos that are in
|
||||
// InitiateDecodeRecoverySeek is responsible for setting up a seek using the
|
||||
// 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
|
||||
// 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
|
||||
// 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());
|
||||
|
||||
DECODER_LOG("InitiateDecodeRecoverySeek");
|
||||
|
||||
SeekJob seekJob;
|
||||
seekJob.mTarget = SeekTarget(GetMediaTime(),
|
||||
SeekTarget::Type::AccurateVideoOnly,
|
||||
SeekTarget::Type seekTargetType = aTracks.contains(TrackInfo::kAudioTrack)
|
||||
? SeekTarget::Type::Accurate
|
||||
: SeekTarget::Type::AccurateVideoOnly;
|
||||
seekJob.mTarget = SeekTarget(GetMediaTime(), seekTargetType,
|
||||
MediaDecoderEventVisibility::Suppressed);
|
||||
|
||||
SetState(DECODER_STATE_SEEKING);
|
||||
|
@ -1423,7 +1440,7 @@ void MediaDecoderStateMachine::InitiateVideoDecodeRecoverySeek()
|
|||
|
||||
// Reset our state machine and decoding pipeline before seeking.
|
||||
if (mSeekTask->NeedToResetMDSM()) {
|
||||
Reset(TrackInfo::kVideoTrack);
|
||||
Reset(aTracks);
|
||||
}
|
||||
|
||||
// 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
|
||||
MediaDecoderStateMachine::ReadMetadata()
|
||||
{
|
||||
|
@ -2222,6 +2252,7 @@ MediaDecoderStateMachine::FinishShutdown()
|
|||
|
||||
// Disconnect canonicals and mirrors before shutting down our task queue.
|
||||
mBuffered.DisconnectIfConnected();
|
||||
mIsReaderSuspended.DisconnectIfConnected();
|
||||
mEstimatedDuration.DisconnectIfConnected();
|
||||
mExplicitDuration.DisconnectIfConnected();
|
||||
mPlayState.DisconnectIfConnected();
|
||||
|
@ -2636,7 +2667,8 @@ bool MediaDecoderStateMachine::IsStateMachineScheduled() const
|
|||
bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return MediaPrefs::MDSMSuspendBackgroundVideoEnabled() && mVideoDecodeSuspended;
|
||||
return (MediaPrefs::MDSMSuspendBackgroundVideoEnabled() && mVideoDecodeSuspended) ||
|
||||
mIsReaderSuspended;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -381,6 +381,8 @@ protected:
|
|||
|
||||
void BufferedRangeUpdated();
|
||||
|
||||
void ReaderSuspendedChanged();
|
||||
|
||||
// Inserts MediaData* samples into their respective MediaQueues.
|
||||
// aSample must not be null.
|
||||
|
||||
|
@ -518,10 +520,11 @@ protected:
|
|||
// The decoder monitor must be held.
|
||||
void InitiateSeek(SeekJob aSeekJob);
|
||||
|
||||
// Clears any previous seeking state and initiates a video-only seek on the
|
||||
// decoder to catch up the video to the current audio position, when recovering
|
||||
// from video decoding being suspended in background.
|
||||
void InitiateVideoDecodeRecoverySeek();
|
||||
// Clears any previous seeking state and initiates a seek on the decoder to
|
||||
// resync the video and audio positions, when recovering from video decoding
|
||||
// being suspended in background or from audio and video decoding being
|
||||
// suspended due to the decoder limit.
|
||||
void InitiateDecodeRecoverySeek(TrackSet aTracks);
|
||||
|
||||
nsresult DispatchAudioDecodeTaskIfNeeded();
|
||||
|
||||
|
@ -975,6 +978,8 @@ private:
|
|||
// The buffered range. Mirrored from the decoder thread.
|
||||
Mirror<media::TimeIntervals> mBuffered;
|
||||
|
||||
Mirror<bool> mIsReaderSuspended;
|
||||
|
||||
// The duration according to the demuxer's current estimate, mirrored from the main thread.
|
||||
Mirror<media::NullableTimeUnit> mEstimatedDuration;
|
||||
|
||||
|
|
|
@ -135,7 +135,6 @@ MediaFormatReader::Shutdown()
|
|||
MOZ_ASSERT(!mVideo.HasPromise());
|
||||
|
||||
mDemuxer = nullptr;
|
||||
|
||||
mPlatform = nullptr;
|
||||
|
||||
return MediaDecoderReader::Shutdown();
|
||||
|
@ -459,6 +458,11 @@ MediaFormatReader::EnsureDecoderInitialized(TrackType aTrack)
|
|||
[self] (TrackType aTrack) {
|
||||
auto& decoder = self->GetDecoderData(aTrack);
|
||||
decoder.mInitPromise.Complete();
|
||||
|
||||
if (self->IsSuspended()) {
|
||||
return;
|
||||
}
|
||||
|
||||
decoder.mDecoderInitialized = true;
|
||||
MonitorAutoLock mon(decoder.mMonitor);
|
||||
decoder.mDescription = decoder.mDecoder->GetDescriptionName();
|
||||
|
@ -533,6 +537,10 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
|
|||
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
||||
}
|
||||
|
||||
if (IsSuspended()) {
|
||||
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
||||
}
|
||||
|
||||
media::TimeUnit timeThreshold{media::TimeUnit::FromMicroseconds(aTimeThreshold)};
|
||||
// Ensure we have no pending seek going as ShouldSkip could return out of date
|
||||
// information.
|
||||
|
@ -624,6 +632,10 @@ MediaFormatReader::RequestAudioData()
|
|||
return MediaDataPromise::CreateAndReject(DECODE_ERROR, __func__);
|
||||
}
|
||||
|
||||
if (IsSuspended()) {
|
||||
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
||||
}
|
||||
|
||||
if (IsSeeking()) {
|
||||
LOG("called mid-seek. Rejecting.");
|
||||
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
|
||||
|
@ -918,6 +930,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
|
|||
AbstractMediaDecoder::AutoNotifyDecoded& aA)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
|
||||
if (decoder.mQueuedSamples.IsEmpty()) {
|
||||
|
@ -1884,6 +1897,9 @@ void MediaFormatReader::ReleaseMediaResources()
|
|||
}
|
||||
mVideo.mInitPromise.DisconnectIfExists();
|
||||
mVideo.ShutdownDecoder();
|
||||
|
||||
mAudio.mInitPromise.DisconnectIfExists();
|
||||
mAudio.ShutdownDecoder();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -101,6 +101,7 @@ public:
|
|||
void GetMozDebugReaderData(nsAString& aString);
|
||||
|
||||
private:
|
||||
|
||||
bool HasVideo() { return mVideo.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.num-decode-threads", MediaThreadPoolDefaultCount, uint32_t, 4);
|
||||
DECL_MEDIA_PREF("media.decoder.limit", MediaDecoderLimit, uint32_t, -1);
|
||||
|
||||
public:
|
||||
// Manage the singleton:
|
||||
|
|
|
@ -59,6 +59,6 @@ enum RequestContext {
|
|||
|
||||
enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
|
||||
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 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.");
|
||||
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == static_cast<uint32_t>(RequestCache::Force_cache),
|
||||
"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.");
|
||||
|
||||
static StaticRefPtr<ServiceWorkerManager> gInstance;
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#include "nsTArray.h"
|
||||
#include "nscore.h"
|
||||
|
||||
// Workaround for windows headers
|
||||
#ifdef SetProp
|
||||
#undef SetProp
|
||||
#endif
|
||||
|
||||
class nsIAtom;
|
||||
class nsIDOMNode;
|
||||
namespace mozilla {
|
||||
|
|
|
@ -498,25 +498,11 @@ nsEditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
|
|||
}
|
||||
|
||||
#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 {
|
||||
|
||||
// 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
|
||||
// (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
|
||||
|
@ -525,16 +511,19 @@ bool IsCtrlShiftPressed(bool& isRTL)
|
|||
// keys pressed at the same time.
|
||||
// To ignore the keys checked in 1, we set their status to 0 before
|
||||
// checking the key status.
|
||||
const int kKeyDownMask = 0x80;
|
||||
if ((keystate[VK_CONTROL] & kKeyDownMask) == 0) {
|
||||
WidgetKeyboardEvent* keyboardEvent =
|
||||
aEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
|
||||
MOZ_ASSERT(keyboardEvent,
|
||||
"DOM key event's internal event must be WidgetKeyboardEvent");
|
||||
|
||||
if (!keyboardEvent->IsControl()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keystate[VK_RSHIFT] & kKeyDownMask) {
|
||||
keystate[VK_RSHIFT] = 0;
|
||||
uint32_t location = keyboardEvent->mLocation;
|
||||
if (location == nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT) {
|
||||
isRTL = true;
|
||||
} else if (keystate[VK_LSHIFT] & kKeyDownMask) {
|
||||
keystate[VK_LSHIFT] = 0;
|
||||
} else if (location == nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT) {
|
||||
isRTL = false;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -542,19 +531,10 @@ bool IsCtrlShiftPressed(bool& isRTL)
|
|||
|
||||
// Scan the key status to find pressed keys. We should abandon changing the
|
||||
// text direction when there are other pressed keys.
|
||||
// This code is executed only when a user is pressing a control key and a
|
||||
// right-shift key (or a left-shift key), i.e. we should ignore the status of
|
||||
// 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;
|
||||
}
|
||||
if (keyboardEvent->IsAlt() || keyboardEvent->IsOS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -598,7 +578,7 @@ nsEditorEventListener::KeyDown(nsIDOMKeyEvent* aKeyEvent)
|
|||
aKeyEvent->GetKeyCode(&keyCode);
|
||||
if (keyCode == nsIDOMKeyEvent::DOM_VK_SHIFT) {
|
||||
bool switchToRTL;
|
||||
if (IsCtrlShiftPressed(switchToRTL)) {
|
||||
if (IsCtrlShiftPressed(aKeyEvent, switchToRTL)) {
|
||||
mShouldSwitchTextDirection = true;
|
||||
mSwitchToRTL = switchToRTL;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,11 @@
|
|||
#include "nsWSRunObject.h"
|
||||
#include <algorithm>
|
||||
|
||||
// Workaround for windows headers
|
||||
#ifdef SetProp
|
||||
#undef SetProp
|
||||
#endif
|
||||
|
||||
class nsISupports;
|
||||
class nsRulesInfo;
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ static const char* const sExtensionNames[] = {
|
|||
"GL_ARB_robustness",
|
||||
"GL_ARB_sampler_objects",
|
||||
"GL_ARB_seamless_cube_map",
|
||||
"GL_ARB_shader_texture_lod",
|
||||
"GL_ARB_sync",
|
||||
"GL_ARB_texture_compression",
|
||||
"GL_ARB_texture_float",
|
||||
|
|
|
@ -128,6 +128,7 @@ enum class GLFeature {
|
|||
sRGB_texture,
|
||||
sampler_objects,
|
||||
seamless_cube_map_opt_in,
|
||||
shader_texture_lod,
|
||||
split_framebuffer,
|
||||
standard_derivatives,
|
||||
sync,
|
||||
|
@ -423,6 +424,7 @@ public:
|
|||
ARB_robustness,
|
||||
ARB_sampler_objects,
|
||||
ARB_seamless_cube_map,
|
||||
ARB_shader_texture_lod,
|
||||
ARB_sync,
|
||||
ARB_texture_compression,
|
||||
ARB_texture_float,
|
||||
|
|
|
@ -567,6 +567,17 @@ static const FeatureInfo sFeatureInfoArr[] = {
|
|||
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?
|
||||
"split_framebuffer",
|
||||
|
|
|
@ -169,6 +169,12 @@ GLXLibrary::EnsureInitialized()
|
|||
{ nullptr, { nullptr } }
|
||||
};
|
||||
|
||||
GLLibraryLoader::SymLoadStruct symbols_videosync[] = {
|
||||
{ (PRFuncPtr*) &xGetVideoSyncInternal, { "glXGetVideoSyncSGI", nullptr } },
|
||||
{ (PRFuncPtr*) &xWaitVideoSyncInternal, { "glXWaitVideoSyncSGI", nullptr } },
|
||||
{ nullptr, { nullptr } }
|
||||
};
|
||||
|
||||
if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &symbols[0])) {
|
||||
NS_WARNING("Couldn't find required entry point in OpenGL shared library");
|
||||
return false;
|
||||
|
@ -246,6 +252,13 @@ GLXLibrary::EnsureInitialized()
|
|||
mHasRobustness = true;
|
||||
}
|
||||
|
||||
if (HasExtension(extensionsStr, "GLX_SGI_video_sync") &&
|
||||
GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_videosync,
|
||||
(GLLibraryLoader::PlatformLookupFunction)&xGetProcAddress))
|
||||
{
|
||||
mHasVideoSync = true;
|
||||
}
|
||||
|
||||
mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI");
|
||||
mIsNVIDIA = serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation");
|
||||
mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa");
|
||||
|
@ -269,6 +282,16 @@ GLXLibrary::SupportsTextureFromPixmap(gfxASurface* aSurface)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GLXLibrary::SupportsVideoSync()
|
||||
{
|
||||
if (!EnsureInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mHasVideoSync;
|
||||
}
|
||||
|
||||
GLXPixmap
|
||||
GLXLibrary::CreatePixmap(gfxASurface* aSurface)
|
||||
{
|
||||
|
@ -737,6 +760,24 @@ GLXLibrary::xCreateContextAttribs(Display* display,
|
|||
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>
|
||||
GLContextGLX::CreateGLContext(
|
||||
const SurfaceCaps& caps,
|
||||
|
|
|
@ -54,9 +54,12 @@ public:
|
|||
, xWaitGLInternal(nullptr)
|
||||
, xWaitXInternal(nullptr)
|
||||
, xCreateContextAttribsInternal(nullptr)
|
||||
, xGetVideoSyncInternal(nullptr)
|
||||
, xWaitVideoSyncInternal(nullptr)
|
||||
, mInitialized(false), mTriedInitializing(false)
|
||||
, mUseTextureFromPixmap(false), mDebug(false)
|
||||
, mHasRobustness(false), mHasCreateContextAttribs(false)
|
||||
, mHasVideoSync(false)
|
||||
, mIsATI(false), mIsNVIDIA(false)
|
||||
, mClientIsMesa(false), mGLXMajorVersion(0)
|
||||
, mGLXMinorVersion(0)
|
||||
|
@ -120,6 +123,9 @@ public:
|
|||
Bool direct,
|
||||
const int* attrib_list);
|
||||
|
||||
int xGetVideoSync(unsigned int* count);
|
||||
int xWaitVideoSync(int divisor, int remainder, unsigned int* count);
|
||||
|
||||
bool EnsureInitialized();
|
||||
|
||||
GLXPixmap CreatePixmap(gfxASurface* aSurface);
|
||||
|
@ -132,6 +138,7 @@ public:
|
|||
bool HasRobustness() { return mHasRobustness; }
|
||||
bool HasCreateContextAttribs() { return mHasCreateContextAttribs; }
|
||||
bool SupportsTextureFromPixmap(gfxASurface* aSurface);
|
||||
bool SupportsVideoSync();
|
||||
bool IsATI() { return mIsATI; }
|
||||
bool GLXVersionCheck(int aMajor, int aMinor);
|
||||
|
||||
|
@ -225,6 +232,12 @@ private:
|
|||
const int *);
|
||||
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
|
||||
void BeforeGLXCall();
|
||||
void AfterGLXCall();
|
||||
|
@ -236,6 +249,7 @@ private:
|
|||
bool mDebug;
|
||||
bool mHasRobustness;
|
||||
bool mHasCreateContextAttribs;
|
||||
bool mHasVideoSync;
|
||||
bool mIsATI;
|
||||
bool mIsNVIDIA;
|
||||
bool mClientIsMesa;
|
||||
|
|
|
@ -732,25 +732,6 @@ gfxContext::Mask(SourceSurface* aSurface, Float aAlpha, const Matrix& aTransform
|
|||
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
|
||||
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, 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());
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace mozilla {
|
|||
* between unquoted and quoted names for serializaiton
|
||||
*/
|
||||
|
||||
enum FontFamilyType {
|
||||
enum FontFamilyType : uint32_t {
|
||||
eFamily_none = 0, // used when finding generics
|
||||
|
||||
// explicitly named font family (e.g. Helvetica)
|
||||
|
|
|
@ -20,6 +20,13 @@
|
|||
#include "gfxUtils.h"
|
||||
#include "gfxFT2FontBase.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"
|
||||
|
||||
|
@ -34,6 +41,12 @@
|
|||
#include "mozilla/Preferences.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 */
|
||||
#if defined(__APPLE__) && defined(Status)
|
||||
#undef Status
|
||||
|
@ -583,3 +596,230 @@ gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *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;
|
||||
}
|
||||
|
||||
#ifdef GL_PROVIDER_GLX
|
||||
already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static gfxFontconfigUtils *sFontconfigUtils;
|
||||
|
||||
|
|
|
@ -273,6 +273,7 @@ private:
|
|||
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-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);
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
#include "nsPresContext.h"
|
||||
#include "nsRegion.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "yuv_convert.h"
|
||||
#include "ycbcr_to_rgb565.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "ImageRegion.h"
|
||||
|
|
|
@ -7,14 +7,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
// On Windows, protypes.h is #included, which defines these types. This sucks!
|
||||
#ifndef PROTYPES_H
|
||||
typedef uint8_t uint8;
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
#endif
|
||||
#include "libyuv/basic_types.h"
|
||||
|
||||
// From Chromium build_config.h:
|
||||
// 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/.
|
||||
|
||||
EXPORTS += [
|
||||
'chromium_types.h',
|
||||
'ycbcr_to_rgb565.h',
|
||||
'YCbCrUtils.h',
|
||||
'yuv_convert.h',
|
||||
'yuv_row.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -63,4 +59,6 @@ if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['HAVE_ARM_NEON']:
|
|||
'yuv_convert_arm.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += ['/media/libyuv/include']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include "yuv_convert.h"
|
||||
|
||||
#include "gfxPrefs.h"
|
||||
#include "libyuv.h"
|
||||
// Header for low level row functions.
|
||||
#include "yuv_row.h"
|
||||
#include "mozilla/SSE.h"
|
||||
|
@ -60,6 +62,72 @@ void ConvertYCbCrToRGB32(const uint8* y_buf,
|
|||
int uv_pitch,
|
||||
int rgb_pitch,
|
||||
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 x_shift = yuv_type == YV24 ? 0 : 1;
|
||||
// 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,
|
||||
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.
|
||||
// Supports rotation and mirroring.
|
||||
void ScaleYCbCrToRGB32(const uint8* yplane,
|
||||
|
|
|
@ -15,44 +15,10 @@ enum { GIF_TRAILER = 0x3B }; // ';'
|
|||
enum { GIF_IMAGE_SEPARATOR = 0x2C }; // ','
|
||||
enum { GIF_EXTENSION_INTRODUCER = 0x21 }; // '!'
|
||||
enum { GIF_GRAPHIC_CONTROL_LABEL = 0xF9 };
|
||||
enum { GIF_COMMENT_LABEL = 0xFE };
|
||||
enum { GIF_PLAIN_TEXT_LABEL = 0x01 };
|
||||
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
|
||||
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
|
||||
uint8_t* stackp; // Current stack pointer
|
||||
int datasize;
|
||||
|
@ -61,7 +27,6 @@ typedef struct gif_struct {
|
|||
int avail; // Index of next available slot in dictionary
|
||||
int oldcode;
|
||||
uint8_t firstchar;
|
||||
int count; // Remaining # bytes in sub-block
|
||||
int bits; // Number of unread bits in "datum"
|
||||
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
|
||||
int32_t screen_width; // Logical screen width & 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 loop_count; // Netscape specific extension block to control
|
||||
// the number of animation loops a GIF
|
||||
|
@ -89,7 +55,6 @@ typedef struct gif_struct {
|
|||
bool is_transparent; // TRUE, if tpixel is valid
|
||||
|
||||
uint16_t prefix[MAX_BITS]; // LZW decoding tables
|
||||
uint8_t* hold; // Accumulation buffer
|
||||
uint32_t global_colormap[MAX_COLORS]; // Default colormap if local not
|
||||
// supplied
|
||||
uint8_t suffix[MAX_BITS]; // LZW decoding tables
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "Decoder.h"
|
||||
#include "GIF2.h"
|
||||
#include "StreamingLexer.h"
|
||||
#include "SurfacePipe.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -54,25 +55,70 @@ private:
|
|||
/// Called when we finish decoding the entire image.
|
||||
void FlushImageData();
|
||||
|
||||
nsresult GifWrite(const uint8_t* buf, uint32_t numbytes);
|
||||
|
||||
/// Transforms a palette index into a pixel.
|
||||
template <typename PixelSize> PixelSize
|
||||
ColormapIndexToPixel(uint8_t aIndex);
|
||||
|
||||
/// A generator function that performs LZW decompression and yields pixels.
|
||||
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.
|
||||
bool DoLzw(const uint8_t* aData);
|
||||
/// Checks if we have transparency, either because the header indicates that
|
||||
/// 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,
|
||||
const uint8_t* buf2 = nullptr, uint32_t count2 = 0);
|
||||
bool CheckForTransparency(const gfx::IntRect& aFrameRect);
|
||||
gfx::IntRect ClampToImageRect(const gfx::IntRect& aFrameRect);
|
||||
// @return the clear code used for LZW decompression.
|
||||
int ClearCode() const { return 1 << mGIFStruct.datasize; }
|
||||
|
||||
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
|
||||
|
||||
|
@ -80,6 +126,11 @@ private:
|
|||
// of decoding it, and -1 otherwise.
|
||||
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
|
||||
bool mGIFOpen;
|
||||
bool mSawTransparency;
|
||||
|
|
|
@ -29,8 +29,6 @@ namespace base {
|
|||
typedef ::Lock Lock;
|
||||
typedef ::AutoLock AutoLock;
|
||||
|
||||
using mozilla::OffTheBooksMutexAutoLock;
|
||||
|
||||
// Static table of checksums for all possible 8 bit bytes.
|
||||
const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL,
|
||||
0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
|
||||
|
@ -177,24 +175,20 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
|
|||
SampleSet snapshot;
|
||||
SnapshotSample(&snapshot);
|
||||
|
||||
// For the rest of the routine, we hold |snapshot|'s lock so as to
|
||||
// be able to examine it atomically.
|
||||
OffTheBooksMutexAutoLock locker(snapshot.mutex());
|
||||
Count sample_count = snapshot.TotalCount();
|
||||
|
||||
Count sample_count = snapshot.TotalCount(locker);
|
||||
|
||||
WriteAsciiHeader(snapshot, locker, sample_count, output);
|
||||
WriteAsciiHeader(snapshot, sample_count, output);
|
||||
output->append(newline);
|
||||
|
||||
// Prepare to normalize graphical rendering of bucket contents.
|
||||
double max_size = 0;
|
||||
if (graph_it)
|
||||
max_size = GetPeakBucketSize(snapshot, locker);
|
||||
max_size = GetPeakBucketSize(snapshot);
|
||||
|
||||
// Calculate space needed to print bucket range numbers. Leave room to print
|
||||
// nearly the largest bucket range without sliding over the histogram.
|
||||
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)
|
||||
break; // All buckets are empty.
|
||||
--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.
|
||||
size_t print_width = 1;
|
||||
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;
|
||||
if (width > print_width)
|
||||
print_width = width;
|
||||
|
@ -214,7 +208,7 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
|
|||
int64_t past = 0;
|
||||
// Output the actual histogram graph.
|
||||
for (size_t i = 0; i < bucket_count(); ++i) {
|
||||
Count current = snapshot.counts(locker, i);
|
||||
Count current = snapshot.counts(i);
|
||||
if (!current && !PrintEmptyBucket(i))
|
||||
continue;
|
||||
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)
|
||||
output->push_back(' ');
|
||||
if (0 == current &&
|
||||
i < bucket_count() - 1 && 0 == snapshot.counts(locker, i + 1)) {
|
||||
while (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(i + 1))
|
||||
++i;
|
||||
output->append("... ");
|
||||
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.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Histogram::Inconsistencies Histogram::FindCorruption(
|
||||
const SampleSet& snapshot,
|
||||
const OffTheBooksMutexAutoLock& snapshotLockEvidence) const {
|
||||
Histogram::Inconsistencies
|
||||
Histogram::FindCorruption(const SampleSet& snapshot) const
|
||||
{
|
||||
int inconsistencies = NO_INCONSISTENCIES;
|
||||
Sample previous_range = -1; // Bottom range is always 0.
|
||||
int64_t count = 0;
|
||||
for (size_t index = 0; index < bucket_count(); ++index) {
|
||||
count += snapshot.counts(snapshotLockEvidence, index);
|
||||
count += snapshot.counts(index);
|
||||
int new_range = ranges(index);
|
||||
if (previous_range >= new_range)
|
||||
inconsistencies |= BUCKET_ORDER_ERROR;
|
||||
|
@ -261,7 +255,7 @@ Histogram::Inconsistencies Histogram::FindCorruption(
|
|||
if (!HasValidRangeChecksum())
|
||||
inconsistencies |= RANGE_CHECKSUM_ERROR;
|
||||
|
||||
int64_t delta64 = snapshot.redundant_count(snapshotLockEvidence) - count;
|
||||
int64_t delta64 = snapshot.redundant_count() - count;
|
||||
if (delta64 != 0) {
|
||||
int delta = static_cast<int>(delta64);
|
||||
if (delta != delta64)
|
||||
|
@ -302,7 +296,6 @@ size_t Histogram::bucket_count() const {
|
|||
}
|
||||
|
||||
void Histogram::SnapshotSample(SampleSet* sample) const {
|
||||
OffTheBooksMutexAutoLock locker(sample_.mutex());
|
||||
*sample = sample_;
|
||||
}
|
||||
|
||||
|
@ -336,9 +329,9 @@ size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
|||
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
|
||||
// is as close as we can get to measuring this array.
|
||||
return aMallocSizeOf(&counts_[0]);
|
||||
|
@ -556,13 +549,11 @@ uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) {
|
|||
//------------------------------------------------------------------------------
|
||||
// Private methods
|
||||
|
||||
double Histogram::GetPeakBucketSize(const SampleSet& snapshot,
|
||||
const OffTheBooksMutexAutoLock&
|
||||
snapshotLockEvidence) const {
|
||||
double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
|
||||
double max = 0;
|
||||
for (size_t i = 0; i < bucket_count() ; ++i) {
|
||||
double current_size
|
||||
= GetBucketSize(snapshot.counts(snapshotLockEvidence, i), i);
|
||||
= GetBucketSize(snapshot.counts(i), i);
|
||||
if (current_size > max)
|
||||
max = current_size;
|
||||
}
|
||||
|
@ -570,15 +561,13 @@ double Histogram::GetPeakBucketSize(const SampleSet& snapshot,
|
|||
}
|
||||
|
||||
void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
|
||||
const OffTheBooksMutexAutoLock&
|
||||
snapshotLockEvidence,
|
||||
Count sample_count,
|
||||
std::string* output) const {
|
||||
StringAppendF(output,
|
||||
"Histogram: %s recorded %d samples",
|
||||
histogram_name().c_str(),
|
||||
sample_count);
|
||||
int64_t snapshot_sum = snapshot.sum(snapshotLockEvidence);
|
||||
int64_t snapshot_sum = snapshot.sum();
|
||||
if (0 == sample_count) {
|
||||
DCHECK_EQ(snapshot_sum, 0);
|
||||
} else {
|
||||
|
@ -629,20 +618,17 @@ void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
|
|||
Histogram::SampleSet::SampleSet()
|
||||
: counts_(),
|
||||
sum_(0),
|
||||
redundant_count_(0),
|
||||
mutex_("Histogram::SampleSet::SampleSet") {
|
||||
redundant_count_(0) {
|
||||
}
|
||||
|
||||
Histogram::SampleSet::~SampleSet() {
|
||||
}
|
||||
|
||||
void Histogram::SampleSet::Resize(const Histogram& histogram) {
|
||||
OffTheBooksMutexAutoLock locker(mutex_);
|
||||
counts_.resize(histogram.bucket_count(), 0);
|
||||
}
|
||||
|
||||
void Histogram::SampleSet::Accumulate(const OffTheBooksMutexAutoLock& ev,
|
||||
Sample value, Count count,
|
||||
void Histogram::SampleSet::Accumulate(Sample value, Count count,
|
||||
size_t index) {
|
||||
DCHECK(count == 1 || count == -1);
|
||||
counts_[index] += count;
|
||||
|
@ -653,15 +639,7 @@ void Histogram::SampleSet::Accumulate(const OffTheBooksMutexAutoLock& ev,
|
|||
DCHECK_GE(redundant_count_, 0);
|
||||
}
|
||||
|
||||
void Histogram::SampleSet::Accumulate(Sample value,
|
||||
Count count,
|
||||
size_t index) {
|
||||
OffTheBooksMutexAutoLock locker(mutex_);
|
||||
Accumulate(locker, value, count, index);
|
||||
}
|
||||
|
||||
Count Histogram::SampleSet::TotalCount(const OffTheBooksMutexAutoLock& ev)
|
||||
const {
|
||||
Count Histogram::SampleSet::TotalCount() const {
|
||||
Count total = 0;
|
||||
for (Counts::const_iterator it = counts_.begin();
|
||||
it != counts_.end();
|
||||
|
@ -672,7 +650,6 @@ Count Histogram::SampleSet::TotalCount(const OffTheBooksMutexAutoLock& ev)
|
|||
}
|
||||
|
||||
void Histogram::SampleSet::Add(const SampleSet& other) {
|
||||
OffTheBooksMutexAutoLock locker(mutex_);
|
||||
DCHECK_EQ(counts_.size(), other.counts_.size());
|
||||
sum_ += other.sum_;
|
||||
redundant_count_ += other.redundant_count_;
|
||||
|
@ -874,8 +851,7 @@ FlagHistogram::Accumulate(Sample value, Count count, size_t index)
|
|||
|
||||
void
|
||||
FlagHistogram::AddSampleSet(const SampleSet& sample) {
|
||||
OffTheBooksMutexAutoLock locker(sample.mutex());
|
||||
DCHECK_EQ(bucket_count(), sample.size(locker));
|
||||
DCHECK_EQ(bucket_count(), sample.size());
|
||||
// We can't be sure the SampleSet provided came from another FlagHistogram,
|
||||
// so we take the following steps:
|
||||
// - If our flag has already been set do nothing.
|
||||
|
@ -889,12 +865,12 @@ FlagHistogram::AddSampleSet(const SampleSet& sample) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (sample.sum(locker) != 1) {
|
||||
if (sample.sum() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t one_index = BucketIndex(1);
|
||||
if (sample.counts(locker, one_index) == 1) {
|
||||
if (sample.counts(one_index) == 1) {
|
||||
Accumulate(1, 1, one_index);
|
||||
}
|
||||
}
|
||||
|
@ -946,20 +922,18 @@ CountHistogram::Accumulate(Sample value, Count count, size_t index)
|
|||
|
||||
void
|
||||
CountHistogram::AddSampleSet(const SampleSet& sample) {
|
||||
OffTheBooksMutexAutoLock locker(sample.mutex());
|
||||
DCHECK_EQ(bucket_count(), sample.size(locker));
|
||||
DCHECK_EQ(bucket_count(), sample.size());
|
||||
// We can't be sure the SampleSet provided came from another CountHistogram,
|
||||
// so we at least check that the unused buckets are empty.
|
||||
|
||||
const size_t indices[] = { BucketIndex(0), BucketIndex(1), BucketIndex(2) };
|
||||
|
||||
if (sample.counts(locker, indices[1]) != 0 ||
|
||||
sample.counts(locker, indices[2]) != 0) {
|
||||
if (sample.counts(indices[1]) != 0 || sample.counts(indices[2]) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sample.counts(locker, indices[0]) != 0) {
|
||||
Accumulate(1, sample.counts(locker, indices[0]), indices[0]);
|
||||
if (sample.counts(indices[0]) != 0) {
|
||||
Accumulate(1, sample.counts(indices[0]), indices[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
@ -56,9 +55,6 @@
|
|||
|
||||
namespace base {
|
||||
|
||||
using mozilla::OffTheBooksMutex;
|
||||
using mozilla::OffTheBooksMutexAutoLock;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Provide easy general purpose histogram in a macro, just like stats counters.
|
||||
// The first four macros use 50 buckets.
|
||||
|
@ -327,20 +323,8 @@ class Histogram {
|
|||
explicit SampleSet();
|
||||
~SampleSet();
|
||||
|
||||
// This class contains a mozilla::OffTheBooksMutex, |mutex_|.
|
||||
// Most of the methods are thread-safe: they acquire and release
|
||||
// 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.
|
||||
// None of the methods in this class are thread-safe. Callers
|
||||
// must deal with locking themselves.
|
||||
|
||||
// Adjust size of counts_ for use with given histogram.
|
||||
void Resize(const Histogram& histogram);
|
||||
|
@ -353,38 +337,20 @@ class Histogram {
|
|||
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
//---------------- THREAD UNSAFE METHODS ----------------//
|
||||
//
|
||||
// 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 {
|
||||
Count counts(size_t i) const {
|
||||
return counts_[i];
|
||||
}
|
||||
Count TotalCount(const OffTheBooksMutexAutoLock& ev) const;
|
||||
int64_t sum(const OffTheBooksMutexAutoLock& ev) const {
|
||||
Count TotalCount() const;
|
||||
int64_t sum() const {
|
||||
return sum_;
|
||||
}
|
||||
int64_t redundant_count(const OffTheBooksMutexAutoLock& ev) const {
|
||||
int64_t redundant_count() const {
|
||||
return redundant_count_;
|
||||
}
|
||||
size_t size(const OffTheBooksMutexAutoLock& ev) const {
|
||||
size_t size() const {
|
||||
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:
|
||||
// Actual histogram data is stored in buckets, showing the count of values
|
||||
// that fit into each bucket.
|
||||
|
@ -402,13 +368,6 @@ class Histogram {
|
|||
// and also the snapshotting code may asynchronously get a mismatch (though
|
||||
// generally either race based mismatch cause is VERY rare).
|
||||
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
|
||||
// a SnapShot process, but should otherwise be false at all times (unless we
|
||||
// have memory over-writes, or DRAM failures).
|
||||
virtual Inconsistencies FindCorruption(const SampleSet& snapshot,
|
||||
const OffTheBooksMutexAutoLock&
|
||||
snapshotLockEvidence) const;
|
||||
virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// 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
|
||||
// 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 bool HasConstructorArguments(Sample minimum, Sample maximum,
|
||||
|
@ -559,13 +516,10 @@ class Histogram {
|
|||
// Helpers for emitting Ascii graphic. Each method appends data to output.
|
||||
|
||||
// Find out how large the (graphically) the largest bucket will appear to be.
|
||||
double GetPeakBucketSize(const SampleSet& snapshot,
|
||||
const OffTheBooksMutexAutoLock&
|
||||
snapshotLockEvidence) const;
|
||||
double GetPeakBucketSize(const SampleSet& snapshot) const;
|
||||
|
||||
// Write a common header message describing this histogram.
|
||||
void WriteAsciiHeader(const SampleSet& snapshot,
|
||||
const OffTheBooksMutexAutoLock& snapshotLockEvidence,
|
||||
Count sample_count, std::string* output) const;
|
||||
|
||||
// Write information about previous, current, and next buckets.
|
||||
|
|
|
@ -276,8 +276,7 @@ BackgroundChildImpl::DeallocPUDPSocketChild(PUDPSocketChild* child)
|
|||
dom::PBroadcastChannelChild*
|
||||
BackgroundChildImpl::AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsCString& aOrigin,
|
||||
const nsString& aChannel,
|
||||
const bool& aPrivateBrowsing)
|
||||
const nsString& aChannel)
|
||||
{
|
||||
RefPtr<dom::BroadcastChannelChild> agent =
|
||||
new dom::BroadcastChannelChild(aOrigin);
|
||||
|
|
|
@ -104,8 +104,7 @@ protected:
|
|||
virtual PBroadcastChannelChild*
|
||||
AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsCString& aOrigin,
|
||||
const nsString& aChannel,
|
||||
const bool& aPrivateBrowsing) override;
|
||||
const nsString& aChannel) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPBroadcastChannelChild(PBroadcastChannelChild* aActor) override;
|
||||
|
|
|
@ -467,8 +467,7 @@ mozilla::dom::PBroadcastChannelParent*
|
|||
BackgroundParentImpl::AllocPBroadcastChannelParent(
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const nsCString& aOrigin,
|
||||
const nsString& aChannel,
|
||||
const bool& aPrivateBrowsing)
|
||||
const nsString& aChannel)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
@ -476,15 +475,11 @@ BackgroundParentImpl::AllocPBroadcastChannelParent(
|
|||
nsString originChannelKey;
|
||||
|
||||
// The format of originChannelKey is:
|
||||
// <channelName>|pb={true,false}|<origin+OriginAttributes>
|
||||
// <channelName>|<origin+OriginAttributes>
|
||||
|
||||
originChannelKey.Assign(aChannel);
|
||||
|
||||
if (aPrivateBrowsing) {
|
||||
originChannelKey.AppendLiteral("|pb=true|");
|
||||
} else {
|
||||
originChannelKey.AppendLiteral("|pb=false|");
|
||||
}
|
||||
originChannelKey.AppendLiteral("|");
|
||||
|
||||
originChannelKey.Append(NS_ConvertUTF8toUTF16(aOrigin));
|
||||
|
||||
|
@ -648,8 +643,7 @@ BackgroundParentImpl::RecvPBroadcastChannelConstructor(
|
|||
PBroadcastChannelParent* actor,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const nsCString& aOrigin,
|
||||
const nsString& aChannel,
|
||||
const bool& aPrivateBrowsing)
|
||||
const nsString& aChannel)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
|
|
@ -90,15 +90,13 @@ protected:
|
|||
virtual PBroadcastChannelParent*
|
||||
AllocPBroadcastChannelParent(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsCString& aOrigin,
|
||||
const nsString& aChannel,
|
||||
const bool& aPrivateBrowsing) override;
|
||||
const nsString& aChannel) override;
|
||||
|
||||
virtual bool
|
||||
RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const nsCString& origin,
|
||||
const nsString& channel,
|
||||
const bool& aPrivateBrowsing) override;
|
||||
const nsString& channel) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
|
||||
|
|
|
@ -79,8 +79,7 @@ parent:
|
|||
async PCameras();
|
||||
|
||||
async PUDPSocket(OptionalPrincipalInfo pInfo, nsCString filter);
|
||||
async PBroadcastChannel(PrincipalInfo pInfo, nsCString origin, nsString channel,
|
||||
bool privateBrowsing);
|
||||
async PBroadcastChannel(PrincipalInfo pInfo, nsCString origin, nsString channel);
|
||||
|
||||
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
|
||||
#define jit_RematerializedFrame_h
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "jsfun.h"
|
||||
|
||||
#include "jit/JitFrameIterator.h"
|
||||
|
@ -180,12 +182,15 @@ class RematerializedFrame
|
|||
unsigned numActualArgs() const {
|
||||
return numActualArgs_;
|
||||
}
|
||||
unsigned numArgSlots() const {
|
||||
return (std::max)(numFormalArgs(), numActualArgs());
|
||||
}
|
||||
|
||||
Value* argv() {
|
||||
return slots_;
|
||||
}
|
||||
Value* locals() {
|
||||
return slots_ + numActualArgs_ + isConstructing_;
|
||||
return slots_ + numArgSlots() + isConstructing_;
|
||||
}
|
||||
|
||||
Value& unaliasedLocal(unsigned i) {
|
||||
|
|
|
@ -458,14 +458,27 @@ ShellInterruptCallback(JSContext* cx)
|
|||
|
||||
bool result;
|
||||
if (sr->haveInterruptFunc) {
|
||||
bool wasAlreadyThrowing = cx->isExceptionPending();
|
||||
JS::AutoSaveExceptionState savedExc(cx);
|
||||
JSAutoCompartment ac(cx, &sr->interruptFunc.toObject());
|
||||
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())
|
||||
result = rval.toBoolean();
|
||||
else
|
||||
|
|