зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to m-i
This commit is contained in:
Коммит
cbb612338e
|
@ -226,7 +226,7 @@ let SocialUI = {
|
|||
return;
|
||||
}
|
||||
}
|
||||
Social.installProvider(targetDoc.location.href, data, function(manifest) {
|
||||
Social.installProvider(targetDoc, data, function(manifest) {
|
||||
this.doActivation(manifest.origin);
|
||||
}.bind(this));
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
|
||||
<emItems>
|
||||
<emItem blockID="s1" id="bad.com@services.mozilla.org"></emItem>
|
||||
<emItem blockID="s1" id="test1.example.com@services.mozilla.org"></emItem>
|
||||
</emItems>
|
||||
</blocklist>
|
||||
|
|
|
@ -17,10 +17,10 @@ let manifest = { // normal provider
|
|||
};
|
||||
let manifest2 = { // used for testing install
|
||||
name: "provider 2",
|
||||
origin: "https://example1.com",
|
||||
sidebarURL: "https://example1.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://example1.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://example1.com/browser/browser/base/content/test/moz.png"
|
||||
origin: "https://test1.example.com",
|
||||
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://test1.example.com/browser/browser/base/content/test/moz.png"
|
||||
};
|
||||
|
||||
function test() {
|
||||
|
@ -174,62 +174,73 @@ var tests = {
|
|||
|
||||
// we expect the addon install dialog to appear, we need to accept the
|
||||
// install from the dialog.
|
||||
let windowListener = {
|
||||
info("Waiting for install dialog");
|
||||
Services.wm.addListener({
|
||||
onWindowTitleChange: function() {},
|
||||
onCloseWindow: function() {},
|
||||
onOpenWindow: function(window) {
|
||||
var domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
onOpenWindow: function(xulwindow) {
|
||||
Services.wm.removeListener(this);
|
||||
var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
var self = this;
|
||||
waitForFocus(function() {
|
||||
self.windowReady(domwindow);
|
||||
}, domwindow);
|
||||
},
|
||||
windowReady: function(window) {
|
||||
if (window.document.location.href == XPINSTALL_URL) {
|
||||
info("Saw install dialog");
|
||||
is(domwindow.document.location.href, XPINSTALL_URL, "Should have seen the right window open");
|
||||
// Initially the accept button is disabled on a countdown timer
|
||||
var button = window.document.documentElement.getButton("accept");
|
||||
var button = domwindow.document.documentElement.getButton("accept");
|
||||
button.disabled = false;
|
||||
window.document.documentElement.acceptDialog();
|
||||
Services.wm.removeListener(windowListener);
|
||||
}
|
||||
domwindow.document.documentElement.acceptDialog();
|
||||
}, domwindow);
|
||||
}
|
||||
};
|
||||
Services.wm.addListener(windowListener);
|
||||
});
|
||||
|
||||
let installFrom = "https://example1.com";
|
||||
Services.prefs.setCharPref("social.whitelist", "");
|
||||
is(SocialService.getOriginActivationType(installFrom), "foreign", "testing foriegn install");
|
||||
Social.installProvider(installFrom, manifest2, function(addonManifest) {
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
let installFrom = doc.nodePrincipal.origin;
|
||||
Services.prefs.setCharPref("social.whitelist", "");
|
||||
is(SocialService.getOriginActivationType(installFrom), "foreign", "testing foriegn install");
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
testWhitelistInstall: function(next) {
|
||||
AddonManager.addAddonListener(installListener(next));
|
||||
|
||||
let installFrom = "https://example1.com";
|
||||
Services.prefs.setCharPref("social.whitelist", installFrom);
|
||||
is(SocialService.getOriginActivationType(installFrom), "whitelist", "testing whitelist install");
|
||||
Social.installProvider(installFrom, manifest2, function(addonManifest) {
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
let installFrom = doc.nodePrincipal.origin;
|
||||
Services.prefs.setCharPref("social.whitelist", installFrom);
|
||||
is(SocialService.getOriginActivationType(installFrom), "whitelist", "testing whitelist install");
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
testDirectoryInstall: function(next) {
|
||||
AddonManager.addAddonListener(installListener(next));
|
||||
|
||||
let installFrom = "https://addons.allizom.org";
|
||||
Services.prefs.setCharPref("social.directories", installFrom);
|
||||
is(SocialService.getOriginActivationType(installFrom), "directory", "testing directory install");
|
||||
Social.installProvider(installFrom, manifest2, function(addonManifest) {
|
||||
Services.prefs.clearUserPref("social.directories");
|
||||
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
let installFrom = doc.nodePrincipal.origin;
|
||||
Services.prefs.setCharPref("social.directories", installFrom);
|
||||
is(SocialService.getOriginActivationType(installFrom), "directory", "testing directory install");
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
Services.prefs.clearUserPref("social.directories");
|
||||
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,18 +11,18 @@ let blocklistURL = "http://test:80/browser/browser/base/content/test/social/bloc
|
|||
let blocklistEmpty = "http://test:80/browser/browser/base/content/test/social/blocklistEmpty.xml";
|
||||
|
||||
let manifest = { // normal provider
|
||||
name: "provider 1",
|
||||
name: "provider ok",
|
||||
origin: "https://example.com",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
|
||||
};
|
||||
let manifest_bad = { // normal provider
|
||||
name: "provider 1",
|
||||
origin: "https://bad.com",
|
||||
sidebarURL: "https://bad.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://bad.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://bad.com/browser/browser/base/content/test/moz.png"
|
||||
name: "provider blocked",
|
||||
origin: "https://test1.example.com",
|
||||
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://test1.example.com/browser/browser/base/content/test/moz.png"
|
||||
};
|
||||
|
||||
function test() {
|
||||
|
@ -40,10 +40,10 @@ var tests = {
|
|||
var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
|
||||
.getService(Components.interfaces.nsIBlocklistService);
|
||||
setAndUpdateBlocklist(blocklistURL, function() {
|
||||
ok(blocklist.isAddonBlocklisted("bad.com@services.mozilla.org", "0", "0", "0"), "blocking 'blocked'");
|
||||
ok(!blocklist.isAddonBlocklisted("good.cpm@services.mozilla.org", "0", "0", "0"), "not blocking 'good'");
|
||||
ok(blocklist.isAddonBlocklisted("test1.example.com@services.mozilla.org", "0", "0", "0"), "blocking 'blocked'");
|
||||
ok(!blocklist.isAddonBlocklisted("example.com@services.mozilla.org", "0", "0", "0"), "not blocking 'good'");
|
||||
setAndUpdateBlocklist(blocklistEmpty, function() {
|
||||
ok(!blocklist.isAddonBlocklisted("bad.com@services.mozilla.org", "0", "0", "0"), "blocklist cleared");
|
||||
ok(!blocklist.isAddonBlocklisted("test1.example.com@services.mozilla.org", "0", "0", "0"), "blocklist cleared");
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
@ -102,20 +102,26 @@ var tests = {
|
|||
Services.prefs.clearUserPref("social.whitelist");
|
||||
setAndUpdateBlocklist(blocklistEmpty, next);
|
||||
}
|
||||
let installFrom = "https://bad.com";
|
||||
// whitelist to avoid the 3rd party install dialog, we only want to test
|
||||
// the blocklist inside installProvider.
|
||||
Services.prefs.setCharPref("social.whitelist", installFrom);
|
||||
setAndUpdateBlocklist(blocklistURL, function() {
|
||||
try {
|
||||
// expecting an exception when attempting to install a hard blocked
|
||||
// provider
|
||||
Social.installProvider(installFrom, manifest_bad, function(addonManifest) {
|
||||
finish(false);
|
||||
});
|
||||
} catch(e) {
|
||||
finish(true);
|
||||
}
|
||||
let activationURL = manifest_bad.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
let installFrom = doc.nodePrincipal.origin;
|
||||
// whitelist to avoid the 3rd party install dialog, we only want to test
|
||||
// the blocklist inside installProvider.
|
||||
Services.prefs.setCharPref("social.whitelist", installFrom);
|
||||
setAndUpdateBlocklist(blocklistURL, function() {
|
||||
try {
|
||||
// expecting an exception when attempting to install a hard blocked
|
||||
// provider
|
||||
Social.installProvider(doc, manifest_bad, function(addonManifest) {
|
||||
gBrowser.removeTab(tab);
|
||||
finish(false);
|
||||
});
|
||||
} catch(e) {
|
||||
gBrowser.removeTab(tab);
|
||||
finish(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
testBlockingExistingProvider: function(next) {
|
||||
|
|
|
@ -251,3 +251,11 @@ function addWindowListener(aURL, aCallback) {
|
|||
onWindowTitleChange: function(aXULWindow, aNewTitle) { }
|
||||
});
|
||||
}
|
||||
|
||||
function addTab(url, callback) {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
|
||||
tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
|
||||
tab.linkedBrowser.removeEventListener("load", tabLoad, true);
|
||||
executeSoon(function() {callback(tab)});
|
||||
}, true);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 0.7.337
|
||||
Current extension version is: 0.7.390
|
||||
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* jshint esnext:true */
|
||||
/* globals Components, Services, XPCOMUtils, NetUtil, dump */
|
||||
|
||||
'use strict';
|
||||
|
||||
var EXPORTED_SYMBOLS = ['PdfRedirector'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const PDF_CONTENT_TYPE = 'application/pdf';
|
||||
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/NetUtil.jsm');
|
||||
|
||||
|
||||
function getDOMWindow(aChannel) {
|
||||
var requestor = aChannel.notificationCallbacks ?
|
||||
aChannel.notificationCallbacks :
|
||||
aChannel.loadGroup.notificationCallbacks;
|
||||
var win = requestor.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
return win;
|
||||
}
|
||||
|
||||
function getObjectUrl(window) {
|
||||
var url;
|
||||
var element = window.frameElement;
|
||||
var isOverlay = false;
|
||||
var params = {};
|
||||
if (element) {
|
||||
var tagName = element.nodeName;
|
||||
while (tagName != 'EMBED' && tagName != 'OBJECT') {
|
||||
// plugin overlay skipping until the target plugin is found
|
||||
isOverlay = true;
|
||||
element = element.parentNode;
|
||||
if (!element)
|
||||
throw 'Plugin element is not found';
|
||||
tagName = element.nodeName;
|
||||
}
|
||||
if (tagName == 'EMBED') {
|
||||
for (var i = 0; i < element.attributes.length; ++i) {
|
||||
params[element.attributes[i].localName] = element.attributes[i].value;
|
||||
}
|
||||
url = params.src;
|
||||
} else {
|
||||
for (var i = 0; i < element.childNodes.length; ++i) {
|
||||
var paramElement = element.childNodes[i];
|
||||
if (paramElement.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
|
||||
paramElement.nodeName != 'PARAM') {
|
||||
continue;
|
||||
}
|
||||
|
||||
params[paramElement.getAttribute('name')] =
|
||||
paramElement.getAttribute('value');
|
||||
}
|
||||
var dataAttribute = element.getAttribute('data');
|
||||
url = dataAttribute || params.movie || params.src;
|
||||
}
|
||||
}
|
||||
if (!url) {
|
||||
return url; // src is not specified
|
||||
}
|
||||
|
||||
var element = window.frameElement;
|
||||
// XXX base uri?
|
||||
var baseUri = !element ? null :
|
||||
Services.io.newURI(element.ownerDocument.location.href, null, null);
|
||||
|
||||
return Services.io.newURI(url, null, baseUri).spec;
|
||||
}
|
||||
|
||||
function PdfRedirector() {
|
||||
}
|
||||
|
||||
PdfRedirector.prototype = {
|
||||
|
||||
// properties required for XPCOM registration:
|
||||
classID: Components.ID('{8cbfd8d0-2042-4976-b3ef-d9dee1efb975}'),
|
||||
classDescription: 'pdf.js Redirector',
|
||||
contractID:
|
||||
'@mozilla.org/streamconv;1?from=application/x-moz-playpreview-pdfjs&to=*/*',
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIStreamConverter,
|
||||
Ci.nsIStreamListener,
|
||||
Ci.nsIRequestObserver
|
||||
]),
|
||||
|
||||
// nsIStreamConverter::convert
|
||||
convert: function(aFromStream, aFromType, aToType, aCtxt) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
// nsIStreamConverter::asyncConvertData
|
||||
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
|
||||
// Store the listener passed to us
|
||||
this.listener = aListener;
|
||||
},
|
||||
|
||||
// nsIStreamListener::onDataAvailable
|
||||
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
|
||||
// Do nothing since all the data loading is handled by the viewer.
|
||||
},
|
||||
|
||||
// nsIRequestObserver::onStartRequest
|
||||
onStartRequest: function(aRequest, aContext) {
|
||||
// Setup the request so we can use it below.
|
||||
aRequest.QueryInterface(Ci.nsIChannel);
|
||||
// Cancel the request so the viewer can handle it.
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
|
||||
var domWindow = getDOMWindow(aRequest);
|
||||
var pdfUrl = getObjectUrl(domWindow);
|
||||
if (!pdfUrl) {
|
||||
Services.console.logStringMessage(
|
||||
'PdfRedirector.js: PDF location is not specified for OBJECT/EMBED tag');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new channel that is viewer loaded as a resource.
|
||||
var ioService = Services.io;
|
||||
var channel = ioService.newChannel(pdfUrl, null, null);
|
||||
|
||||
channel.loadGroup = aRequest.loadGroup;
|
||||
|
||||
channel.asyncOpen(this.listener, aContext);
|
||||
},
|
||||
|
||||
// nsIRequestObserver::onStopRequest
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfRedirector]);
|
|
@ -337,6 +337,7 @@ ChromeActions.prototype = {
|
|||
}, '*');
|
||||
};
|
||||
|
||||
var self = this;
|
||||
this.dataListener.oncomplete =
|
||||
function ChromeActions_dataListenerComplete(data, errorCode) {
|
||||
|
||||
|
@ -346,7 +347,7 @@ ChromeActions.prototype = {
|
|||
errorCode: errorCode
|
||||
}, '*');
|
||||
|
||||
delete this.dataListener;
|
||||
delete self.dataListener;
|
||||
};
|
||||
|
||||
return true;
|
||||
|
@ -385,21 +386,19 @@ ChromeActions.prototype = {
|
|||
var message = getLocalizedString(strings, 'unsupported_feature');
|
||||
|
||||
var notificationBox = null;
|
||||
// Multiple browser windows can be opened, finding one for notification box
|
||||
var windowsEnum = Services.wm
|
||||
.getZOrderDOMWindowEnumerator('navigator:browser', true);
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
var win = windowsEnum.getNext();
|
||||
if (win.closed)
|
||||
continue;
|
||||
var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document);
|
||||
if (browser) {
|
||||
// right window/browser is found, getting the notification box
|
||||
notificationBox = win.gBrowser.getNotificationBox(browser);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!notificationBox) {
|
||||
try {
|
||||
// Based on MDN's "Working with windows in chrome code"
|
||||
var mainWindow = domWindow
|
||||
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIWebNavigation)
|
||||
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
var browser = mainWindow.gBrowser
|
||||
.getBrowserForDocument(domWindow.top.document);
|
||||
notificationBox = mainWindow.gBrowser.getNotificationBox(browser);
|
||||
} catch (e) {
|
||||
log('Unable to get a notification box for the fallback message');
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -33,11 +33,15 @@ const PDF_CONTENT_TYPE = 'application/pdf';
|
|||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://pdf.js.components/PdfStreamConverter.js');
|
||||
Cu.import('resource://pdf.js.components/PdfRedirector.js');
|
||||
|
||||
let Svc = {};
|
||||
XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
|
||||
'@mozilla.org/mime;1',
|
||||
'nsIMIMEService');
|
||||
XPCOMUtils.defineLazyServiceGetter(Svc, 'pluginHost',
|
||||
'@mozilla.org/plugin/host;1',
|
||||
'nsIPluginHost');
|
||||
|
||||
function getBoolPref(aPref, aDefaultValue) {
|
||||
try {
|
||||
|
@ -55,8 +59,10 @@ function getIntPref(aPref, aDefaultValue) {
|
|||
}
|
||||
}
|
||||
|
||||
// Register/unregister a constructor as a component.
|
||||
let Factory = {
|
||||
// Factory that registers/unregisters a constructor as a component.
|
||||
function Factory() {}
|
||||
|
||||
Factory.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]),
|
||||
_targetConstructor: null,
|
||||
|
||||
|
@ -193,7 +199,14 @@ let PdfJs = {
|
|||
if (this._registered)
|
||||
return;
|
||||
|
||||
Factory.register(PdfStreamConverter);
|
||||
this._pdfStreamConverterFactory = new Factory();
|
||||
this._pdfStreamConverterFactory.register(PdfStreamConverter);
|
||||
|
||||
this._pdfRedirectorFactory = new Factory();
|
||||
this._pdfRedirectorFactory.register(PdfRedirector);
|
||||
Svc.pluginHost.registerPlayPreviewMimeType('application/pdf', true,
|
||||
'data:application/x-moz-playpreview-pdfjs;,');
|
||||
|
||||
this._registered = true;
|
||||
},
|
||||
|
||||
|
@ -201,7 +214,13 @@ let PdfJs = {
|
|||
if (!this._registered)
|
||||
return;
|
||||
|
||||
Factory.unregister();
|
||||
this._pdfStreamConverterFactory.unregister();
|
||||
delete this._pdfStreamConverterFactory;
|
||||
|
||||
this._pdfRedirectorFactory.unregister;
|
||||
delete this._pdfRedirectorFactory;
|
||||
Svc.pluginHost.unregisterPlayPreviewMimeType('application/pdf');
|
||||
|
||||
this._registered = false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
*/
|
||||
|
||||
var PDFJS = {};
|
||||
PDFJS.version = '0.7.337';
|
||||
PDFJS.build = 'f58aee1';
|
||||
PDFJS.version = '0.7.390';
|
||||
PDFJS.build = '921f321';
|
||||
|
||||
(function pdfjsWrapper() {
|
||||
// Use strict in our context only - users might not want it
|
||||
|
@ -832,6 +832,23 @@ var Util = PDFJS.Util = (function UtilClosure() {
|
|||
return [xt, yt];
|
||||
};
|
||||
|
||||
// Applies the transform to the rectangle and finds the minimum axially
|
||||
// aligned bounding box.
|
||||
Util.getAxialAlignedBoundingBox =
|
||||
function Util_getAxialAlignedBoundingBox(r, m) {
|
||||
|
||||
var p1 = Util.applyTransform(r, m);
|
||||
var p2 = Util.applyTransform(r.slice(2, 4), m);
|
||||
var p3 = Util.applyTransform([r[0], r[3]], m);
|
||||
var p4 = Util.applyTransform([r[2], r[1]], m);
|
||||
return [
|
||||
Math.min(p1[0], p2[0], p3[0], p4[0]),
|
||||
Math.min(p1[1], p2[1], p3[1], p4[1]),
|
||||
Math.max(p1[0], p2[0], p3[0], p4[0]),
|
||||
Math.max(p1[1], p2[1], p3[1], p4[1])
|
||||
];
|
||||
};
|
||||
|
||||
Util.inverseTransform = function Util_inverseTransform(m) {
|
||||
var d = m[0] * m[3] - m[1] * m[2];
|
||||
return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d,
|
||||
|
@ -2260,6 +2277,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
this.objs = objs;
|
||||
this.textLayer = textLayer;
|
||||
this.imageLayer = imageLayer;
|
||||
this.groupStack = [];
|
||||
if (canvasCtx) {
|
||||
addContextCurrentTransform(canvasCtx);
|
||||
}
|
||||
|
@ -2392,6 +2410,25 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
return tmpCanvas;
|
||||
}
|
||||
|
||||
function copyCtxState(sourceCtx, destCtx) {
|
||||
var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha',
|
||||
'lineWidth', 'lineCap', 'lineJoin', 'miterLimit',
|
||||
'globalCompositeOperation', 'font'];
|
||||
for (var i = 0, ii = properties.length; i < ii; i++) {
|
||||
var property = properties[i];
|
||||
if (property in sourceCtx) {
|
||||
destCtx[property] = sourceCtx[property];
|
||||
}
|
||||
}
|
||||
if ('setLineDash' in sourceCtx) {
|
||||
destCtx.setLineDash(sourceCtx.getLineDash());
|
||||
destCtx.lineDashOffset = sourceCtx.lineDashOffset;
|
||||
} else if ('mozDash' in sourceCtx) {
|
||||
destCtx.mozDash = sourceCtx.mozDash;
|
||||
destCtx.mozDashOffset = sourceCtx.mozDashOffset;
|
||||
}
|
||||
}
|
||||
|
||||
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
|
||||
var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
|
||||
var NORMAL_CLIP = {};
|
||||
|
@ -2596,6 +2633,22 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
this.current.fillAlpha = state[1];
|
||||
this.ctx.globalAlpha = state[1];
|
||||
break;
|
||||
case 'BM':
|
||||
if (value && value.name && (value.name !== 'Normal')) {
|
||||
var mode = value.name.replace(/([A-Z])/g,
|
||||
function(c) {
|
||||
return '-' + c.toLowerCase();
|
||||
}
|
||||
).substring(1);
|
||||
this.ctx.globalCompositeOperation = mode;
|
||||
if (this.ctx.globalCompositeOperation !== mode) {
|
||||
warn('globalCompositeOperation "' + mode +
|
||||
'" is not supported');
|
||||
}
|
||||
} else {
|
||||
this.ctx.globalCompositeOperation = 'source-over';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3008,7 +3061,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
var character = glyph.fontChar;
|
||||
var vmetric = glyph.vmetric || defaultVMetrics;
|
||||
if (vertical) {
|
||||
var vx = vmetric[1] * fontSize * current.fontMatrix[0];
|
||||
var vx = glyph.vmetric ? vmetric[1] : glyph.width * 0.5;
|
||||
vx = -vx * fontSize * current.fontMatrix[0];
|
||||
var vy = vmetric[2] * fontSize * current.fontMatrix[0];
|
||||
}
|
||||
var width = vmetric ? -vmetric[0] : glyph.width;
|
||||
|
@ -3083,7 +3137,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
geom.canvasWidth = canvasWidth;
|
||||
if (vertical) {
|
||||
var vmetric = font.defaultVMetrics;
|
||||
geom.x -= vmetric[1] * fontSize * current.fontMatrix[0] /
|
||||
geom.x += vmetric[1] * fontSize * current.fontMatrix[0] /
|
||||
fontSizeScale * geom.hScale;
|
||||
geom.y += vmetric[2] * fontSize * current.fontMatrix[0] /
|
||||
fontSizeScale * geom.vScale;
|
||||
|
@ -3144,7 +3198,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
if (vertical) {
|
||||
var fontSizeScale = current.fontSizeScale;
|
||||
var vmetric = font.defaultVMetrics;
|
||||
geom.x -= vmetric[1] * fontSize * current.fontMatrix[0] /
|
||||
geom.x += vmetric[1] * fontSize * current.fontMatrix[0] /
|
||||
fontSizeScale * geom.hScale;
|
||||
geom.y += vmetric[2] * fontSize * current.fontMatrix[0] /
|
||||
fontSizeScale * geom.vScale;
|
||||
|
@ -3365,6 +3419,89 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
} while (this.current.paintFormXObjectDepth >= depth);
|
||||
},
|
||||
|
||||
beginGroup: function CanvasGraphics_beginGroup(group) {
|
||||
this.save();
|
||||
var currentCtx = this.ctx;
|
||||
// TODO non-isolated groups - according to Rik at adobe non-isolated
|
||||
// group results aren't usually that different and they even have tools
|
||||
// that ignore this setting. Notes from Rik on implmenting:
|
||||
// - When you encounter an transparency group, create a new canvas with
|
||||
// the dimensions of the bbox
|
||||
// - copy the content from the previous canvas to the new canvas
|
||||
// - draw as usual
|
||||
// - remove the backdrop alpha:
|
||||
// alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha
|
||||
// value of your transparency group and 'alphaBackdrop' the alpha of the
|
||||
// backdrop
|
||||
// - remove background color:
|
||||
// colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew)
|
||||
if (!group.isolated) {
|
||||
TODO('Support non-isolated groups.');
|
||||
}
|
||||
|
||||
// TODO knockout - supposedly possible with the clever use of compositing
|
||||
// modes.
|
||||
if (group.knockout) {
|
||||
TODO('Support knockout groups.');
|
||||
}
|
||||
|
||||
var currentTransform = currentCtx.mozCurrentTransform;
|
||||
if (group.matrix) {
|
||||
currentCtx.transform.apply(currentCtx, group.matrix);
|
||||
}
|
||||
assert(group.bbox, 'Bounding box is required.');
|
||||
|
||||
// Based on the current transform figure out how big the bounding box
|
||||
// will actually be.
|
||||
var bounds = Util.getAxialAlignedBoundingBox(
|
||||
group.bbox,
|
||||
currentCtx.mozCurrentTransform);
|
||||
// Use ceil in case we're between sizes so we don't create canvas that is
|
||||
// too small.
|
||||
var drawnWidth = Math.ceil(bounds[2] - bounds[0]);
|
||||
var drawnHeight = Math.ceil(bounds[3] - bounds[1]);
|
||||
var scratchCanvas = createScratchCanvas(drawnWidth, drawnHeight);
|
||||
var groupCtx = scratchCanvas.getContext('2d');
|
||||
addContextCurrentTransform(groupCtx);
|
||||
// Since we created a new canvas that is just the size of the bounding box
|
||||
// we have to translate the group ctx.
|
||||
var offsetX = bounds[0];
|
||||
var offsetY = bounds[1];
|
||||
groupCtx.translate(-offsetX, -offsetY);
|
||||
groupCtx.transform.apply(groupCtx, currentTransform);
|
||||
|
||||
// Setup the current ctx so when the group is popped we draw it the right
|
||||
// location.
|
||||
currentCtx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
currentCtx.translate(offsetX, offsetY);
|
||||
|
||||
// The transparency group inherits all off the current graphics state
|
||||
// except the blend mode, soft mask, and alpha constants.
|
||||
copyCtxState(currentCtx, groupCtx);
|
||||
this.ctx = groupCtx;
|
||||
this.setGState([
|
||||
['SMask', 'None'],
|
||||
['BM', 'Normal'],
|
||||
['ca', 1],
|
||||
['CA', 1]
|
||||
]);
|
||||
this.groupStack.push(currentCtx);
|
||||
},
|
||||
|
||||
endGroup: function CanvasGraphics_endGroup(group) {
|
||||
var groupCtx = this.ctx;
|
||||
this.ctx = this.groupStack.pop();
|
||||
// Turn off image smoothing to avoid sub pixel interpolation which can
|
||||
// look kind of blurry for some pdfs.
|
||||
if ('imageSmoothingEnabled' in this.ctx) {
|
||||
this.ctx.imageSmoothingEnabled = false;
|
||||
} else {
|
||||
this.ctx.mozImageSmoothingEnabled = false;
|
||||
}
|
||||
this.ctx.drawImage(groupCtx.canvas, 0, 0);
|
||||
this.restore();
|
||||
},
|
||||
|
||||
paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
|
||||
var domImage = this.objs.get(objId);
|
||||
if (!domImage) {
|
||||
|
@ -3920,7 +4057,7 @@ var Catalog = (function CatalogClosure() {
|
|||
if (isStream(js)) {
|
||||
js = bytesToString(js.getBytes());
|
||||
}
|
||||
javaScript.push(js);
|
||||
javaScript.push(stringToPDFString(js));
|
||||
}
|
||||
}
|
||||
return shadow(this, 'javaScript', javaScript);
|
||||
|
@ -4385,6 +4522,9 @@ var NameTree = (function NameTreeClosure() {
|
|||
while (queue.length > 0) {
|
||||
var i, n;
|
||||
var obj = xref.fetchIfRef(queue.shift());
|
||||
if (!isDict(obj)) {
|
||||
continue;
|
||||
}
|
||||
if (obj.has('Kids')) {
|
||||
var kids = obj.get('Kids');
|
||||
for (i = 0, n = kids.length; i < n; i++) {
|
||||
|
@ -14652,6 +14792,60 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
return loadedName;
|
||||
}
|
||||
|
||||
function buildFormXObject(xobj, smask) {
|
||||
var matrix = xobj.dict.get('Matrix');
|
||||
var bbox = xobj.dict.get('BBox');
|
||||
var group = xobj.dict.get('Group');
|
||||
if (group) {
|
||||
var groupOptions = {
|
||||
matrix: matrix,
|
||||
bbox: bbox,
|
||||
smask: !!smask,
|
||||
isolated: false,
|
||||
knockout: false
|
||||
};
|
||||
|
||||
var groupSubtype = group.get('S');
|
||||
if (isName(groupSubtype) && groupSubtype.name === 'Transparency') {
|
||||
groupOptions.isolated = group.get('I') || false;
|
||||
groupOptions.knockout = group.get('K') || false;
|
||||
// There is also a group colorspace, but since we put everything in
|
||||
// RGB I'm not sure we need it.
|
||||
}
|
||||
fnArray.push('beginGroup');
|
||||
argsArray.push([groupOptions]);
|
||||
}
|
||||
|
||||
fnArray.push('paintFormXObjectBegin');
|
||||
argsArray.push([matrix, bbox]);
|
||||
|
||||
// This adds the operatorList of the xObj to the current queue.
|
||||
var depIdx = dependencyArray.length;
|
||||
|
||||
// Pass in the current `queue` object. That means the `fnArray`
|
||||
// and the `argsArray` in this scope is reused and new commands
|
||||
// are added to them.
|
||||
self.getOperatorList(xobj,
|
||||
xobj.dict.get('Resources') || resources,
|
||||
dependencyArray, queue);
|
||||
|
||||
self.getOperatorList(xobj,
|
||||
xobj.dict.get('Resources') || resources,
|
||||
dependencyArray, queue);
|
||||
|
||||
// Add the dependencies that are required to execute the
|
||||
// operatorList.
|
||||
insertDependency(dependencyArray.slice(depIdx));
|
||||
|
||||
fnArray.push('paintFormXObjectEnd');
|
||||
argsArray.push([]);
|
||||
|
||||
if (group) {
|
||||
fnArray.push('endGroup');
|
||||
argsArray.push([groupOptions]);
|
||||
}
|
||||
}
|
||||
|
||||
function buildPaintImageXObject(image, inline) {
|
||||
var dict = image.dict;
|
||||
var w = dict.get('Width', 'W');
|
||||
|
@ -14825,28 +15019,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
);
|
||||
|
||||
if ('Form' == type.name) {
|
||||
var matrix = xobj.dict.get('Matrix');
|
||||
var bbox = xobj.dict.get('BBox');
|
||||
|
||||
fnArray.push('paintFormXObjectBegin');
|
||||
argsArray.push([matrix, bbox]);
|
||||
|
||||
// This adds the operatorList of the xObj to the current queue.
|
||||
var depIdx = dependencyArray.length;
|
||||
|
||||
// Pass in the current `queue` object. That means the `fnArray`
|
||||
// and the `argsArray` in this scope is reused and new commands
|
||||
// are added to them.
|
||||
this.getOperatorList(xobj,
|
||||
xobj.dict.get('Resources') || resources,
|
||||
dependencyArray, queue);
|
||||
|
||||
// Add the dependencies that are required to execute the
|
||||
// operatorList.
|
||||
insertDependency(dependencyArray.slice(depIdx));
|
||||
|
||||
fn = 'paintFormXObjectEnd';
|
||||
buildFormXObject(xobj);
|
||||
args = [];
|
||||
continue;
|
||||
} else if ('Image' == type.name) {
|
||||
buildPaintImageXObject(xobj, false);
|
||||
} else {
|
||||
|
@ -14905,6 +15080,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
case 'FL':
|
||||
case 'CA':
|
||||
case 'ca':
|
||||
case 'BM':
|
||||
gsStateObj.push([key, value]);
|
||||
break;
|
||||
case 'Font':
|
||||
|
@ -14914,11 +15090,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
value[1]
|
||||
]);
|
||||
break;
|
||||
case 'BM':
|
||||
// We support the default so don't trigger the TODO.
|
||||
if (!isName(value) || value.name != 'Normal')
|
||||
TODO('graphic state operator ' + key);
|
||||
break;
|
||||
case 'SMask':
|
||||
// We support the default so don't trigger the TODO.
|
||||
if (!isName(value) || value.name != 'None')
|
||||
|
@ -15459,7 +15630,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
|
||||
if (properties.vertical) {
|
||||
var vmetrics = dict.get('DW2') || [880, -1000];
|
||||
defaultVMetrics = [vmetrics[1], vmetrics[1] / 2, vmetrics[0]];
|
||||
defaultVMetrics = [vmetrics[1], defaultWidth * 0.5, vmetrics[0]];
|
||||
vmetrics = dict.get('W2');
|
||||
if (vmetrics) {
|
||||
for (var i = 0, ii = vmetrics.length; i < ii; i++) {
|
||||
|
@ -16073,7 +16244,23 @@ var nonStdFontMap = {
|
|||
'LucidaConsole': 'Courier',
|
||||
'LucidaConsole-Bold': 'Courier-Bold',
|
||||
'LucidaConsole-BoldItalic': 'Courier-BoldOblique',
|
||||
'LucidaConsole-Italic': 'Courier-Oblique'
|
||||
'LucidaConsole-Italic': 'Courier-Oblique',
|
||||
'MS-Gothic': 'MS Gothic',
|
||||
'MS-Gothic-Bold': 'MS Gothic-Bold',
|
||||
'MS-Gothic-BoldItalic': 'MS Gothic-BoldItalic',
|
||||
'MS-Gothic-Italic': 'MS Gothic-Italic',
|
||||
'MS-Mincho': 'MS Mincho',
|
||||
'MS-Mincho-Bold': 'MS Mincho-Bold',
|
||||
'MS-Mincho-BoldItalic': 'MS Mincho-BoldItalic',
|
||||
'MS-Mincho-Italic': 'MS Mincho-Italic',
|
||||
'MS-PGothic': 'MS PGothic',
|
||||
'MS-PGothic-Bold': 'MS PGothic-Bold',
|
||||
'MS-PGothic-BoldItalic': 'MS PGothic-BoldItalic',
|
||||
'MS-PGothic-Italic': 'MS PGothic-Italic',
|
||||
'MS-PMincho': 'MS PMincho',
|
||||
'MS-PMincho-Bold': 'MS PMincho-Bold',
|
||||
'MS-PMincho-BoldItalic': 'MS PMincho-BoldItalic',
|
||||
'MS-PMincho-Italic': 'MS PMincho-Italic',
|
||||
};
|
||||
|
||||
var serifFonts = {
|
||||
|
@ -16139,6 +16326,7 @@ var CMapConverterList = {
|
|||
'90msp-RKSJ-H': sjisToUnicode,
|
||||
'90msp-RKSJ-V': sjisToUnicode,
|
||||
'GBK-EUC-H': gbkToUnicode,
|
||||
'B5pc-H': big5ToUnicode,
|
||||
'ETenms-B5-H': big5ToUnicode,
|
||||
'ETenms-B5-V': big5ToUnicode,
|
||||
};
|
||||
|
@ -18164,8 +18352,8 @@ var Font = (function FontClosure() {
|
|||
}
|
||||
var bmpLength = i + 1;
|
||||
|
||||
var trailingRangesCount = ranges[bmpLength - 1][1] < 0xFFFF ? 1 : 0;
|
||||
var segCount = bmpLength + trailingRangesCount;
|
||||
if (ranges[i][1] === 0xFFFF) { ranges[i][1] = 0xFFFE; }
|
||||
var segCount = bmpLength + 1;
|
||||
var segCount2 = segCount * 2;
|
||||
var searchRange = getMaxPower2(segCount) * 2;
|
||||
var searchEntry = Math.log(segCount) / Math.log(2);
|
||||
|
@ -18210,12 +18398,10 @@ var Font = (function FontClosure() {
|
|||
}
|
||||
}
|
||||
|
||||
if (trailingRangesCount > 0) {
|
||||
endCount += '\xFF\xFF';
|
||||
startCount += '\xFF\xFF';
|
||||
idDeltas += '\x00\x01';
|
||||
idRangeOffsets += '\x00\x00';
|
||||
}
|
||||
endCount += '\xFF\xFF';
|
||||
startCount += '\xFF\xFF';
|
||||
idDeltas += '\x00\x01';
|
||||
idRangeOffsets += '\x00\x00';
|
||||
|
||||
var format314 = '\x00\x00' + // language
|
||||
string16(segCount2) +
|
||||
|
@ -18302,6 +18488,14 @@ var Font = (function FontClosure() {
|
|||
if (firstChar > lastChar) {
|
||||
return false;
|
||||
}
|
||||
stream.getBytes(6); // skipping sTypoAscender/Descender/LineGap
|
||||
var usWinAscent = int16(stream.getBytes(2));
|
||||
if (usWinAscent === 0) { // makes font unreadable by windows
|
||||
return false;
|
||||
}
|
||||
|
||||
// OS/2 appears to be valid, resetting some fields
|
||||
os2.data[8] = os2.data[9] = 0; // IE rejects fonts if fsType != 0
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -19151,16 +19345,6 @@ var Font = (function FontClosure() {
|
|||
return names;
|
||||
}
|
||||
|
||||
function isOS2Valid(os2Table) {
|
||||
var data = os2Table.data;
|
||||
// usWinAscent == 0 makes font unreadable by windows
|
||||
var usWinAscent = (data[74] << 8) | data[75];
|
||||
if (usWinAscent === 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
var TTOpsStackDeltas = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1,
|
||||
|
@ -19355,12 +19539,6 @@ var Font = (function FontClosure() {
|
|||
// of missing tables
|
||||
createOpenTypeHeader(header.version, ttf, numTables);
|
||||
|
||||
// Invalid OS/2 can break the font for the Windows
|
||||
if (os2 && !isOS2Valid(os2)) {
|
||||
tables.splice(tables.indexOf(os2), 1);
|
||||
os2 = null;
|
||||
}
|
||||
|
||||
// Ensure the hmtx table contains the advance width and
|
||||
// sidebearings information for numGlyphs in the maxp table
|
||||
font.pos = (font.start || 0) + maxp.offset;
|
||||
|
@ -21658,10 +21836,19 @@ var CFFParser = (function CFFParserClosure() {
|
|||
}
|
||||
return { charStrings: charStrings, seacs: seacs };
|
||||
},
|
||||
emptyPrivateDictionary:
|
||||
function CFFParser_emptyPrivateDictionary(parentDict) {
|
||||
var privateDict = this.createDict(CFFPrivateDict, [],
|
||||
parentDict.strings);
|
||||
parentDict.setByKey(18, [0, 0]);
|
||||
parentDict.privateDict = privateDict;
|
||||
},
|
||||
parsePrivateDict: function CFFParser_parsePrivateDict(parentDict) {
|
||||
// no private dict, do nothing
|
||||
if (!parentDict.hasName('Private'))
|
||||
if (!parentDict.hasName('Private')) {
|
||||
this.emptyPrivateDictionary(parentDict);
|
||||
return;
|
||||
}
|
||||
var privateOffset = parentDict.getByName('Private');
|
||||
// make sure the params are formatted correctly
|
||||
if (!isArray(privateOffset) || privateOffset.length !== 2) {
|
||||
|
@ -21672,7 +21859,7 @@ var CFFParser = (function CFFParserClosure() {
|
|||
var offset = privateOffset[1];
|
||||
// remove empty dicts or ones that refer to invalid location
|
||||
if (size === 0 || offset >= this.bytes.length) {
|
||||
parentDict.removeByName('Private');
|
||||
this.emptyPrivateDictionary(parentDict);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -21690,7 +21877,7 @@ var CFFParser = (function CFFParserClosure() {
|
|||
var relativeOffset = offset + subrsOffset;
|
||||
// Validate the offset.
|
||||
if (subrsOffset === 0 || relativeOffset >= this.bytes.length) {
|
||||
privateDict.removeByName('Subrs');
|
||||
this.emptyPrivateDictionary(parentDict);
|
||||
return;
|
||||
}
|
||||
var subrsIndex = this.parseIndex(relativeOffset);
|
||||
|
@ -22371,15 +22558,23 @@ var CFFCompiler = (function CFFCompilerClosure() {
|
|||
output) {
|
||||
for (var i = 0, ii = dicts.length; i < ii; ++i) {
|
||||
var fontDict = dicts[i];
|
||||
if (!fontDict.privateDict || !fontDict.hasName('Private'))
|
||||
continue;
|
||||
assert(fontDict.privateDict && fontDict.hasName('Private'),
|
||||
'There must be an private dictionary.');
|
||||
var privateDict = fontDict.privateDict;
|
||||
var privateDictTracker = new CFFOffsetTracker();
|
||||
var privateDictData = this.compileDict(privateDict, privateDictTracker);
|
||||
|
||||
privateDictTracker.offset(output.length);
|
||||
var outputLength = output.length;
|
||||
privateDictTracker.offset(outputLength);
|
||||
if (!privateDictData.length) {
|
||||
// The private dictionary was empty, set the output length to zero to
|
||||
// ensure the offset length isn't out of bounds in the eyes of the
|
||||
// sanitizer.
|
||||
outputLength = 0;
|
||||
}
|
||||
|
||||
trackers[i].setEntryLocation('Private',
|
||||
[privateDictData.length, output.length],
|
||||
[privateDictData.length, outputLength],
|
||||
output);
|
||||
output.add(privateDictData);
|
||||
|
||||
|
|
|
@ -826,6 +826,11 @@ html[dir='rtl'] .toolbarButton.pageDown::before {
|
|||
padding-top: 4px;
|
||||
}
|
||||
|
||||
#viewBookmark[href='#'] {
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.toolbarButton.bookmark::before {
|
||||
content: url(images/toolbarButton-bookmark.png);
|
||||
}
|
||||
|
|
|
@ -1290,6 +1290,7 @@ var PDFView = {
|
|||
if (PDFView.supportsPrinting) {
|
||||
pdfDocument.getJavaScript().then(function(javaScript) {
|
||||
if (javaScript.length) {
|
||||
console.warn('Warning: JavaScript is not supported');
|
||||
PDFView.fallback();
|
||||
}
|
||||
// Hack to support auto printing.
|
||||
|
@ -1363,7 +1364,7 @@ var PDFView = {
|
|||
self.setTitle(pdfTitle + ' - ' + document.title);
|
||||
|
||||
if (info.IsAcroFormPresent) {
|
||||
// AcroForm/XFA was found
|
||||
console.warn('Warning: AcroForm/XFA is not supported');
|
||||
PDFView.fallback();
|
||||
}
|
||||
});
|
||||
|
@ -1565,55 +1566,52 @@ var PDFView = {
|
|||
},
|
||||
|
||||
getVisiblePages: function pdfViewGetVisiblePages() {
|
||||
return this.getVisibleElements(this.container,
|
||||
this.pages, true);
|
||||
if (!this.isFullscreen) {
|
||||
return this.getVisibleElements(this.container, this.pages, true);
|
||||
} else {
|
||||
// The algorithm in getVisibleElements is broken in fullscreen mode.
|
||||
var visible = [], page = this.page;
|
||||
var currentPage = this.pages[page - 1];
|
||||
visible.push({ id: currentPage.id, view: currentPage });
|
||||
|
||||
return { first: currentPage, last: currentPage, views: visible};
|
||||
}
|
||||
},
|
||||
|
||||
getVisibleThumbs: function pdfViewGetVisibleThumbs() {
|
||||
return this.getVisibleElements(this.thumbnailContainer,
|
||||
this.thumbnails);
|
||||
return this.getVisibleElements(this.thumbnailContainer, this.thumbnails);
|
||||
},
|
||||
|
||||
// Generic helper to find out what elements are visible within a scroll pane.
|
||||
getVisibleElements: function pdfViewGetVisibleElements(
|
||||
scrollEl, views, sortByVisibility) {
|
||||
var currentHeight = 0, view;
|
||||
var top = scrollEl.scrollTop;
|
||||
var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
|
||||
var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
|
||||
|
||||
for (var i = 1, ii = views.length; i <= ii; ++i) {
|
||||
view = views[i - 1];
|
||||
var visible = [], view;
|
||||
var currentHeight, viewHeight, hiddenHeight, percentHeight;
|
||||
var currentWidth, viewWidth;
|
||||
for (var i = 0, ii = views.length; i < ii; ++i) {
|
||||
view = views[i];
|
||||
currentHeight = view.el.offsetTop + view.el.clientTop;
|
||||
if (currentHeight + view.el.clientHeight > top)
|
||||
break;
|
||||
currentHeight += view.el.clientHeight;
|
||||
}
|
||||
|
||||
var visible = [];
|
||||
|
||||
// Algorithm broken in fullscreen mode
|
||||
if (this.isFullscreen) {
|
||||
var currentPage = this.pages[this.page - 1];
|
||||
visible.push({
|
||||
id: currentPage.id,
|
||||
view: currentPage
|
||||
});
|
||||
|
||||
return { first: currentPage, last: currentPage, views: visible};
|
||||
}
|
||||
|
||||
var bottom = top + scrollEl.clientHeight;
|
||||
var nextHeight, hidden, percent, viewHeight;
|
||||
for (; i <= ii && currentHeight < bottom; ++i) {
|
||||
view = views[i - 1];
|
||||
viewHeight = view.el.clientHeight;
|
||||
currentHeight = view.el.offsetTop + view.el.clientTop;
|
||||
nextHeight = currentHeight + viewHeight;
|
||||
hidden = Math.max(0, top - currentHeight) +
|
||||
Math.max(0, nextHeight - bottom);
|
||||
percent = Math.floor((viewHeight - hidden) * 100.0 / viewHeight);
|
||||
if ((currentHeight + viewHeight) < top) {
|
||||
continue;
|
||||
}
|
||||
if (currentHeight > bottom) {
|
||||
break;
|
||||
}
|
||||
currentWidth = view.el.offsetLeft + view.el.clientLeft;
|
||||
viewWidth = view.el.clientWidth;
|
||||
if ((currentWidth + viewWidth) < left || currentWidth > right) {
|
||||
continue;
|
||||
}
|
||||
hiddenHeight = Math.max(0, top - currentHeight) +
|
||||
Math.max(0, currentHeight + viewHeight - bottom);
|
||||
percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;
|
||||
|
||||
visible.push({ id: view.id, y: currentHeight,
|
||||
view: view, percent: percent });
|
||||
currentHeight = nextHeight;
|
||||
view: view, percent: percentHeight });
|
||||
}
|
||||
|
||||
var first = visible[0];
|
||||
|
@ -1622,13 +1620,12 @@ var PDFView = {
|
|||
if (sortByVisibility) {
|
||||
visible.sort(function(a, b) {
|
||||
var pc = a.percent - b.percent;
|
||||
if (Math.abs(pc) > 0.001)
|
||||
if (Math.abs(pc) > 0.001) {
|
||||
return -pc;
|
||||
|
||||
}
|
||||
return a.id - b.id; // ensure stability
|
||||
});
|
||||
}
|
||||
|
||||
return {first: first, last: last, views: visible};
|
||||
},
|
||||
|
||||
|
@ -1704,6 +1701,10 @@ var PDFView = {
|
|||
this.page = this.page;
|
||||
this.clearMouseScrollState();
|
||||
this.hidePresentationControls();
|
||||
|
||||
// Ensure that the thumbnail of the current page is visible
|
||||
// when exiting fullscreen mode.
|
||||
scrollIntoView(document.getElementById('thumbnailContainer' + this.page));
|
||||
},
|
||||
|
||||
showPresentationControls: function pdfViewShowPresentationControls() {
|
||||
|
@ -2086,7 +2087,6 @@ var PageView = function pageView(container, pdfPage, id, scale,
|
|||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.id = 'page' + this.id;
|
||||
canvas.mozOpaque = true;
|
||||
div.appendChild(canvas);
|
||||
this.canvas = canvas;
|
||||
|
||||
|
@ -2116,13 +2116,10 @@ var PageView = function pageView(container, pdfPage, id, scale,
|
|||
}
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// TODO(mack): use data attributes to store these
|
||||
ctx._scaleX = outputScale.sx;
|
||||
ctx._scaleY = outputScale.sy;
|
||||
ctx.save();
|
||||
ctx.fillStyle = 'rgb(255, 255, 255)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.restore();
|
||||
if (outputScale.scaled) {
|
||||
ctx.scale(outputScale.sx, outputScale.sy);
|
||||
}
|
||||
|
@ -2337,7 +2334,6 @@ var ThumbnailView = function thumbnailView(container, pdfPage, id) {
|
|||
function getPageDrawContext() {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.id = 'thumbnail' + id;
|
||||
canvas.mozOpaque = true;
|
||||
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
|
@ -2599,7 +2595,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
|
|||
var textDiv = document.createElement('div');
|
||||
|
||||
// vScale and hScale already contain the scaling to pixel units
|
||||
var fontHeight = geom.fontSize * geom.vScale;
|
||||
var fontHeight = geom.fontSize * Math.abs(geom.vScale);
|
||||
textDiv.dataset.canvasWidth = geom.canvasWidth * geom.hScale;
|
||||
textDiv.dataset.fontName = geom.fontName;
|
||||
|
||||
|
@ -3250,7 +3246,8 @@ window.addEventListener('keydown', function keydown(evt) {
|
|||
|
||||
// First, handle the key bindings that are independent whether an input
|
||||
// control is selected or not.
|
||||
if (cmd == 1 || cmd == 8) { // either CTRL or META key.
|
||||
if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
|
||||
// either CTRL or META key with optional SHIFT.
|
||||
switch (evt.keyCode) {
|
||||
case 70:
|
||||
if (!PDFView.supportsIntegratedFind) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
chrome.manifest
|
||||
components/PdfRedirector.js
|
||||
components/PdfStreamConverter.js
|
||||
content/build/pdf.js
|
||||
content/PdfJs.jsm
|
||||
|
|
|
@ -45,7 +45,6 @@ var SelectionHandler = {
|
|||
_contentOffset: { x:0, y:0 },
|
||||
_domWinUtils: null,
|
||||
_selectionMoveActive: false,
|
||||
_lastMarker: "",
|
||||
_debugOptions: { dumpRanges: false, displayRanges: false },
|
||||
_snap: true,
|
||||
|
||||
|
@ -189,6 +188,14 @@ var SelectionHandler = {
|
|||
// We bail if things get out of sync here implying we missed a message.
|
||||
this._selectionMoveActive = true;
|
||||
|
||||
if (this._targetIsEditable) {
|
||||
// If we're coming out of an out-of-bounds scroll, the node the user is
|
||||
// trying to drag may be hidden (the monocle will be pegged to the edge
|
||||
// of the edit). Make sure the node the user wants to move is visible
|
||||
// and has focus.
|
||||
this._updateInputFocus(aMsg.change);
|
||||
}
|
||||
|
||||
// Update the position of our selection monocles
|
||||
this._updateSelectionUI(true, true);
|
||||
},
|
||||
|
@ -594,63 +601,16 @@ var SelectionHandler = {
|
|||
}
|
||||
|
||||
// Adjust our y position up such that we are sending coordinates on
|
||||
// the text line vs. below it where the monocle is positioned. This
|
||||
// applies to free floating text areas. For text inputs we'll constrain
|
||||
// coordinates further below.
|
||||
// the text line vs. below it where the monocle is positioned.
|
||||
let halfLineHeight = this._queryHalfLineHeight(aMarker, selection);
|
||||
clientPoint.yPos -= halfLineHeight;
|
||||
|
||||
// Modify selection based on monocle movement
|
||||
if (this._targetIsEditable) {
|
||||
// Check to see if we are beyond the bounds of selection in a input
|
||||
// control. If we are we want to add selection and scroll the added
|
||||
// selection into view.
|
||||
let result = this.updateTextEditSelection(clientPoint);
|
||||
|
||||
// If we're targeting a text input of any kind, make sure clientPoint
|
||||
// is contained within the bounds of the text control. For example, if
|
||||
// a user drags up too close to an upper bounds, selectAtPoint might
|
||||
// select the content above the control. This looks crappy and breaks
|
||||
// our selection rect management.
|
||||
clientPoint =
|
||||
this._constrainPointWithinControl(clientPoint, halfLineHeight);
|
||||
|
||||
// If result.trigger is true, the monocle is outside the bounds of the
|
||||
// control. If it's false, fall through to our additive text selection
|
||||
// below.
|
||||
if (result.trigger) {
|
||||
// _handleSelectionPoint is triggered by input movement, so if we've
|
||||
// tested positive for out-of-bounds scrolling here, we need to set a
|
||||
// recurring timer to keep the expected selection behavior going as
|
||||
// long as the user keeps the monocle out of bounds.
|
||||
if (!this._scrollTimer)
|
||||
this._scrollTimer = new Util.Timeout();
|
||||
this._setTextEditUpdateInterval(result.speed);
|
||||
|
||||
// Smooth the selection
|
||||
this._setContinuousSelection();
|
||||
|
||||
// Update the other monocle's position if we've dragged off to one side
|
||||
this._updateSelectionUI(result.start, result.end);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._lastMarker = aMarker;
|
||||
|
||||
// If we aren't out-of-bounds, clear the scroll timer if it exists.
|
||||
this._clearTimers();
|
||||
|
||||
// Adjusts the selection based on monocle movement
|
||||
this._adjustSelection(aMarker, clientPoint, aEndOfSelection);
|
||||
|
||||
// Update the other monocle's position. We do this because the dragging
|
||||
// monocle may reset the static monocle to a new position if the dragging
|
||||
// monocle drags ahead or behind the other.
|
||||
if (aMarker == "start") {
|
||||
this._updateSelectionUI(false, true);
|
||||
this._adjustEditableSelection(aMarker, clientPoint,
|
||||
halfLineHeight, aEndOfSelection);
|
||||
} else {
|
||||
this._updateSelectionUI(true, false);
|
||||
this._adjustSelection(aMarker, clientPoint, aEndOfSelection);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -659,6 +619,59 @@ var SelectionHandler = {
|
|||
*/
|
||||
|
||||
/*
|
||||
* _adjustEditableSelection
|
||||
*
|
||||
* Based on a monocle marker and position, adds or subtracts from the
|
||||
* existing selection in editable controls. Handles auto-scroll as well.
|
||||
*
|
||||
* @param the marker currently being manipulated
|
||||
* @param aAdjustedClientPoint client point adjusted for line height.
|
||||
* @param aHalfLineHeight half line height in pixels
|
||||
* @param aEndOfSelection indicates if this is the end of a selection
|
||||
* move, in which case we may want to snap to the end of a word or
|
||||
* sentence.
|
||||
*/
|
||||
_adjustEditableSelection: function _adjustEditableSelection(aMarker,
|
||||
aAdjustedClientPoint,
|
||||
aHalfLineHeight,
|
||||
aEndOfSelection) {
|
||||
// Test to see if we need to handle auto-scroll in cases where the
|
||||
// monocle is outside the bounds of the control. This also handles
|
||||
// adjusting selection if out-of-bounds is true.
|
||||
let result = this.updateTextEditSelection(aAdjustedClientPoint);
|
||||
|
||||
// If result.trigger is true, the monocle is outside the bounds of the
|
||||
// control.
|
||||
if (result.trigger) {
|
||||
// _handleSelectionPoint is triggered by input movement, so if we've
|
||||
// tested positive for out-of-bounds scrolling here, we need to set a
|
||||
// recurring timer to keep the expected selection behavior going as
|
||||
// long as the user keeps the monocle out of bounds.
|
||||
this._setTextEditUpdateInterval(result.speed);
|
||||
|
||||
// Smooth the selection
|
||||
this._setContinuousSelection();
|
||||
|
||||
// Update the other monocle's position if we've dragged off to one side
|
||||
this._updateSelectionUI(result.start, result.end);
|
||||
} else {
|
||||
// If we aren't out-of-bounds, clear the scroll timer if it exists.
|
||||
this._clearTimers();
|
||||
|
||||
// Restrict the client point to the interior of the control. Prevents
|
||||
// _adjustSelection from accidentally selecting content outside the
|
||||
// control.
|
||||
let constrainedPoint =
|
||||
this._constrainPointWithinControl(aAdjustedClientPoint, aHalfLineHeight);
|
||||
|
||||
// Add or subtract selection
|
||||
this._adjustSelection(aMarker, constrainedPoint, aEndOfSelection);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* _adjustSelection
|
||||
*
|
||||
* Based on a monocle marker and position, adds or subtracts from the
|
||||
* existing selection.
|
||||
*
|
||||
|
@ -707,6 +720,15 @@ var SelectionHandler = {
|
|||
|
||||
// Smooth over the selection between all existing ranges.
|
||||
this._setContinuousSelection();
|
||||
|
||||
// Update the other monocle's position. We do this because the dragging
|
||||
// monocle may reset the static monocle to a new position if the dragging
|
||||
// monocle drags ahead or behind the other.
|
||||
if (aMarker == "start") {
|
||||
this._updateSelectionUI(false, true);
|
||||
} else {
|
||||
this._updateSelectionUI(true, false);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -780,9 +802,9 @@ var SelectionHandler = {
|
|||
/*
|
||||
* updateTextEditSelection(aPoint, aClientPoint)
|
||||
*
|
||||
* Checks to see if the monocle point is outside the bounds of the
|
||||
* target edit. If so, use the selection controller to select and
|
||||
* scroll the edit appropriately.
|
||||
* Checks to see if the monocle point is outside the bounds of the target
|
||||
* edit. If so, use the selection controller to select and scroll the edit
|
||||
* appropriately.
|
||||
*
|
||||
* @param aClientPoint raw pointer position
|
||||
* @return { speed: 0.0 -> 1.0,
|
||||
|
@ -823,6 +845,8 @@ var SelectionHandler = {
|
|||
|
||||
_setTextEditUpdateInterval: function _setTextEditUpdateInterval(aSpeedValue) {
|
||||
let timeout = (75 - (aSpeedValue * 75));
|
||||
if (!this._scrollTimer)
|
||||
this._scrollTimer = new Util.Timeout();
|
||||
this._scrollTimer.interval(timeout, this.scrollTimerCallback);
|
||||
},
|
||||
|
||||
|
@ -836,7 +860,6 @@ var SelectionHandler = {
|
|||
* _addEditSelection - selection control call wrapper for text inputs.
|
||||
* Adds selection on the anchor or focus side of selection in a text
|
||||
* input. Scrolls the location into view as well.
|
||||
* (TBD: anchor side scrolling is currently broken, see bug 848594)
|
||||
*
|
||||
* @param const selection node identifier
|
||||
*/
|
||||
|
@ -844,16 +867,32 @@ var SelectionHandler = {
|
|||
let selCtrl = this._getSelectController();
|
||||
try {
|
||||
if (aLocation == kSelectionNodeAnchor) {
|
||||
this._targetElement.selectionStart = this._targetElement.selectionStart - 1;
|
||||
selCtrl.scrollSelectionIntoView(Ci.nsISelectionController.SELECTION_NORMAL,
|
||||
Ci.nsISelectionController.SELECTION_ANCHOR_REGION,
|
||||
Ci.nsISelectionController.SCROLL_SYNCHRONOUS);
|
||||
let start = Math.max(this._targetElement.selectionStart - 1, 0);
|
||||
this._targetElement.setSelectionRange(start, this._targetElement.selectionEnd,
|
||||
"backward");
|
||||
} else {
|
||||
this._targetElement.selectionEnd = this._targetElement.selectionEnd + 1;
|
||||
selCtrl.scrollSelectionIntoView(Ci.nsISelectionController.SELECTION_NORMAL,
|
||||
Ci.nsISelectionController.SELECTION_FOCUS_REGION,
|
||||
Ci.nsISelectionController.SCROLL_SYNCHRONOUS);
|
||||
let end = Math.min(this._targetElement.selectionEnd + 1,
|
||||
this._targetElement.textLength);
|
||||
this._targetElement.setSelectionRange(this._targetElement.selectionStart,
|
||||
end,
|
||||
"forward");
|
||||
}
|
||||
selCtrl.scrollSelectionIntoView(Ci.nsISelectionController.SELECTION_NORMAL,
|
||||
Ci.nsISelectionController.SELECTION_FOCUS_REGION,
|
||||
Ci.nsISelectionController.SCROLL_SYNCHRONOUS);
|
||||
} catch (ex) { Util.dumpLn(ex);}
|
||||
},
|
||||
|
||||
_updateInputFocus: function _updateInputFocus(aMarker) {
|
||||
try {
|
||||
let selCtrl = this._getSelectController();
|
||||
this._targetElement.setSelectionRange(this._targetElement.selectionStart,
|
||||
this._targetElement.selectionEnd,
|
||||
aMarker == "start" ?
|
||||
"backward" : "forward");
|
||||
selCtrl.scrollSelectionIntoView(Ci.nsISelectionController.SELECTION_NORMAL,
|
||||
Ci.nsISelectionController.SELECTION_FOCUS_REGION,
|
||||
Ci.nsISelectionController.SCROLL_SYNCHRONOUS);
|
||||
} catch (ex) {}
|
||||
},
|
||||
|
||||
|
|
|
@ -178,8 +178,8 @@ this.Social = {
|
|||
return null;
|
||||
},
|
||||
|
||||
installProvider: function(origin ,sourceURI, data, installCallback) {
|
||||
SocialService.installProvider(origin ,sourceURI, data, installCallback);
|
||||
installProvider: function(doc, data, installCallback) {
|
||||
SocialService.installProvider(doc, data, installCallback);
|
||||
},
|
||||
|
||||
uninstallProvider: function(origin) {
|
||||
|
|
|
@ -441,6 +441,12 @@ public class DoCommand {
|
|||
strReturn += GetProcessInfo();
|
||||
strReturn += "\n";
|
||||
strReturn += GetSutUserInfo();
|
||||
strReturn += "\n";
|
||||
strReturn += GetDiskInfo("/data");
|
||||
strReturn += "\n";
|
||||
strReturn += GetDiskInfo("/system");
|
||||
strReturn += "\n";
|
||||
strReturn += GetDiskInfo("/mnt/sdcard");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -495,6 +501,15 @@ public class DoCommand {
|
|||
strReturn += GetTemperatureInfo();
|
||||
break;
|
||||
|
||||
case DISK:
|
||||
strReturn += "\n";
|
||||
strReturn += GetDiskInfo("/data");
|
||||
strReturn += "\n";
|
||||
strReturn += GetDiskInfo("/system");
|
||||
strReturn += "\n";
|
||||
strReturn += GetDiskInfo("/mnt/sdcard");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2670,18 +2685,19 @@ private void CancelNotification()
|
|||
return "Temperature: " + sTempVal;
|
||||
}
|
||||
|
||||
// todo
|
||||
public String GetDiskInfo(String sPath)
|
||||
{
|
||||
String sRet = "";
|
||||
StatFs statFS = new StatFs(sPath);
|
||||
|
||||
int nBlockCount = statFS.getBlockCount();
|
||||
int nBlockSize = statFS.getBlockSize();
|
||||
int nBlocksAvail = statFS.getAvailableBlocks();
|
||||
int nBlocksFree = statFS.getFreeBlocks();
|
||||
long nBlockCount = statFS.getBlockCount();
|
||||
long nBlockSize = statFS.getBlockSize();
|
||||
long nBlocksAvail = statFS.getAvailableBlocks();
|
||||
// Free is often the same as Available, but can include reserved
|
||||
// blocks that are not available to normal applications.
|
||||
// long nBlocksFree = statFS.getFreeBlocks();
|
||||
|
||||
sRet = "total: " + (nBlockCount * nBlockSize) + "\nfree: " + (nBlocksFree * nBlockSize) + "\navailable: " + (nBlocksAvail * nBlockSize);
|
||||
sRet = sPath + ": " + (nBlockCount * nBlockSize) + " total, " + (nBlocksAvail * nBlockSize) + " available";
|
||||
|
||||
return (sRet);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ GCONF_VERSION=1.2.1
|
|||
GIO_VERSION=2.20
|
||||
STARTUP_NOTIFICATION_VERSION=0.8
|
||||
DBUS_VERSION=0.60
|
||||
SQLITE_VERSION=3.7.15.2
|
||||
SQLITE_VERSION=3.7.16
|
||||
|
||||
MSMANIFEST_TOOL=
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ class nsTextControlFrame;
|
|||
|
||||
// IID for the nsITextControl interface
|
||||
#define NS_ITEXTCONTROLELEMENT_IID \
|
||||
{ 0x3558afa1, 0x6136, 0x4421, \
|
||||
{ 0xbd, 0xdc, 0x2c, 0x9d, 0x5f, 0xc1, 0xfb, 0x91 } }
|
||||
{ 0x3dd53b59, 0x9d8f, 0x40a3, \
|
||||
{ 0x81, 0xd7, 0xb3, 0x43, 0xa0, 0x51, 0xfc, 0xb5 } }
|
||||
|
||||
/**
|
||||
* This interface is used for the text control frame to get the editor and
|
||||
|
@ -76,6 +76,11 @@ public:
|
|||
*/
|
||||
NS_IMETHOD_(int32_t) GetRows() = 0;
|
||||
|
||||
/**
|
||||
* Get the default value of the text control
|
||||
*/
|
||||
NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue) = 0;
|
||||
|
||||
/**
|
||||
* Return true if the value of the control has been changed.
|
||||
*/
|
||||
|
|
|
@ -353,6 +353,9 @@ HTMLTextAreaElement::SetValueChanged(bool aValueChanged)
|
|||
bool previousValue = mValueChanged;
|
||||
|
||||
mValueChanged = aValueChanged;
|
||||
if (!aValueChanged && !mState.IsEmpty()) {
|
||||
mState.EmptyValue();
|
||||
}
|
||||
|
||||
if (mValueChanged != previousValue) {
|
||||
UpdateState(true);
|
||||
|
@ -1379,6 +1382,12 @@ HTMLTextAreaElement::GetRows()
|
|||
return DEFAULT_ROWS_TEXTAREA;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
HTMLTextAreaElement::GetDefaultValueFromContent(nsAString& aValue)
|
||||
{
|
||||
GetDefaultValue(aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
HTMLTextAreaElement::ValueChanged() const
|
||||
{
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
NS_IMETHOD_(int32_t) GetCols();
|
||||
NS_IMETHOD_(int32_t) GetWrapCols();
|
||||
NS_IMETHOD_(int32_t) GetRows();
|
||||
NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue);
|
||||
NS_IMETHOD_(bool) ValueChanged() const;
|
||||
NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const;
|
||||
NS_IMETHOD_(nsIEditor*) GetTextEditor();
|
||||
|
|
|
@ -5557,6 +5557,20 @@ nsHTMLInputElement::GetRows()
|
|||
return DEFAULT_ROWS;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
nsHTMLInputElement::GetDefaultValueFromContent(nsAString& aValue)
|
||||
{
|
||||
nsTextEditorState *state = GetEditorState();
|
||||
if (state) {
|
||||
GetDefaultValue(aValue);
|
||||
// This is called by the frame to show the value.
|
||||
// We have to sanitize it when needed.
|
||||
if (!mParserCreating) {
|
||||
SanitizeValue(aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
nsHTMLInputElement::ValueChanged() const
|
||||
{
|
||||
|
|
|
@ -147,6 +147,7 @@ public:
|
|||
NS_IMETHOD_(int32_t) GetCols();
|
||||
NS_IMETHOD_(int32_t) GetWrapCols();
|
||||
NS_IMETHOD_(int32_t) GetRows();
|
||||
NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue);
|
||||
NS_IMETHOD_(bool) ValueChanged() const;
|
||||
NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const;
|
||||
NS_IMETHOD_(nsIEditor*) GetTextEditor();
|
||||
|
|
|
@ -1747,7 +1747,9 @@ nsTextEditorState::GetValue(nsAString& aValue, bool aIgnoreWrap) const
|
|||
mCachedValue.Truncate();
|
||||
}
|
||||
} else {
|
||||
if (mValue) {
|
||||
if (!mTextCtrlElement->ValueChanged() || !mValue) {
|
||||
mTextCtrlElement->GetDefaultValueFromContent(aValue);
|
||||
} else {
|
||||
aValue = NS_ConvertUTF8toUTF16(*mValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,8 @@ public:
|
|||
void SetValue(const nsAString& aValue, bool aUserInput,
|
||||
bool aSetValueAsChanged);
|
||||
void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
|
||||
void EmptyValue() { if (mValue) mValue->Truncate(); }
|
||||
bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
|
||||
|
||||
nsresult CreatePlaceholderNode();
|
||||
|
||||
|
|
|
@ -2282,96 +2282,81 @@ nsHTMLDocument::Plugins()
|
|||
return Embeds();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLDocument::ResolveName(const nsAString& aName,
|
||||
nsIContent *aForm,
|
||||
nsISupports **aResult,
|
||||
nsWrapperCache **aCache)
|
||||
nsISupports*
|
||||
nsHTMLDocument::ResolveName(const nsAString& aName, nsWrapperCache **aCache)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
*aCache = nullptr;
|
||||
|
||||
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
|
||||
if (!entry) {
|
||||
return NS_OK;
|
||||
*aCache = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t length = 0;
|
||||
nsBaseContentList *list = entry->GetNameContentList();
|
||||
if (list) {
|
||||
list->GetLength(&length);
|
||||
}
|
||||
uint32_t length = list ? list->Length() : 0;
|
||||
|
||||
if (length > 0) {
|
||||
if (length == 1) {
|
||||
// Only one element in the list, return the element instead of
|
||||
// returning the list
|
||||
|
||||
// Only one element in the list, return the element instead of returning
|
||||
// the list.
|
||||
nsIContent *node = list->Item(0);
|
||||
if (!aForm || nsContentUtils::BelongsInForm(aForm, node)) {
|
||||
NS_ADDREF(*aResult = node);
|
||||
*aCache = node;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
*aCache = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
// The list contains more than one element, return the whole
|
||||
// list, unless...
|
||||
|
||||
if (aForm) {
|
||||
// ... we're called from a form, in that case we create a
|
||||
// nsFormContentList which will filter out the elements in the
|
||||
// list that don't belong to aForm
|
||||
|
||||
nsFormContentList *fc_list = new nsFormContentList(aForm, *list);
|
||||
NS_ENSURE_TRUE(fc_list, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
uint32_t len;
|
||||
fc_list->GetLength(&len);
|
||||
|
||||
if (len < 2) {
|
||||
// After the nsFormContentList is done filtering there's either
|
||||
// nothing or one element in the list. Return that element, or null
|
||||
// if there's no element in the list.
|
||||
|
||||
nsIContent *node = fc_list->Item(0);
|
||||
|
||||
NS_IF_ADDREF(*aResult = node);
|
||||
*aCache = node;
|
||||
|
||||
delete fc_list;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
list = fc_list;
|
||||
}
|
||||
|
||||
return CallQueryInterface(list, aResult);
|
||||
// The list contains more than one element, return the whole list.
|
||||
*aCache = list;
|
||||
return list;
|
||||
}
|
||||
|
||||
// No named items were found, see if there's one registerd by id for
|
||||
// aName. If we get this far, FindNamedItems() will have been called
|
||||
// for aName, so we're guaranteed that if there is an element with
|
||||
// the id aName, it'll be entry's IdContent.
|
||||
|
||||
// No named items were found, see if there's one registerd by id for aName.
|
||||
Element *e = entry->GetIdElement();
|
||||
|
||||
if (e && e->IsHTML()) {
|
||||
nsIAtom *tag = e->Tag();
|
||||
|
||||
if ((tag == nsGkAtoms::embed ||
|
||||
tag == nsGkAtoms::img ||
|
||||
tag == nsGkAtoms::object ||
|
||||
tag == nsGkAtoms::applet) &&
|
||||
(!aForm || nsContentUtils::BelongsInForm(aForm, e))) {
|
||||
NS_ADDREF(*aResult = e);
|
||||
if (tag == nsGkAtoms::embed ||
|
||||
tag == nsGkAtoms::img ||
|
||||
tag == nsGkAtoms::object ||
|
||||
tag == nsGkAtoms::applet) {
|
||||
*aCache = e;
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
*aCache = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
nsHTMLDocument::ResolveName(const nsAString& aName,
|
||||
nsIContent *aForm,
|
||||
nsWrapperCache **aCache)
|
||||
{
|
||||
nsISupports* result = ResolveName(aName, aCache);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> node = do_QueryInterface(result);
|
||||
if (!node) {
|
||||
// We create a nsFormContentList which will filter out the elements in the
|
||||
// list that don't belong to aForm.
|
||||
nsRefPtr<nsBaseContentList> list =
|
||||
new nsFormContentList(aForm, *static_cast<nsBaseContentList*>(result));
|
||||
if (list->Length() > 1) {
|
||||
*aCache = list;
|
||||
return list.forget();
|
||||
}
|
||||
|
||||
// After the nsFormContentList is done filtering there's either nothing or
|
||||
// one element in the list. Return that element, or null if there's no
|
||||
// element in the list.
|
||||
node = list->Item(0);
|
||||
} else if (!nsContentUtils::BelongsInForm(aForm, node)) {
|
||||
node = nullptr;
|
||||
}
|
||||
|
||||
*aCache = node;
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
//----------------------------
|
||||
|
|
|
@ -114,10 +114,10 @@ public:
|
|||
nsWrapperCache **aCache,
|
||||
nsresult *aResult);
|
||||
|
||||
virtual nsresult ResolveName(const nsAString& aName,
|
||||
nsIContent *aForm,
|
||||
nsISupports **aResult,
|
||||
nsWrapperCache **aCache);
|
||||
nsISupports* ResolveName(const nsAString& aName, nsWrapperCache **aCache);
|
||||
virtual already_AddRefed<nsISupports> ResolveName(const nsAString& aName,
|
||||
nsIContent *aForm,
|
||||
nsWrapperCache **aCache);
|
||||
|
||||
virtual void AddedForm();
|
||||
virtual void RemovedForm();
|
||||
|
|
|
@ -33,10 +33,9 @@ public:
|
|||
*/
|
||||
virtual void SetCompatibilityMode(nsCompatibility aMode) = 0;
|
||||
|
||||
virtual nsresult ResolveName(const nsAString& aName,
|
||||
nsIContent *aForm,
|
||||
nsISupports **aResult,
|
||||
nsWrapperCache **aCache) = 0;
|
||||
virtual already_AddRefed<nsISupports> ResolveName(const nsAString& aName,
|
||||
nsIContent *aForm,
|
||||
nsWrapperCache **aCache) = 0;
|
||||
|
||||
/**
|
||||
* Called when form->BindToTree() is called so that document knows
|
||||
|
|
|
@ -108,7 +108,7 @@ AudioContext::CreateGain()
|
|||
already_AddRefed<DelayNode>
|
||||
AudioContext::CreateDelay(double aMaxDelayTime, ErrorResult& aRv)
|
||||
{
|
||||
if (aMaxDelayTime > 0. && aMaxDelayTime < 3.) {
|
||||
if (aMaxDelayTime > 0. && aMaxDelayTime < 180.) {
|
||||
nsRefPtr<DelayNode> delayNode = new DelayNode(this, aMaxDelayTime);
|
||||
return delayNode.forget();
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ addLoadEvent(function() {
|
|||
context.createDelay(0);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
expectException(function() {
|
||||
context.createDelay(3);
|
||||
context.createDelay(180);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
expectTypeError(function() {
|
||||
context.createDelay(NaN);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
This is sqlite 3.7.15.2
|
||||
This is sqlite 3.7.16
|
||||
|
||||
-- Ryan VanderMeulen <ryanvm@gmail.com>, 01/2013
|
||||
-- Ryan VanderMeulen <ryanvm@gmail.com>, 03/2013
|
||||
|
||||
See http://www.sqlite.org/ for more info.
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -107,9 +107,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.7.15.2"
|
||||
#define SQLITE_VERSION_NUMBER 3007015
|
||||
#define SQLITE_SOURCE_ID "2013-01-09 11:53:05 c0e09560d26f0a6456be9dd3447f5311eb4f238f"
|
||||
#define SQLITE_VERSION "3.7.16"
|
||||
#define SQLITE_VERSION_NUMBER 3007016
|
||||
#define SQLITE_SOURCE_ID "2013-03-18 11:39:23 66d5f2b76750f3520eb7a495f6247206758f5b90"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
@ -288,7 +288,7 @@ typedef sqlite_uint64 sqlite3_uint64;
|
|||
** [sqlite3_blob_close | close] all [BLOB handles], and
|
||||
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
|
||||
** with the [sqlite3] object prior to attempting to close the object. ^If
|
||||
** sqlite3_close() is called on a [database connection] that still has
|
||||
** sqlite3_close_v2() is called on a [database connection] that still has
|
||||
** outstanding [prepared statements], [BLOB handles], and/or
|
||||
** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
|
||||
** of resources is deferred until all [prepared statements], [BLOB handles],
|
||||
|
@ -483,7 +483,17 @@ SQLITE_API int sqlite3_exec(
|
|||
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
||||
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
||||
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
||||
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
|
||||
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
|
||||
#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8))
|
||||
#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8))
|
||||
#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8))
|
||||
#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8))
|
||||
#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8))
|
||||
#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8))
|
||||
#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8))
|
||||
#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
|
||||
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags For File Open Operations
|
||||
|
|
|
@ -14,21 +14,24 @@
|
|||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.mozAlarms.enabled", true]]}, function() {
|
||||
if (SpecialPowers.hasPermission("alarms", document)) {
|
||||
SpecialPowers.removePermission("alarms", document);
|
||||
window.location.reload();
|
||||
} else {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.mozAlarms.enabled", true]]}, function() {
|
||||
SpecialPowers.removePermission("alarms", document);
|
||||
|
||||
// mozAlarms is intalled on all platforms except Android for the moment.
|
||||
if (navigator.appVersion.indexOf("Android") != -1) {
|
||||
ok(!('mozAlarms' in navigator), "navigator.mozAlarms should not exist");
|
||||
} else {
|
||||
ok('mozAlarms' in navigator, "navigator.mozAlarms should exist");
|
||||
is(navigator.mozAlarms, null, "navigator.mozAlarms should return null");
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
// mozAlarms is intalled on all platforms except Android for the moment.
|
||||
if (navigator.appVersion.indexOf("Android") != -1) {
|
||||
ok(!('mozAlarms' in navigator), "navigator.mozAlarms should not exist");
|
||||
} else {
|
||||
ok('mozAlarms' in navigator, "navigator.mozAlarms should exist");
|
||||
is(navigator.mozAlarms, null, "navigator.mozAlarms should return null");
|
||||
}
|
||||
SpecialPowers.addPermission("alarms", true, document);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -28,7 +28,6 @@ SpecialPowers.pushPrefEnv({"set": [["dom.mozAlarms.enabled", true]]}, function()
|
|||
"navigator.mozAlarms should be an nsIDOMMozAlarmsManager object");
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("alarms", document);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ const Ci = Components.interfaces;
|
|||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["FreeSpaceWatcher"];
|
||||
|
||||
|
@ -42,15 +43,7 @@ this.FreeSpaceWatcher = {
|
|||
currentStatus: null,
|
||||
notify: function(aTimer) {
|
||||
try {
|
||||
let deviceStorage = Services.wm.getMostRecentWindow("navigator:browser")
|
||||
.navigator.getDeviceStorage("apps");
|
||||
let req = deviceStorage.freeSpace();
|
||||
req.onsuccess = req.onerror = function statResult(e) {
|
||||
if (!e.target.result) {
|
||||
return;
|
||||
}
|
||||
|
||||
let freeBytes = e.target.result;
|
||||
let checkFreeSpace = function (freeBytes) {
|
||||
debug("Free bytes: " + freeBytes);
|
||||
let newStatus = freeBytes > aThreshold;
|
||||
if (newStatus != callback.currentStatus) {
|
||||
|
@ -58,8 +51,44 @@ this.FreeSpaceWatcher = {
|
|||
aOnStatusChange(newStatus ? "free" : "full");
|
||||
callback.currentStatus = newStatus;
|
||||
}
|
||||
};
|
||||
|
||||
let deviceStorage = Services.wm.getMostRecentWindow("navigator:browser")
|
||||
.navigator.getDeviceStorage("apps");
|
||||
if (deviceStorage) {
|
||||
let req = deviceStorage.freeSpace();
|
||||
req.onsuccess = req.onerror = function statResult(e) {
|
||||
if (!e.target.result) {
|
||||
return;
|
||||
}
|
||||
|
||||
let freeBytes = e.target.result;
|
||||
checkFreeSpace(freeBytes);
|
||||
}
|
||||
} else {
|
||||
// deviceStorage isn't available, so use the webappsDir instead.
|
||||
// This needs to be moved from a hardcoded string to DIRECTORY_NAME
|
||||
// in AppsUtils. See bug 852685.
|
||||
let dir = FileUtils.getDir("webappsDir", ["webapps"], true, true);
|
||||
let freeBytes;
|
||||
try {
|
||||
freeBytes = dir.diskSpaceAvailable;
|
||||
} catch(e) {
|
||||
// If disk space information isn't available, we should assume
|
||||
// that there is enough free space, and that we'll fail when
|
||||
// we actually run out of disk space.
|
||||
callback.currentStatus = true;
|
||||
}
|
||||
if (freeBytes) {
|
||||
// We have disk space information. Call this here so that
|
||||
// any exceptions are caught in the outer catch block.
|
||||
checkFreeSpace(freeBytes);
|
||||
}
|
||||
}
|
||||
} catch(e) { debug(e); }
|
||||
} catch(e) {
|
||||
// If the aOnStatusChange callback has errored we'll end up here.
|
||||
debug(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -267,6 +267,8 @@ WebappsRegistry.prototype = {
|
|||
QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistry,
|
||||
#ifdef MOZ_B2G
|
||||
Ci.mozIDOMApplicationRegistry2,
|
||||
#elifdef MOZ_WIDGET_ANDROID
|
||||
Ci.mozIDOMApplicationRegistry2,
|
||||
#endif
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
|
@ -275,6 +277,8 @@ WebappsRegistry.prototype = {
|
|||
interfaces: [Ci.mozIDOMApplicationRegistry,
|
||||
#ifdef MOZ_B2G
|
||||
Ci.mozIDOMApplicationRegistry2,
|
||||
#elifdef MOZ_WIDGET_ANDROID
|
||||
Ci.mozIDOMApplicationRegistry2,
|
||||
#endif
|
||||
],
|
||||
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
||||
|
|
|
@ -1799,7 +1799,9 @@ this.DOMApplicationRegistry = {
|
|||
delete this.queuedDownload[aManifestURL];
|
||||
},
|
||||
|
||||
confirmInstall: function(aData, aFromSync, aProfileDir, aOfflineCacheObserver) {
|
||||
confirmInstall: function(aData, aFromSync, aProfileDir,
|
||||
aOfflineCacheObserver,
|
||||
aZipDownloadSuccessCallback) {
|
||||
let isReinstall = false;
|
||||
let app = aData.app;
|
||||
app.removable = true;
|
||||
|
@ -1956,6 +1958,9 @@ this.DOMApplicationRegistry = {
|
|||
manifestURL: appObject.manifestURL,
|
||||
app: app,
|
||||
manifest: aManifest });
|
||||
if (aZipDownloadSuccessCallback) {
|
||||
aZipDownloadSuccessCallback(aManifest);
|
||||
}
|
||||
}).bind(this));
|
||||
}).bind(this));
|
||||
}
|
||||
|
@ -2401,28 +2406,45 @@ this.DOMApplicationRegistry = {
|
|||
sendProgressEvent();
|
||||
};
|
||||
|
||||
let checkDownloadSize = function (freeBytes) {
|
||||
if (freeBytes) {
|
||||
debug("Free storage: " + freeBytes + ". Download size: " +
|
||||
aApp.downloadSize);
|
||||
if (freeBytes <=
|
||||
aApp.downloadSize + AppDownloadManager.MIN_REMAINING_FREESPACE) {
|
||||
cleanup("INSUFFICIENT_STORAGE");
|
||||
return;
|
||||
}
|
||||
}
|
||||
download();
|
||||
};
|
||||
|
||||
let deviceStorage = Services.wm.getMostRecentWindow("navigator:browser")
|
||||
.navigator.getDeviceStorage("apps");
|
||||
let req = deviceStorage.freeSpace();
|
||||
req.onsuccess = req.onerror = function statResult(e) {
|
||||
// Even if we could not retrieve the device storage free space, we try
|
||||
// to download the package.
|
||||
if (!e.target.result) {
|
||||
download();
|
||||
return;
|
||||
}
|
||||
|
||||
let freeBytes = e.target.result;
|
||||
if (freeBytes) {
|
||||
debug("Free storage: " + freeBytes + ". Download size: " +
|
||||
aApp.downloadSize);
|
||||
if (freeBytes <=
|
||||
aApp.downloadSize + AppDownloadManager.MIN_REMAINING_FREESPACE) {
|
||||
cleanup("INSUFFICIENT_STORAGE");
|
||||
if (deviceStorage) {
|
||||
let req = deviceStorage.freeSpace();
|
||||
req.onsuccess = req.onerror = function statResult(e) {
|
||||
// Even if we could not retrieve the device storage free space, we try
|
||||
// to download the package.
|
||||
if (!e.target.result) {
|
||||
download();
|
||||
return;
|
||||
}
|
||||
|
||||
let freeBytes = e.target.result;
|
||||
checkDownloadSize(freeBytes);
|
||||
}
|
||||
} else {
|
||||
// deviceStorage isn't available, so use FileUtils to find the size of available storage.
|
||||
let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps"], true, true);
|
||||
try {
|
||||
checkDownloadSize(dir.diskSpaceAvailable);
|
||||
} catch(ex) {
|
||||
// If disk space information isn't available, we'll end up here.
|
||||
// We should either proceed anyway, otherwise devices that support neither
|
||||
// deviceStorage nor diskSpaceAvailable will never be able to install packaged apps.
|
||||
download();
|
||||
}
|
||||
download();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -3637,7 +3637,7 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSHandleObject obj,
|
|||
}
|
||||
|
||||
if (!result) {
|
||||
document->ResolveName(str, nullptr, getter_AddRefs(result), &cache);
|
||||
result = document->ResolveName(str, &cache);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
|
@ -6612,7 +6612,8 @@ ResolveImpl(JSContext *cx, nsIXPConnectWrappedNative *wrapper, jsid id,
|
|||
nsDependentJSString depStr;
|
||||
NS_ENSURE_TRUE(depStr.init(cx, str), NS_ERROR_UNEXPECTED);
|
||||
|
||||
return doc->ResolveName(depStr, nullptr, result, aCache);
|
||||
NS_IF_ADDREF(*result = doc->ResolveName(depStr, aCache));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -7218,7 +7219,7 @@ nsHTMLFormElementSH::FindNamedItem(nsIForm *aForm, jsid id,
|
|||
do_QueryInterface(content->GetDocument());
|
||||
|
||||
if (html_doc && content) {
|
||||
html_doc->ResolveName(name, content, aResult, aCache);
|
||||
*aResult = html_doc->ResolveName(name, content, aCache).get();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -467,6 +467,7 @@ BluetoothOppManager::AfterOppDisconnected()
|
|||
mConnected = false;
|
||||
mLastCommand = 0;
|
||||
mBlob = nullptr;
|
||||
mPacketLeftLength = 0;
|
||||
|
||||
// We can't reset mSuccessFlag here since this function may be called
|
||||
// before we send system message of transfer complete
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
# 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/.
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDOMPowerManager.idl',
|
||||
|
|
|
@ -19,24 +19,24 @@ SimpleTest.waitForExplicitFinish();
|
|||
function startTest() {
|
||||
window.frames[0].frameElement.setAttribute('onload', 'doTest2()');
|
||||
power = window.frames[0].navigator.mozPower;
|
||||
ok(!power, "Shouldn't be able to access power manager without permission.");
|
||||
|
||||
SpecialPowers.addPermission("power", true, window.frames[0].document);
|
||||
window.frames[0].location.reload();
|
||||
}
|
||||
|
||||
function doTest2() {
|
||||
window.frames[0].frameElement.setAttribute('onload', 'doTest3()');
|
||||
power = window.frames[0].navigator.mozPower;
|
||||
ok(power, "Should be able to access power manager with permission.");
|
||||
|
||||
SpecialPowers.removePermission("power", window.frames[0].document);
|
||||
window.frames[0].location.reload();
|
||||
}
|
||||
|
||||
function doTest2() {
|
||||
window.frames[0].frameElement.setAttribute('onload', 'doTest3()');
|
||||
power = window.frames[0].navigator.mozPower;
|
||||
ok(!power, "Shouldn't be able to access power manager with permission.");
|
||||
|
||||
SpecialPowers.addPermission("power",true, window.frames[0].document);
|
||||
window.frames[0].location.reload();
|
||||
}
|
||||
|
||||
function doTest3() {
|
||||
power = window.frames[0].navigator.mozPower;
|
||||
ok(!power, "Shouldn't be able to access power manager without permission.");
|
||||
ok(power, "Should be able to access power manager with permission.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -204,6 +204,8 @@ win32-gdi-font-cache-no-HFONT.patch: Bug 717178, don't cache GDI font faces when
|
|||
|
||||
fix-win32-font-assertion.patch: Bug 838617, fix assertion from bug 717178 that was in the wrong place
|
||||
|
||||
xlib-flush-glyphs.patch: bug 839745, flush glyphs when necessary
|
||||
|
||||
==== pixman patches ====
|
||||
|
||||
pixman-android-cpu-detect.patch: Add CPU detection support for Android, where we can't reliably access /proc/self/auxv.
|
||||
|
|
|
@ -55,8 +55,10 @@
|
|||
#include "cairo-surface-snapshot-private.h"
|
||||
#include "cairo-surface-subsurface-private.h"
|
||||
#include "cairo-region-private.h"
|
||||
#include "cairo-xlib-xrender-private.h"
|
||||
|
||||
#include <X11/Xutil.h> /* for XDestroyImage */
|
||||
#include <X11/Xlibint.h> /* for access to XDisplay's innards */
|
||||
|
||||
#define XLIB_COORD_MAX 32767
|
||||
|
||||
|
@ -73,7 +75,6 @@
|
|||
#endif
|
||||
|
||||
#if DEBUG
|
||||
#include <X11/Xlibint.h>
|
||||
static void CAIRO_PRINTF_FORMAT (2, 3)
|
||||
_x_bread_crumb (Display *dpy,
|
||||
const char *fmt,
|
||||
|
@ -4318,6 +4319,13 @@ _cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
|
|||
}
|
||||
/* XXX assume X server wants pixman padding. Xft assumes this as well */
|
||||
|
||||
struct _XDisplay *dpy = (struct _XDisplay *) display->display;
|
||||
int req_length = sz_xRenderAddGlyphsReq + 4;
|
||||
if (req_length & 3)
|
||||
req_length += 4 - (req_length & 3);
|
||||
if (dpy->bufptr + req_length > dpy->bufmax)
|
||||
XFlush (display->display);
|
||||
|
||||
XRenderAddGlyphs (display->display, glyphset_info->glyphset,
|
||||
&glyph_index, &glyph_info, 1,
|
||||
(char *) data,
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c
|
||||
index f0de3c7..e24c962 100644
|
||||
--- a/gfx/cairo/cairo/src/cairo-xlib-surface.c
|
||||
+++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c
|
||||
@@ -50,35 +50,36 @@
|
||||
#include "cairo-xlib-private.h"
|
||||
#include "cairo-xlib-surface-private.h"
|
||||
#include "cairo-clip-private.h"
|
||||
#include "cairo-error-private.h"
|
||||
#include "cairo-scaled-font-private.h"
|
||||
#include "cairo-surface-snapshot-private.h"
|
||||
#include "cairo-surface-subsurface-private.h"
|
||||
#include "cairo-region-private.h"
|
||||
+#include "cairo-xlib-xrender-private.h"
|
||||
|
||||
#include <X11/Xutil.h> /* for XDestroyImage */
|
||||
+#include <X11/Xlibint.h> /* for access to XDisplay's innards */
|
||||
|
||||
#define XLIB_COORD_MAX 32767
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
#define UNSUPPORTED(reason) \
|
||||
fprintf (stderr, \
|
||||
"cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \
|
||||
__FUNCTION__, __LINE__, reason), \
|
||||
CAIRO_INT_STATUS_UNSUPPORTED
|
||||
#else
|
||||
#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
-#include <X11/Xlibint.h>
|
||||
static void CAIRO_PRINTF_FORMAT (2, 3)
|
||||
_x_bread_crumb (Display *dpy,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
xReq *req;
|
||||
char buf[2048];
|
||||
unsigned int len, len_dwords;
|
||||
@@ -4313,16 +4314,23 @@ _cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED;
|
||||
break;
|
||||
}
|
||||
/* XXX assume X server wants pixman padding. Xft assumes this as well */
|
||||
|
||||
+ struct _XDisplay *dpy = (struct _XDisplay *) display->display;
|
||||
+ int req_length = sz_xRenderAddGlyphsReq + 4;
|
||||
+ if (req_length & 3)
|
||||
+ req_length += 4 - (req_length & 3);
|
||||
+ if (dpy->bufptr + req_length > dpy->bufmax)
|
||||
+ XFlush (display->display);
|
||||
+
|
||||
XRenderAddGlyphs (display->display, glyphset_info->glyphset,
|
||||
&glyph_index, &glyph_info, 1,
|
||||
(char *) data,
|
||||
glyph_surface->stride * glyph_surface->height);
|
||||
|
||||
if (data != glyph_surface->data)
|
||||
free (data);
|
||||
|
|
@ -379,7 +379,17 @@ gfxPlatform::Init()
|
|||
= do_CreateInstance("@mozilla.org/gfx/init;1");
|
||||
|
||||
if (Preferences::GetBool("gfx.2d.recording", false)) {
|
||||
gPlatform->mRecorder = Factory::CreateEventRecorderForFile("browserrecording.aer");
|
||||
|
||||
nsAutoCString fileName;
|
||||
nsAdoptingString prefFileName = Preferences::GetString("gfx.2d.recordingfile");
|
||||
|
||||
if (prefFileName) {
|
||||
fileName.Append(NS_ConvertUTF16toUTF8(prefFileName));
|
||||
} else {
|
||||
fileName.AssignLiteral("browserrecording.aer");
|
||||
}
|
||||
|
||||
gPlatform->mRecorder = Factory::CreateEventRecorderForFile(fileName.BeginReading());
|
||||
Factory::SetGlobalEventRecorder(gPlatform->mRecorder);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,4 +53,5 @@ Includes = (
|
|||
'nsTArray.h',
|
||||
'nsIFile.h',
|
||||
'mozilla/ipc/ProtocolUtils.h',
|
||||
'GeckoProfiler.h'
|
||||
)
|
||||
|
|
|
@ -4932,6 +4932,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
args=[ ExprLiteral.String(md.prettyMsgName(self.protocol.name
|
||||
+'::')) ])),
|
||||
self.logMessage(md, md.msgCast(msgexpr), 'Received '),
|
||||
self.profilerLabel('Recv', md.decl.progname),
|
||||
Whitespace.NL
|
||||
])
|
||||
|
||||
|
@ -4986,13 +4987,13 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
|
||||
return stmts
|
||||
|
||||
|
||||
def sendAsync(self, md, msgexpr, actor=None):
|
||||
sendok = ExprVar('__sendok')
|
||||
return (
|
||||
sendok,
|
||||
([ Whitespace.NL,
|
||||
self.logMessage(md, msgexpr, 'Sending ') ]
|
||||
self.logMessage(md, msgexpr, 'Sending '),
|
||||
self.profilerLabel('AsyncSend', md.decl.progname) ]
|
||||
+ self.transition(md, 'out', actor)
|
||||
+ [ Whitespace.NL,
|
||||
StmtDecl(Decl(Type.BOOL, sendok.name),
|
||||
|
@ -5008,7 +5009,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
return (
|
||||
sendok,
|
||||
([ Whitespace.NL,
|
||||
self.logMessage(md, msgexpr, 'Sending ') ]
|
||||
self.logMessage(md, msgexpr, 'Sending '),
|
||||
self.profilerLabel('Send', md.decl.progname) ]
|
||||
+ self.transition(md, 'out', actor)
|
||||
+ [ Whitespace.NL,
|
||||
StmtDecl(
|
||||
|
@ -5084,6 +5086,11 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
args=[ ExprLiteral.String('['+ actorname +'] '+ pfx),
|
||||
ExprVar('stderr') ])) ])
|
||||
|
||||
def profilerLabel(self, tag, msgname):
|
||||
return StmtExpr(ExprCall(ExprVar('PROFILER_LABEL'),
|
||||
[ ExprLiteral.String('IPDL::' + self.protocol.name),
|
||||
ExprLiteral.String(tag + msgname) ]))
|
||||
|
||||
def saveActorId(self, md):
|
||||
idvar = ExprVar('__id')
|
||||
if md.decl.type.hasReply():
|
||||
|
|
|
@ -79,13 +79,13 @@ ParallelArrayObject::initProps(JSContext *cx, HandleObject obj)
|
|||
RootedValue undef(cx, UndefinedValue());
|
||||
RootedValue zero(cx, Int32Value(0));
|
||||
|
||||
if (!JSObject::setProperty(cx, obj, obj, cx->names().buffer, &undef, true))
|
||||
if (!JSObject::defineProperty(cx, obj, cx->names().buffer, undef))
|
||||
return false;
|
||||
if (!JSObject::setProperty(cx, obj, obj, cx->names().offset, &zero, true))
|
||||
if (!JSObject::defineProperty(cx, obj, cx->names().offset, zero))
|
||||
return false;
|
||||
if (!JSObject::setProperty(cx, obj, obj, cx->names().shape, &undef, true))
|
||||
if (!JSObject::defineProperty(cx, obj, cx->names().shape, undef))
|
||||
return false;
|
||||
if (!JSObject::setProperty(cx, obj, obj, cx->names().get, &undef, true))
|
||||
if (!JSObject::defineProperty(cx, obj, cx->names().get, undef))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -46,7 +46,7 @@ ParseContext<ParseHandler>::ParseContext(Parser<ParseHandler> *prs, SharedContex
|
|||
decls_(prs->context),
|
||||
args_(prs->context),
|
||||
vars_(prs->context),
|
||||
yieldNode(ParseHandler::null()),
|
||||
yieldOffset(0),
|
||||
parserPC(&prs->pc),
|
||||
lexdeps(prs->context),
|
||||
parent(prs->pc),
|
||||
|
|
|
@ -332,12 +332,9 @@ ParseContext<ParseHandler>::generateFunctionBindings(JSContext *cx, InternalHand
|
|||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...)
|
||||
Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
|
||||
unsigned errorNumber, va_list args)
|
||||
{
|
||||
uint32_t offset = (pn ? handler.getPosition(pn) : tokenStream.currentToken().pos).begin;
|
||||
|
||||
va_list args;
|
||||
va_start(args, errorNumber);
|
||||
bool result = false;
|
||||
switch (kind) {
|
||||
case ParseError:
|
||||
|
@ -354,6 +351,30 @@ Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigne
|
|||
result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...)
|
||||
{
|
||||
uint32_t offset = (pn ? handler.getPosition(pn) : tokenStream.currentToken().pos).begin;
|
||||
|
||||
va_list args;
|
||||
va_start(args, errorNumber);
|
||||
bool result = reportHelper(kind, strict, offset, errorNumber, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset,
|
||||
unsigned errorNumber, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, errorNumber);
|
||||
bool result = reportHelper(kind, strict, offset, errorNumber, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
@ -2882,7 +2903,7 @@ Parser<ParseHandler>::returnOrYield(bool useAssignExpr)
|
|||
pc->sc->asFunctionBox()->setIsGenerator();
|
||||
} else {
|
||||
pc->yieldCount++;
|
||||
pc->yieldNode = pn;
|
||||
pc->yieldOffset = handler.getPosition(pn).begin;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -5215,7 +5236,7 @@ class GenexpGuard
|
|||
ParseContext<ParseHandler> *pc = parser->pc;
|
||||
if (pc->parenDepth == 0) {
|
||||
pc->yieldCount = 0;
|
||||
pc->yieldNode = ParseHandler::null();
|
||||
pc->yieldOffset = 0;
|
||||
}
|
||||
startYieldCount = pc->yieldCount;
|
||||
pc->parenDepth++;
|
||||
|
@ -5246,10 +5267,12 @@ GenexpGuard<ParseHandler>::checkValidBody(Node pn, unsigned err)
|
|||
{
|
||||
ParseContext<ParseHandler> *pc = parser->pc;
|
||||
if (pc->yieldCount > startYieldCount) {
|
||||
Node errorNode = pc->yieldNode;
|
||||
if (!errorNode)
|
||||
errorNode = pn;
|
||||
parser->report(ParseError, false, errorNode, err, js_yield_str);
|
||||
uint32_t offset = pc->yieldOffset
|
||||
? pc->yieldOffset
|
||||
: (pn ? parser->handler.getPosition(pn)
|
||||
: parser->tokenStream.currentToken().pos).begin;
|
||||
|
||||
parser->reportWithOffset(ParseError, false, offset, err, js_yield_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -155,10 +155,10 @@ struct ParseContext /* tree context for semantic checks */
|
|||
bool generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const;
|
||||
|
||||
public:
|
||||
Node yieldNode; /* parse node for a yield expression that might
|
||||
be an error if we turn out to be inside a
|
||||
generator expression */
|
||||
|
||||
uint32_t yieldOffset; /* offset of a yield expression that might
|
||||
be an error if we turn out to be inside
|
||||
a generator expression. Zero means
|
||||
there isn't one. */
|
||||
private:
|
||||
ParseContext **parserPC; /* this points to the Parser's active pc
|
||||
and holds either |this| or one of
|
||||
|
@ -296,7 +296,13 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
|||
/* State specific to the kind of parse being performed. */
|
||||
ParseHandler handler;
|
||||
|
||||
private:
|
||||
bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
|
||||
unsigned errorNumber, va_list args);
|
||||
public:
|
||||
bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
|
||||
bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber,
|
||||
...);
|
||||
|
||||
Parser(JSContext *cx, const CompileOptions &options,
|
||||
const jschar *chars, size_t length, bool foldConstants);
|
||||
|
|
|
@ -395,6 +395,46 @@ class StrictModeGetter {
|
|||
virtual bool strictMode() = 0;
|
||||
};
|
||||
|
||||
// TokenStream is the lexical scanner for Javascript source text.
|
||||
//
|
||||
// It takes a buffer of jschars and linearly scans it into |Token|s.
|
||||
// Internally the class uses a four element circular buffer |tokens| of
|
||||
// |Token|s. As an index for |tokens|, the member |cursor| points to the
|
||||
// current token.
|
||||
// Calls to getToken() increase |cursor| by one and return the new current
|
||||
// token. If a TokenStream was just created, the current token is initialized
|
||||
// with random data (i.e. not initialized). It is therefore important that
|
||||
// either of the first four member functions listed below is called first.
|
||||
// The circular buffer lets us go back up to two tokens from the last
|
||||
// scanned token. Internally, the relative number of backward steps that were
|
||||
// taken (via ungetToken()) after the last token was scanned is stored in
|
||||
// |lookahead|.
|
||||
//
|
||||
// The following table lists in which situations it is safe to call each listed
|
||||
// function. No checks are made by the functions in non-debug builds.
|
||||
//
|
||||
// Function Name | Precondition; changes to |lookahead|
|
||||
// ------------------+---------------------------------------------------------
|
||||
// getToken | none; if |lookahead > 0| then |lookahead--|
|
||||
// peekToken | none; none
|
||||
// peekTokenSameLine | none; none
|
||||
// matchToken | none; if |lookahead > 0| and the match succeeds then
|
||||
// | |lookahead--|
|
||||
// consumeKnownToken | none; if |lookahead > 0| then |lookahead--|
|
||||
// ungetToken | 0 <= |lookahead| <= |maxLookahead - 1|; |lookahead++|
|
||||
//
|
||||
// The behavior of the token scanning process (see getTokenInternal()) can be
|
||||
// modified by calling one of the first four above listed member functions with
|
||||
// an optional argument of type TokenStreamFlags. The two flags that do
|
||||
// influence the scanning process are TSF_OPERAND and TSF_KEYWORD_IS_NAME.
|
||||
// However, they will be ignored unless |lookahead == 0| holds.
|
||||
// Due to constraints of the grammar, this turns out not to be a problem in
|
||||
// practice. See the mozilla.dev.tech.js-engine.internals thread entitled 'Bug
|
||||
// in the scanner?' for more details (https://groups.google.com/forum/?
|
||||
// fromgroups=#!topic/mozilla.dev.tech.js-engine.internals/2JLH5jRcr7E).
|
||||
//
|
||||
// The methods seek() and tell() allow to rescan from a previous visited
|
||||
// location of the buffer.
|
||||
class TokenStream
|
||||
{
|
||||
/* Unicode separators that are treated as line terminators, in addition to \n, \r */
|
||||
|
@ -535,7 +575,7 @@ class TokenStream
|
|||
* Push the last scanned token back into the stream.
|
||||
*/
|
||||
void ungetToken() {
|
||||
JS_ASSERT(lookahead < ntokensMask);
|
||||
JS_ASSERT(lookahead < maxLookahead);
|
||||
lookahead++;
|
||||
cursor = (cursor - 1) & ntokensMask;
|
||||
}
|
||||
|
|
|
@ -255,7 +255,7 @@ class RelocatablePtr : public EncapsulatedPtr<T>
|
|||
if (v)
|
||||
post();
|
||||
}
|
||||
explicit RelocatablePtr(const RelocatablePtr<T> &v) : EncapsulatedPtr<T>(v) {
|
||||
RelocatablePtr(const RelocatablePtr<T> &v) : EncapsulatedPtr<T>(v) {
|
||||
if (this->value)
|
||||
post();
|
||||
}
|
||||
|
|
|
@ -99,3 +99,35 @@ function assertTypeFailInEval(str)
|
|||
assertTypeFailInEval('function f({}) { "use asm"; function g() {} return g }');
|
||||
assertTypeFailInEval('function f({global}) { "use asm"; function g() {} return g }');
|
||||
assertTypeFailInEval('function f(global, {imports}) { "use asm"; function g() {} return g }');
|
||||
|
||||
function assertLinkFailInEval(str)
|
||||
{
|
||||
if (!isAsmJSCompilationAvailable())
|
||||
return;
|
||||
|
||||
var caught = false;
|
||||
var oldOpts = options("werror");
|
||||
assertEq(oldOpts.indexOf("werror"), -1);
|
||||
try {
|
||||
eval(str);
|
||||
} catch (e) {
|
||||
assertEq((''+e).indexOf(ASM_OK_STRING) == -1, false);
|
||||
caught = true;
|
||||
}
|
||||
assertEq(caught, true);
|
||||
options("werror");
|
||||
|
||||
var code = eval(str);
|
||||
|
||||
var caught = false;
|
||||
var oldOpts = options("werror");
|
||||
assertEq(oldOpts.indexOf("werror"), -1);
|
||||
try {
|
||||
code.apply(null, Array.slice(arguments, 1));
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
assertEq(caught, true);
|
||||
options("werror");
|
||||
}
|
||||
assertLinkFailInEval('(function(global) { "use asm"; var im=global.Math.imul; function g() {} return g })');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// |jit-test| error:InternalError
|
||||
// |jit-test| slow; error:InternalError
|
||||
|
||||
// Binary: cache/js-dbg-64-a2bbe9c999b4-linux
|
||||
// Flags: -m -n
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// Don't assert.
|
||||
try {
|
||||
eval("function x(y = {\
|
||||
x: (7) ? 0 : yield(0)\
|
||||
}");
|
||||
} catch (e) {}
|
|
@ -165,7 +165,7 @@ class JSFunction : public JSObject
|
|||
}
|
||||
|
||||
JSAtom *atom() const { return hasGuessedAtom() ? NULL : atom_.get(); }
|
||||
js::PropertyName *name() const { return hasGuessedAtom() ? NULL : atom_->asPropertyName(); }
|
||||
js::PropertyName *name() const { return hasGuessedAtom() || !atom_ ? NULL : atom_->asPropertyName(); }
|
||||
inline void initAtom(JSAtom *atom);
|
||||
JSAtom *displayAtom() const { return atom_; }
|
||||
|
||||
|
|
|
@ -235,6 +235,7 @@ JSObject::finalize(js::FreeOp *fop)
|
|||
js::Probes::finalizeObject(this);
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(isTenured());
|
||||
if (!IsBackgroundFinalized(tenuredGetAllocKind())) {
|
||||
/* Assert we're on the main thread. */
|
||||
fop->runtime()->assertValidThread();
|
||||
|
|
|
@ -2593,7 +2593,6 @@ template<typename T>
|
|||
JSBool
|
||||
ArrayBufferObject::createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
typedef TypedArrayTemplate<T> ArrayType;
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args);
|
||||
}
|
||||
|
|
До Ширина: | Высота: | Размер: 41 KiB После Ширина: | Высота: | Размер: 41 KiB |
|
@ -1105,8 +1105,23 @@ nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPres
|
|||
}
|
||||
}
|
||||
|
||||
if (!overflowContainers)
|
||||
// Our own excess overflow containers from a previous reflow can still be
|
||||
// present if our next-in-flow hasn't been reflown yet.
|
||||
nsFrameList* selfExcessOCFrames =
|
||||
RemovePropTableFrames(aPresContext, ExcessOverflowContainersProperty());
|
||||
if (selfExcessOCFrames) {
|
||||
if (overflowContainers) {
|
||||
overflowContainers->AppendFrames(nullptr, *selfExcessOCFrames);
|
||||
delete selfExcessOCFrames;
|
||||
} else {
|
||||
overflowContainers = selfExcessOCFrames;
|
||||
SetPropTableFrames(aPresContext, overflowContainers,
|
||||
OverflowContainersProperty());
|
||||
}
|
||||
}
|
||||
if (!overflowContainers) {
|
||||
return NS_OK; // nothing to reflow
|
||||
}
|
||||
|
||||
nsOverflowContinuationTracker tracker(aPresContext, this, false, false);
|
||||
bool shouldReflowAllKids = aReflowState.ShouldReflowAllKids();
|
||||
|
@ -1655,7 +1670,17 @@ nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
|
|||
nsresult rv = NS_OK;
|
||||
bool reparented = false;
|
||||
nsPresContext* presContext = aOverflowCont->PresContext();
|
||||
const bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow();
|
||||
bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow();
|
||||
|
||||
// If we have a list and aOverflowCont is already in it then don't try to
|
||||
// add it again.
|
||||
if (addToList && aOverflowCont->GetParent() == mParent &&
|
||||
(aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
|
||||
mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) {
|
||||
addToList = false;
|
||||
mPrevOverflowCont = aOverflowCont->GetPrevSibling();
|
||||
}
|
||||
|
||||
if (addToList) {
|
||||
if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
|
||||
// aOverflowCont is in some other overflow container list,
|
||||
|
@ -1682,6 +1707,27 @@ nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
|
|||
mParent);
|
||||
reparented = true;
|
||||
}
|
||||
|
||||
// If aOverflowCont has a prev/next-in-flow that might be in
|
||||
// mOverflowContList we need to find it and insert after/before it to
|
||||
// maintain the order amongst next-in-flows in this list.
|
||||
nsIFrame* pif = aOverflowCont->GetPrevInFlow();
|
||||
nsIFrame* nif = aOverflowCont->GetNextInFlow();
|
||||
if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) ||
|
||||
(nif && nif->GetParent() == mParent && mPrevOverflowCont)) {
|
||||
for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) {
|
||||
nsIFrame* f = e.get();
|
||||
if (f == pif) {
|
||||
mPrevOverflowCont = pif;
|
||||
break;
|
||||
}
|
||||
if (f == nif) {
|
||||
mPrevOverflowCont = f->GetPrevSibling();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
|
||||
aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ PARALLEL_DIRS += [
|
|||
'mathml',
|
||||
'inspector/public',
|
||||
'inspector/src',
|
||||
'tools/recording',
|
||||
]
|
||||
|
||||
if CONFIG['NS_PRINTING']:
|
||||
|
|
До Ширина: | Высота: | Размер: 396 B После Ширина: | Высота: | Размер: 396 B |
До Ширина: | Высота: | Размер: 843 B После Ширина: | Высота: | Размер: 843 B |
|
@ -3091,7 +3091,10 @@ nsSVGTextFrame2::FindCloserFrameForSelection(
|
|||
nsPoint aPoint,
|
||||
nsIFrame::FrameWithDistance* aCurrentBestFrame)
|
||||
{
|
||||
UpdateGlyphPositioning(false);
|
||||
if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
|
||||
return;
|
||||
}
|
||||
UpdateGlyphPositioning(true);
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# vim: set shiftwidth=8 tabstop=8 autoindent noexpandtab copyindent:
|
||||
# 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/.
|
||||
|
||||
DEPTH = @DEPTH@
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
recording-cmdline.js \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_COMPONENTS += recording-cmdline.manifest
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,4 @@
|
|||
recording.jar:
|
||||
% content recording %content/
|
||||
content/recording.xul (recording.xul)
|
||||
content/recording.js (recording.js)
|
|
@ -0,0 +1,6 @@
|
|||
# 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/.
|
||||
|
||||
MODULE = 'recording'
|
|
@ -0,0 +1,73 @@
|
|||
/* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const nsISupports = Components.interfaces.nsISupports;
|
||||
|
||||
const nsICommandLine = Components.interfaces.nsICommandLine;
|
||||
const nsICommandLineHandler = Components.interfaces.nsICommandLineHandler;
|
||||
const nsISupportsString = Components.interfaces.nsISupportsString;
|
||||
const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher;
|
||||
|
||||
function RecordingCmdLineHandler() {}
|
||||
RecordingCmdLineHandler.prototype =
|
||||
{
|
||||
classID: Components.ID('{86FB70EC-90FF-45AD-A1C1-F77D3C1184E9}'),
|
||||
|
||||
/* nsISupports */
|
||||
QueryInterface: XPCOMUtils.generateQI([nsICommandLineHandler]),
|
||||
|
||||
/* nsICommandLineHandler */
|
||||
handle : function handler_handle(cmdLine) {
|
||||
var args = { };
|
||||
args.wrappedJSObject = args;
|
||||
try {
|
||||
var uristr = cmdLine.handleFlagWithParam("recording", false);
|
||||
if (uristr == null)
|
||||
return;
|
||||
try {
|
||||
args.uri = cmdLine.resolveURI(uristr).spec;
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
cmdLine.handleFlag("recording", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manipulate preferences by adding to the *default* branch. Adding
|
||||
* to the default branch means the changes we make won't get written
|
||||
* back to user preferences.
|
||||
*
|
||||
* We want to do this here rather than in reftest.js because it's
|
||||
* important to set the recording pref before the platform Init gets
|
||||
* called.
|
||||
*/
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefService);
|
||||
var branch = prefs.getDefaultBranch("");
|
||||
branch.setBoolPref("gfx.2d.recording", true);
|
||||
|
||||
try {
|
||||
var outputstr = cmdLine.handleFlagWithParam("recording-output", false);
|
||||
if (outputstr != null) {
|
||||
branch.setCharPref("gfx.2d.recordingfile", outputstr);
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(nsIWindowWatcher);
|
||||
wwatch.openWindow(null, "chrome://recording/content/recording.xul", "_blank",
|
||||
"chrome,dialog=no,all", args);
|
||||
cmdLine.preventDefault = true;
|
||||
},
|
||||
|
||||
helpInfo : " -recording <file> Record drawing for a given URL.\n" +
|
||||
" -recording-output <file> Specify destination file for a drawing recording.\n"
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RecordingCmdLineHandler]);
|
|
@ -0,0 +1,3 @@
|
|||
component {86FB70EC-90FF-45AD-A1C1-F77D3C1184E9} recording-cmdline.js
|
||||
contract @mozilla.org/commandlinehandler/general-startup;1?type=recording {86FB70EC-90FF-45AD-A1C1-F77D3C1184E9}
|
||||
category command-line-handler m-recording @mozilla.org/commandlinehandler/general-startup;1?type=recording
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
|
||||
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
|
||||
/* 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 CC = Components.classes;
|
||||
const CI = Components.interfaces;
|
||||
const CR = Components.results;
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const NS_GFXINFO_CONTRACTID = "@mozilla.org/gfx/info;1";
|
||||
|
||||
var gContainingWindow = null;
|
||||
|
||||
var gBrowser;
|
||||
|
||||
function OnDocumentLoad() {
|
||||
gContainingWindow.close();
|
||||
}
|
||||
|
||||
this.OnRecordingLoad = function OnRecordingLoad(win) {
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
|
||||
if (win === undefined || win == null) {
|
||||
win = window;
|
||||
}
|
||||
if (gContainingWindow == null && win != null) {
|
||||
gContainingWindow = win;
|
||||
}
|
||||
|
||||
gBrowser = gContainingWindow.document.getElementById("browser");
|
||||
|
||||
var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo);
|
||||
var info = gfxInfo.getInfo();
|
||||
dump(info.AzureContentBackend);
|
||||
if (info.AzureContentBackend == "none") {
|
||||
alert("Page recordings may only be made with Azure content enabled.");
|
||||
gContainingWindow.close();
|
||||
return;
|
||||
}
|
||||
|
||||
gContainingWindow.document.addEventListener("load", OnDocumentLoad, true);
|
||||
|
||||
var args = window.arguments[0].wrappedJSObject;
|
||||
|
||||
gBrowser.loadURI(args.uri);
|
||||
}
|
||||
|
||||
function OnRecordingUnload() {
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<!-- vim: set shiftwidth=4 tabstop=8 autoindent expandtab: -->
|
||||
<!-- 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/. -->
|
||||
<?xml-stylesheet type="text/css" href="data:text/css,
|
||||
|
||||
%23_box_windowsDefaultTheme:-moz-system-metric(windows-default-theme) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
" ?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="recording-window"
|
||||
hidechrome="true"
|
||||
onload="OnRecordingLoad();"
|
||||
onunload="OnRecordingUnload();"
|
||||
style="background:white; overflow:hidden; width:800px; height:600px;"
|
||||
>
|
||||
<script type="application/ecmascript" src="recording.js" />
|
||||
<browser id="browser" type="content-primary" style="min-width: 1024px; min-height: 768px; max-width: 1024px; max-height: 768px"/>
|
||||
</window>
|
|
@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
|
|||
|
||||
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
|
||||
|
||||
The git commit ID used was c9c97571980ca77c990a763802c11682a332cbd6.
|
||||
The git commit ID used was bfaee9a6c7a15dfe8ae2ffda8dcd156d5a562010.
|
||||
|
|
|
@ -52,8 +52,9 @@ struct cubeb_stream {
|
|||
};
|
||||
|
||||
static void
|
||||
bufferqueue_callback(SLBufferQueueItf caller, struct cubeb_stream *stm)
|
||||
bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||
{
|
||||
cubeb_stream * stm = user_ptr;
|
||||
SLBufferQueueState state;
|
||||
(*stm->bufq)->GetState(stm->bufq, &state);
|
||||
|
||||
|
@ -78,8 +79,13 @@ bufferqueue_callback(SLBufferQueueItf caller, struct cubeb_stream *stm)
|
|||
return;
|
||||
}
|
||||
|
||||
(*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize);
|
||||
stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
|
||||
if (written) {
|
||||
(*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize);
|
||||
stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
|
||||
} else if (!i) {
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((written * stm->framesize) < stm->queuebuf_len) {
|
||||
stm->draining = 1;
|
||||
|
|
|
@ -27,7 +27,7 @@ if [ -n "$rev" ]; then
|
|||
version=$version-dirty
|
||||
echo "WARNING: updating from a dirty git repository."
|
||||
fi
|
||||
sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\+\(-dirty\)\?\./$version./" README_MOZILLA
|
||||
sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\{40\}\(-dirty\)\{0,1\}\./$version./" README_MOZILLA
|
||||
rm README_MOZILLA.bak
|
||||
else
|
||||
echo "Remember to update README_MOZILLA with the version details."
|
||||
|
|
|
@ -229,9 +229,6 @@ int NrIceCtx::ice_completed(void *obj, nr_ice_peer_ctx *pctx) {
|
|||
|
||||
ctx->SetState(ICE_CTX_OPEN);
|
||||
|
||||
// Signal that we are done
|
||||
ctx->SignalCompleted(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -413,12 +410,21 @@ nsresult NrIceCtx::SetResolver(nr_resolver *resolver) {
|
|||
}
|
||||
|
||||
nsresult NrIceCtx::StartGathering() {
|
||||
MOZ_ASSERT(ctx_->state == ICE_CTX_INIT);
|
||||
if (ctx_->state != ICE_CTX_INIT) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "ICE ctx in the wrong state for gathering: '"
|
||||
<< name_ << "'");
|
||||
SetState(ICE_CTX_FAILED);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int r = nr_ice_initialize(ctx_, &NrIceCtx::initialized_cb,
|
||||
this);
|
||||
|
||||
if (r && r != R_WOULDBLOCK) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Couldn't gather ICE candidates for '"
|
||||
<< name_ << "'");
|
||||
SetState(ICE_CTX_FAILED);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -434,8 +440,6 @@ void NrIceCtx::EmitAllCandidates() {
|
|||
for(size_t i=0; i<streams_.size(); ++i) {
|
||||
streams_[i]->EmitAllCandidates();
|
||||
}
|
||||
|
||||
SignalGatheringCompleted(this);
|
||||
}
|
||||
|
||||
RefPtr<NrIceMediaStream> NrIceCtx::FindStream(
|
||||
|
@ -498,6 +502,7 @@ nsresult NrIceCtx::StartChecks() {
|
|||
if (r) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Couldn't pair candidates on "
|
||||
<< name_ << "'");
|
||||
SetState(ICE_CTX_FAILED);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -509,6 +514,7 @@ nsresult NrIceCtx::StartChecks() {
|
|||
} else {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Couldn't start peer checks on "
|
||||
<< name_ << "'");
|
||||
SetState(ICE_CTX_FAILED);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
|
@ -522,10 +528,9 @@ nsresult NrIceCtx::StartChecks() {
|
|||
void NrIceCtx::initialized_cb(NR_SOCKET s, int h, void *arg) {
|
||||
NrIceCtx *ctx = static_cast<NrIceCtx *>(arg);
|
||||
|
||||
ctx->SetState(ICE_CTX_GATHERED);
|
||||
|
||||
// Report that we are done gathering
|
||||
ctx->EmitAllCandidates();
|
||||
|
||||
ctx->SetState(ICE_CTX_GATHERED);
|
||||
}
|
||||
|
||||
nsresult NrIceCtx::Finalize() {
|
||||
|
@ -541,12 +546,29 @@ nsresult NrIceCtx::Finalize() {
|
|||
}
|
||||
|
||||
void NrIceCtx::SetState(State state) {
|
||||
MOZ_MTLOG(PR_LOG_DEBUG, "NrIceCtx(" << name_ << "): state " <<
|
||||
state_ << "->" << state);
|
||||
state_ = state;
|
||||
}
|
||||
} // close namespace
|
||||
if (state == state_)
|
||||
return;
|
||||
|
||||
MOZ_MTLOG(PR_LOG_DEBUG, "NrIceCtx(" << name_ << "): state " <<
|
||||
state_ << "->" << state);
|
||||
state_ = state;
|
||||
|
||||
switch(state_) {
|
||||
case ICE_CTX_GATHERED:
|
||||
SignalGatheringCompleted(this);
|
||||
break;
|
||||
case ICE_CTX_OPEN:
|
||||
SignalCompleted(this);
|
||||
break;
|
||||
case ICE_CTX_FAILED:
|
||||
SignalFailed(this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // close namespace
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
|
|
@ -198,8 +198,11 @@ class NrIceCtx {
|
|||
|
||||
// Signals to indicate events. API users can (and should)
|
||||
// register for these.
|
||||
// TODO(ekr@rtfm.com): refactor this to be state change instead
|
||||
// so we don't need to keep adding signals?
|
||||
sigslot::signal1<NrIceCtx *> SignalGatheringCompleted; // Done gathering
|
||||
sigslot::signal1<NrIceCtx *> SignalCompleted; // Done handshaking
|
||||
sigslot::signal1<NrIceCtx *> SignalFailed; // Failure.
|
||||
|
||||
// The thread to direct method calls to
|
||||
nsCOMPtr<nsIEventTarget> thread() { return sts_target_; }
|
||||
|
|
|
@ -211,14 +211,15 @@ class TransportTestPeer : public sigslot::has_slots<> {
|
|||
ice_ = new TransportLayerIce(name, ice_ctx_, stream, 1);
|
||||
|
||||
// Assemble the stack
|
||||
std::queue<mozilla::TransportLayer *> layers;
|
||||
layers.push(ice_);
|
||||
layers.push(dtls_);
|
||||
nsAutoPtr<std::queue<mozilla::TransportLayer *> > layers(
|
||||
new std::queue<mozilla::TransportLayer *>);
|
||||
layers->push(ice_);
|
||||
layers->push(dtls_);
|
||||
|
||||
test_utils->sts_target()->Dispatch(
|
||||
WrapRunnableRet(flow_, &TransportFlow::PushLayers, layers, &res),
|
||||
NS_DISPATCH_SYNC);
|
||||
|
||||
|
||||
ASSERT_EQ((nsresult)NS_OK, res);
|
||||
|
||||
// Listen for media events
|
||||
|
|
|
@ -7,11 +7,16 @@
|
|||
// Original author: ekr@rtfm.com
|
||||
#include <deque>
|
||||
|
||||
#include <prlog.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "transportflow.h"
|
||||
#include "transportlayer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MOZ_MTLOG_MODULE("mtransport")
|
||||
|
||||
TransportFlow::~TransportFlow() {
|
||||
for (std::deque<TransportLayer *>::iterator it = layers_.begin();
|
||||
it != layers_.end(); ++it) {
|
||||
|
@ -20,9 +25,19 @@ TransportFlow::~TransportFlow() {
|
|||
}
|
||||
|
||||
nsresult TransportFlow::PushLayer(TransportLayer *layer) {
|
||||
// Don't allow pushes once we are in error state.
|
||||
if (state_ == TransportLayer::TS_ERROR) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, id_ + ": Can't call PushLayer in error state for flow ");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = layer->Init();
|
||||
if (!NS_SUCCEEDED(rv))
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
// Set ourselves to have failed.
|
||||
MOZ_MTLOG(PR_LOG_ERROR, id_ << ": Layer initialization failed; invalidating");
|
||||
StateChangeInt(TransportLayer::TS_ERROR);
|
||||
return rv;
|
||||
}
|
||||
|
||||
TransportLayer *old_layer = layers_.empty() ? nullptr : layers_.front();
|
||||
|
||||
|
@ -31,32 +46,79 @@ nsresult TransportFlow::PushLayer(TransportLayer *layer) {
|
|||
old_layer->SignalStateChange.disconnect(this);
|
||||
old_layer->SignalPacketReceived.disconnect(this);
|
||||
}
|
||||
layer->SignalStateChange.connect(this, &TransportFlow::StateChange);
|
||||
layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived);
|
||||
|
||||
layers_.push_front(layer);
|
||||
layer->Inserted(this, old_layer);
|
||||
|
||||
layer->SignalStateChange.connect(this, &TransportFlow::StateChange);
|
||||
layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived);
|
||||
StateChangeInt(layer->state());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult TransportFlow::PushLayers(std::queue<TransportLayer *> layers) {
|
||||
nsresult rv;
|
||||
|
||||
while (!layers.empty()) {
|
||||
rv = PushLayer(layers.front());
|
||||
layers.pop();
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
// Destroy any layers we could not push.
|
||||
while (!layers.empty()) {
|
||||
delete layers.front();
|
||||
layers.pop();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
// This is all-or-nothing.
|
||||
nsresult TransportFlow::PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers) {
|
||||
MOZ_ASSERT(!layers->empty());
|
||||
if (layers->empty()) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, id_ << ": Can't call PushLayers with empty layers");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Don't allow pushes once we are in error state.
|
||||
if (state_ == TransportLayer::TS_ERROR) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, id_ << ": Can't call PushLayers in error state for flow ");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Disconnect all the old signals.
|
||||
disconnect_all();
|
||||
|
||||
TransportLayer *layer;
|
||||
|
||||
while (!layers->empty()) {
|
||||
TransportLayer *old_layer = layers_.empty() ? nullptr : layers_.front();
|
||||
layer = layers->front();
|
||||
|
||||
rv = layer->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, id_ << ": Layer initialization failed; invalidating flow ");
|
||||
break;
|
||||
}
|
||||
|
||||
// Push the layer onto the queue.
|
||||
layers_.push_front(layer);
|
||||
layers->pop();
|
||||
layer->Inserted(this, old_layer);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
// Destroy any layers we could not push.
|
||||
while (!layers->empty()) {
|
||||
delete layers->front();
|
||||
layers->pop();
|
||||
}
|
||||
|
||||
// Now destroy the rest of the flow, because it's no longer
|
||||
// in an acceptable state.
|
||||
while (!layers_.empty()) {
|
||||
delete layers_.front();
|
||||
layers_.pop_front();
|
||||
}
|
||||
|
||||
// Set ourselves to have failed.
|
||||
StateChangeInt(TransportLayer::TS_ERROR);
|
||||
|
||||
// Return failure.
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Finally, attach ourselves to the top layer.
|
||||
layer->SignalStateChange.connect(this, &TransportFlow::StateChange);
|
||||
layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived);
|
||||
StateChangeInt(layer->state()); // Signals if the state changes.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -75,17 +137,29 @@ TransportLayer *TransportFlow::GetLayer(const std::string& id) const {
|
|||
}
|
||||
|
||||
TransportLayer::State TransportFlow::state() {
|
||||
return top() ? top()->state() : TransportLayer::TS_NONE;
|
||||
return state_;
|
||||
}
|
||||
|
||||
TransportResult TransportFlow::SendPacket(const unsigned char *data,
|
||||
size_t len) {
|
||||
if (state_ != TransportLayer::TS_OPEN) {
|
||||
return TE_ERROR;
|
||||
}
|
||||
return top() ? top()->SendPacket(data, len) : TE_ERROR;
|
||||
}
|
||||
|
||||
void TransportFlow::StateChangeInt(TransportLayer::State state) {
|
||||
if (state == state_) {
|
||||
return;
|
||||
}
|
||||
|
||||
state_ = state;
|
||||
SignalStateChange(this, state_);
|
||||
}
|
||||
|
||||
void TransportFlow::StateChange(TransportLayer *layer,
|
||||
TransportLayer::State state) {
|
||||
SignalStateChange(this, state);
|
||||
StateChangeInt(state);
|
||||
}
|
||||
|
||||
void TransportFlow::PacketReceived(TransportLayer* layer,
|
||||
|
@ -94,9 +168,4 @@ void TransportFlow::PacketReceived(TransportLayer* layer,
|
|||
SignalPacketReceived(this, data, len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // close namespace
|
||||
|
|
|
@ -24,8 +24,13 @@ namespace mozilla {
|
|||
|
||||
class TransportFlow : public sigslot::has_slots<> {
|
||||
public:
|
||||
TransportFlow() : id_("(anonymous)") {}
|
||||
TransportFlow(const std::string id) : id_(id) {}
|
||||
TransportFlow()
|
||||
: id_("(anonymous)"),
|
||||
state_(TransportLayer::TS_NONE) {}
|
||||
TransportFlow(const std::string id)
|
||||
: id_(id),
|
||||
state_(TransportLayer::TS_NONE) {}
|
||||
|
||||
~TransportFlow();
|
||||
|
||||
const std::string& id() const { return id_; }
|
||||
|
@ -41,10 +46,10 @@ class TransportFlow : public sigslot::has_slots<> {
|
|||
|
||||
// Convenience function to push multiple layers on. Layers
|
||||
// are pushed on in the order that they are in the queue.
|
||||
// Any layers which cannot be pushed on are just deleted
|
||||
// and an error is returned.
|
||||
// Any failures cause the flow to become inoperable and
|
||||
// destroys all the layers including those already pushed.
|
||||
// TODO(ekr@rtfm.com): Change layers to be ref-counted.
|
||||
nsresult PushLayers(std::queue<TransportLayer *> layers);
|
||||
nsresult PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers);
|
||||
|
||||
TransportLayer *top() const;
|
||||
TransportLayer *GetLayer(const std::string& id) const;
|
||||
|
@ -68,11 +73,13 @@ class TransportFlow : public sigslot::has_slots<> {
|
|||
DISALLOW_COPY_ASSIGN(TransportFlow);
|
||||
|
||||
void StateChange(TransportLayer *layer, TransportLayer::State state);
|
||||
void StateChangeInt(TransportLayer::State state);
|
||||
void PacketReceived(TransportLayer* layer, const unsigned char *data,
|
||||
size_t len);
|
||||
|
||||
std::string id_;
|
||||
std::deque<TransportLayer *> layers_;
|
||||
TransportLayer::State state_;
|
||||
};
|
||||
|
||||
} // close namespace
|
||||
|
|
|
@ -705,20 +705,19 @@ static short vcmSetIceCandidate_m(const char *peerconnection,
|
|||
if (!stream)
|
||||
return VCM_ERROR;
|
||||
|
||||
nsresult res;
|
||||
nsresult rv = pc.impl()->media()->ice_ctx()->thread()->Dispatch(
|
||||
WrapRunnableRet(stream, &NrIceMediaStream::ParseTrickleCandidate, icecandidate, &res),
|
||||
NS_DISPATCH_SYNC);
|
||||
nsresult rv = RUN_ON_THREAD(pc.impl()->media()->ice_ctx()->thread(),
|
||||
WrapRunnable(stream,
|
||||
&NrIceMediaStream::ParseTrickleCandidate,
|
||||
std::string(icecandidate)),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
CSFLogError( logTag, "%s(): Could not dispatch to ICE thread", __FUNCTION__, level);
|
||||
return VCM_ERROR;
|
||||
}
|
||||
|
||||
if (!NS_SUCCEEDED(res)) {
|
||||
CSFLogError( logTag, "%s(): Could not parse trickle candidate for stream %d", __FUNCTION__, level);
|
||||
return VCM_ERROR;
|
||||
}
|
||||
// TODO(ekr@rtfm.com): generate an error if the parse
|
||||
// fails. Bug 847449.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -769,18 +768,17 @@ static short vcmStartIceChecks_m(const char *peerconnection, cc_boolean isContro
|
|||
CSFLogError( logTag, "%s: couldn't set controlling", __FUNCTION__ );
|
||||
return VCM_ERROR;
|
||||
}
|
||||
// TODO(ekr@rtfm.com): Figure out how to report errors here.
|
||||
// Bug 854516.
|
||||
nsresult rv = pc.impl()->media()->ice_ctx()->thread()->Dispatch(
|
||||
WrapRunnableRet(pc.impl()->media()->ice_ctx(), &NrIceCtx::StartChecks, &res),
|
||||
NS_DISPATCH_SYNC);
|
||||
WrapRunnable(pc.impl()->media()->ice_ctx(), &NrIceCtx::StartChecks),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
CSFLogError( logTag, "%s(): Could not dispatch to ICE thread", __FUNCTION__);
|
||||
return VCM_ERROR;
|
||||
}
|
||||
if (!NS_SUCCEEDED(res)) {
|
||||
CSFLogError( logTag, "%s: couldn't start ICE checks", __FUNCTION__ );
|
||||
return VCM_ERROR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2683,19 +2681,24 @@ vcmCreateTransportFlow(sipcc::PeerConnectionImpl *pc, int level, bool rtcp,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
std::queue<TransportLayer *> layers;
|
||||
layers.push(ice.forget());
|
||||
layers.push(dtls.forget());
|
||||
nsAutoPtr<std::queue<TransportLayer *> > layers(new std::queue<TransportLayer *>);
|
||||
layers->push(ice.forget());
|
||||
layers->push(dtls.forget());
|
||||
|
||||
|
||||
// Layers are now owned by the flow.
|
||||
// TODO(ekr@rtfm.com): Propagate errors for when this fails.
|
||||
// Bug 854518.
|
||||
nsresult rv = pc->media()->ice_ctx()->thread()->Dispatch(
|
||||
WrapRunnableRet(flow, &TransportFlow::PushLayers, layers, &res),
|
||||
NS_DISPATCH_SYNC);
|
||||
WrapRunnable(flow, &TransportFlow::PushLayers, layers),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
if (NS_FAILED(rv) || NS_FAILED(res) || !pc->media().get()) { // SYNC re-check
|
||||
if (NS_FAILED(rv)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Note, this flow may actually turn out to be invalid
|
||||
// because PushLayers is async and can fail.
|
||||
pc->media()->AddTransportFlow(level, rtcp, flow);
|
||||
}
|
||||
return flow;
|
||||
|
|
|
@ -54,43 +54,52 @@ static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp";
|
|||
nsresult MediaPipeline::Init() {
|
||||
ASSERT_ON_THREAD(main_thread_);
|
||||
|
||||
// TODO(ekr@rtfm.com): is there a way to make this async?
|
||||
nsresult ret;
|
||||
RUN_ON_THREAD(sts_thread_,
|
||||
WrapRunnableRet(this, &MediaPipeline::Init_s, &ret),
|
||||
NS_DISPATCH_SYNC);
|
||||
return ret;
|
||||
WrapRunnable(
|
||||
nsRefPtr<MediaPipeline>(this),
|
||||
&MediaPipeline::Init_s),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult MediaPipeline::Init_s() {
|
||||
ASSERT_ON_THREAD(sts_thread_);
|
||||
conduit_->AttachTransport(transport_);
|
||||
|
||||
MOZ_ASSERT(rtp_transport_);
|
||||
|
||||
nsresult res;
|
||||
|
||||
MOZ_ASSERT(rtp_transport_);
|
||||
// Look to see if the transport is ready
|
||||
rtp_transport_->SignalStateChange.connect(this,
|
||||
&MediaPipeline::StateChange);
|
||||
|
||||
if (rtp_transport_->state() == TransportLayer::TS_OPEN) {
|
||||
res = TransportReady(rtp_transport_);
|
||||
res = TransportReady_s(rtp_transport_);
|
||||
if (NS_FAILED(res)) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady()");
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady(); res="
|
||||
<< static_cast<uint32_t>(res) << " in " << __FUNCTION__);
|
||||
return res;
|
||||
}
|
||||
} else if (rtp_transport_->state() == TransportLayer::TS_ERROR) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "RTP transport is already in error state");
|
||||
TransportFailed_s(rtp_transport_);
|
||||
return NS_ERROR_FAILURE;
|
||||
} else {
|
||||
if (!muxed_) {
|
||||
rtcp_transport_->SignalStateChange.connect(this,
|
||||
&MediaPipeline::StateChange);
|
||||
|
||||
if (rtcp_transport_->state() == TransportLayer::TS_OPEN) {
|
||||
res = TransportReady(rtcp_transport_);
|
||||
res = TransportReady_s(rtcp_transport_);
|
||||
if (NS_FAILED(res)) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady()");
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady(); res="
|
||||
<< static_cast<uint32_t>(res) << " in " << __FUNCTION__);
|
||||
return res;
|
||||
}
|
||||
} else if (rtcp_transport_->state() == TransportLayer::TS_ERROR) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "RTCP transport is already in error state");
|
||||
TransportFailed_s(rtcp_transport_);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +109,7 @@ nsresult MediaPipeline::Init_s() {
|
|||
|
||||
// Disconnect us from the transport so that we can cleanly destruct
|
||||
// the pipeline on the main thread.
|
||||
void MediaPipeline::DetachTransport_s() {
|
||||
void MediaPipeline::ShutdownTransport_s() {
|
||||
ASSERT_ON_THREAD(sts_thread_);
|
||||
|
||||
disconnect_all();
|
||||
|
@ -109,42 +118,23 @@ void MediaPipeline::DetachTransport_s() {
|
|||
rtcp_transport_ = NULL;
|
||||
}
|
||||
|
||||
void MediaPipeline::DetachTransport() {
|
||||
RUN_ON_THREAD(sts_thread_,
|
||||
WrapRunnable(this, &MediaPipeline::DetachTransport_s),
|
||||
NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
void MediaPipeline::StateChange(TransportFlow *flow, TransportLayer::State state) {
|
||||
if (state == TransportLayer::TS_OPEN) {
|
||||
MOZ_MTLOG(MP_LOG_INFO, "Flow is ready");
|
||||
TransportReady(flow);
|
||||
TransportReady_s(flow);
|
||||
} else if (state == TransportLayer::TS_CLOSED ||
|
||||
state == TransportLayer::TS_ERROR) {
|
||||
TransportFailed(flow);
|
||||
TransportFailed_s(flow);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult MediaPipeline::TransportReady(TransportFlow *flow) {
|
||||
nsresult rv;
|
||||
nsresult res;
|
||||
|
||||
rv = RUN_ON_THREAD(sts_thread_,
|
||||
WrapRunnableRet(this, &MediaPipeline::TransportReadyInt, flow, &res),
|
||||
NS_DISPATCH_SYNC);
|
||||
|
||||
// res is invalid unless the dispatch succeeded
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) {
|
||||
nsresult MediaPipeline::TransportReady_s(TransportFlow *flow) {
|
||||
MOZ_ASSERT(!description_.empty());
|
||||
bool rtcp = !(flow == rtp_transport_);
|
||||
State *state = rtcp ? &rtcp_state_ : &rtp_state_;
|
||||
|
||||
// TODO(ekr@rtfm.com): implement some kind of notification on
|
||||
// failure. bug 852665.
|
||||
if (*state != MP_CONNECTING) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Transport ready for flow in wrong state:" <<
|
||||
description_ << ": " << (rtcp ? "rtcp" : "rtp"));
|
||||
|
@ -166,6 +156,7 @@ nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) {
|
|||
res = dtls->GetSrtpCipher(&cipher_suite);
|
||||
if (NS_FAILED(res)) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Failed to negotiate DTLS-SRTP. This is an error");
|
||||
*state = MP_CLOSED;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -268,7 +259,8 @@ nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult MediaPipeline::TransportFailed(TransportFlow *flow) {
|
||||
nsresult MediaPipeline::TransportFailed_s(TransportFlow *flow) {
|
||||
ASSERT_ON_THREAD(sts_thread_);
|
||||
bool rtcp = !(flow == rtp_transport_);
|
||||
|
||||
State *state = rtcp ? &rtcp_state_ : &rtp_state_;
|
||||
|
@ -368,6 +360,16 @@ void MediaPipeline::RtpPacketReceived(TransportLayer *layer,
|
|||
return;
|
||||
}
|
||||
|
||||
if (rtp_state_ != MP_OPEN) {
|
||||
MOZ_MTLOG(PR_LOG_DEBUG, "Discarding incoming packet; pipeline not open");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rtp_transport_->state() != TransportLayer::TS_OPEN) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Discarding incoming packet; transport not open");
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(rtp_recv_srtp_); // This should never happen
|
||||
|
||||
if (direction_ == TRANSMIT) {
|
||||
|
@ -406,6 +408,16 @@ void MediaPipeline::RtcpPacketReceived(TransportLayer *layer,
|
|||
return;
|
||||
}
|
||||
|
||||
if (rtcp_state_ != MP_OPEN) {
|
||||
MOZ_MTLOG(PR_LOG_DEBUG, "Discarding incoming packet; pipeline not open");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rtcp_transport_->state() != TransportLayer::TS_OPEN) {
|
||||
MOZ_MTLOG(PR_LOG_ERROR, "Discarding incoming packet; transport not open");
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction_ == RECEIVE) {
|
||||
// Discard any RTCP that is being transmitted to us
|
||||
// This will be unnecessary when we have SSRC filtering.
|
||||
|
@ -503,9 +515,9 @@ nsresult MediaPipelineTransmit::Init() {
|
|||
return MediaPipeline::Init();
|
||||
}
|
||||
|
||||
nsresult MediaPipelineTransmit::TransportReady(TransportFlow *flow) {
|
||||
nsresult MediaPipelineTransmit::TransportReady_s(TransportFlow *flow) {
|
||||
// Call base ready function.
|
||||
MediaPipeline::TransportReady(flow);
|
||||
MediaPipeline::TransportReady_s(flow);
|
||||
|
||||
if (flow == rtp_transport_) {
|
||||
// TODO(ekr@rtfm.com): Move onto MSG thread.
|
||||
|
@ -910,7 +922,9 @@ MediaPipelineReceiveVideo::PipelineListener::PipelineListener(
|
|||
SourceMediaStream* source, TrackID track_id)
|
||||
: source_(source),
|
||||
track_id_(track_id),
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
played_(0),
|
||||
#endif
|
||||
width_(640),
|
||||
height_(480),
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
|
|
|
@ -100,13 +100,19 @@ class MediaPipeline : public sigslot::has_slots<> {
|
|||
MOZ_ASSERT(!stream_); // Check that we have shut down already.
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
|
||||
|
||||
// Must be called on the STS thread. Must be called
|
||||
// before ShutdownMedia_m.
|
||||
void ShutdownTransport_s();
|
||||
|
||||
// Must be called on the main thread.
|
||||
void ShutdownMedia_m() {
|
||||
ASSERT_ON_THREAD(main_thread_);
|
||||
// First shut down networking and then disconnect from
|
||||
// the media streams. DetachTransport() is sync so
|
||||
// we are sure that the transport is shut down before
|
||||
// we touch stream_ or conduit_.
|
||||
DetachTransport();
|
||||
|
||||
MOZ_ASSERT(!rtp_transport_);
|
||||
MOZ_ASSERT(!rtcp_transport_);
|
||||
|
||||
if (stream_) {
|
||||
DetachMediaStream();
|
||||
}
|
||||
|
@ -152,8 +158,8 @@ class MediaPipeline : public sigslot::has_slots<> {
|
|||
};
|
||||
friend class PipelineTransport;
|
||||
|
||||
virtual nsresult TransportReady(TransportFlow *flow); // The transport is ready
|
||||
virtual nsresult TransportFailed(TransportFlow *flow); // The transport is down
|
||||
virtual nsresult TransportFailed_s(TransportFlow *flow); // The transport is down
|
||||
virtual nsresult TransportReady_s(TransportFlow *flow); // The transport is ready
|
||||
|
||||
void increment_rtp_packets_sent();
|
||||
void increment_rtcp_packets_sent();
|
||||
|
@ -216,10 +222,6 @@ class MediaPipeline : public sigslot::has_slots<> {
|
|||
|
||||
private:
|
||||
nsresult Init_s();
|
||||
void DetachTransport();
|
||||
void DetachTransport_s();
|
||||
|
||||
nsresult TransportReadyInt(TransportFlow *flow);
|
||||
|
||||
bool IsRtp(const unsigned char *data, size_t len);
|
||||
};
|
||||
|
@ -268,7 +270,7 @@ class MediaPipelineTransmit : public MediaPipeline {
|
|||
}
|
||||
|
||||
// Override MediaPipeline::TransportReady.
|
||||
virtual nsresult TransportReady(TransportFlow *flow);
|
||||
virtual nsresult TransportReady_s(TransportFlow *flow);
|
||||
|
||||
// Separate class to allow ref counting
|
||||
class PipelineListener : public MediaStreamListener {
|
||||
|
@ -507,7 +509,9 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive {
|
|||
private:
|
||||
SourceMediaStream *source_;
|
||||
TrackID track_id_;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
TrackTicks played_; // Amount of media played.
|
||||
#endif
|
||||
int width_;
|
||||
int height_;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
|
|
|
@ -331,7 +331,7 @@ PeerConnectionImpl::CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo
|
|||
static_cast<mozilla::SourceMediaStream*>(comstream->GetStream())->SetPullEnabled(true);
|
||||
|
||||
nsRefPtr<RemoteSourceStreamInfo> remote;
|
||||
remote = new RemoteSourceStreamInfo(comstream);
|
||||
remote = new RemoteSourceStreamInfo(comstream, mMedia);
|
||||
*aInfo = remote;
|
||||
|
||||
return NS_OK;
|
||||
|
@ -498,6 +498,7 @@ PeerConnectionImpl::Initialize(IPeerConnectionObserver* aObserver,
|
|||
// Connect ICE slots.
|
||||
mMedia->SignalIceGatheringCompleted.connect(this, &PeerConnectionImpl::IceGatheringCompleted);
|
||||
mMedia->SignalIceCompleted.connect(this, &PeerConnectionImpl::IceCompleted);
|
||||
mMedia->SignalIceFailed.connect(this, &PeerConnectionImpl::IceFailed);
|
||||
|
||||
// Initialize the media object.
|
||||
if (aRTCConfiguration) {
|
||||
|
@ -590,15 +591,7 @@ PeerConnectionImpl::CreateFakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aR
|
|||
aHint &= ~MEDIA_STREAM_MUTE;
|
||||
}
|
||||
|
||||
nsresult res;
|
||||
if (!mThread || NS_IsMainThread()) {
|
||||
res = MakeMediaStream(mWindow, aHint, aRetval);
|
||||
} else {
|
||||
mThread->Dispatch(WrapRunnableNMRet(
|
||||
&PeerConnectionImpl::MakeMediaStream, mWindow, aHint, aRetval, &res
|
||||
), NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
nsresult res = MakeMediaStream(mWindow, aHint, aRetval);
|
||||
if (NS_FAILED(res)) {
|
||||
return res;
|
||||
}
|
||||
|
@ -1196,19 +1189,9 @@ PeerConnectionImpl::ShutdownMedia(bool aIsSynchronous)
|
|||
if (!mMedia)
|
||||
return;
|
||||
|
||||
// Post back to our own thread to shutdown the media objects.
|
||||
// This avoids reentrancy issues with the garbage collector.
|
||||
// Note that no media calls may be made after this point
|
||||
// because we have removed the pointer.
|
||||
// For the aIsSynchronous case, we *know* the PeerConnection is
|
||||
// still alive, and are shutting it down on network teardown/etc, so
|
||||
// recursive GC isn't an issue. (Recursive GC should assert)
|
||||
|
||||
// Forget the reference so that we can transfer it to
|
||||
// SelfDestruct().
|
||||
RUN_ON_THREAD(mThread, WrapRunnable(mMedia.forget().get(),
|
||||
&PeerConnectionMedia::SelfDestruct),
|
||||
aIsSynchronous ? NS_DISPATCH_SYNC : NS_DISPATCH_NORMAL);
|
||||
mMedia.forget().get()->SelfDestruct();
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
|
@ -1327,34 +1310,11 @@ PeerConnectionImpl::IceGatheringCompleted(NrIceCtx *aCtx)
|
|||
nsRefPtr<PeerConnectionImpl> pc(this);
|
||||
RUN_ON_THREAD(mThread,
|
||||
WrapRunnable(pc,
|
||||
&PeerConnectionImpl::IceGatheringCompleted_m),
|
||||
&PeerConnectionImpl::IceStateChange_m,
|
||||
kIceWaiting),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PeerConnectionImpl::IceGatheringCompleted_m()
|
||||
{
|
||||
PC_AUTO_ENTER_API_CALL(false);
|
||||
|
||||
CSFLogDebug(logTag, __FUNCTION__);
|
||||
|
||||
mIceState = kIceWaiting;
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
|
||||
if (!pco) {
|
||||
return NS_OK;
|
||||
}
|
||||
RUN_ON_THREAD(mThread,
|
||||
WrapRunnable(pco,
|
||||
&IPeerConnectionObserver::OnStateChange,
|
||||
// static_cast required to work around old C++ compiler on Android NDK r5c
|
||||
static_cast<int>(IPeerConnectionObserver::kIceState)),
|
||||
NS_DISPATCH_NORMAL);
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionImpl::IceCompleted(NrIceCtx *aCtx)
|
||||
{
|
||||
|
@ -1363,18 +1323,32 @@ PeerConnectionImpl::IceCompleted(NrIceCtx *aCtx)
|
|||
nsRefPtr<PeerConnectionImpl> pc(this);
|
||||
RUN_ON_THREAD(mThread,
|
||||
WrapRunnable(pc,
|
||||
&PeerConnectionImpl::IceCompleted_m),
|
||||
&PeerConnectionImpl::IceStateChange_m,
|
||||
kIceConnected),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionImpl::IceFailed(NrIceCtx *aCtx)
|
||||
{
|
||||
(void) aCtx;
|
||||
// Do an async call here to unwind the stack. refptr keeps the PC alive.
|
||||
nsRefPtr<PeerConnectionImpl> pc(this);
|
||||
RUN_ON_THREAD(mThread,
|
||||
WrapRunnable(pc,
|
||||
&PeerConnectionImpl::IceStateChange_m,
|
||||
kIceFailed),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PeerConnectionImpl::IceCompleted_m()
|
||||
PeerConnectionImpl::IceStateChange_m(IceState aState)
|
||||
{
|
||||
PC_AUTO_ENTER_API_CALL(false);
|
||||
|
||||
CSFLogDebug(logTag, __FUNCTION__);
|
||||
|
||||
mIceState = kIceConnected;
|
||||
mIceState = aState;
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
|
||||
|
|
|
@ -178,6 +178,7 @@ public:
|
|||
// ICE events
|
||||
void IceGatheringCompleted(NrIceCtx *aCtx);
|
||||
void IceCompleted(NrIceCtx *aCtx);
|
||||
void IceFailed(NrIceCtx *aCtx);
|
||||
void IceStreamReady(NrIceMediaStream *aStream);
|
||||
|
||||
static void ListenThread(void *aData);
|
||||
|
@ -269,8 +270,7 @@ private:
|
|||
void ShutdownMedia(bool isSynchronous);
|
||||
|
||||
// ICE callbacks run on the right thread.
|
||||
nsresult IceGatheringCompleted_m();
|
||||
nsresult IceCompleted_m();
|
||||
nsresult IceStateChange_m(IceState aState);
|
||||
|
||||
// The role we are adopting
|
||||
Role mRole;
|
||||
|
|
|
@ -60,6 +60,55 @@ LocalSourceStreamInfo::VideoTrackCount()
|
|||
return mVideoTracks.Length();
|
||||
}
|
||||
|
||||
void LocalSourceStreamInfo::DetachTransport_s()
|
||||
{
|
||||
ASSERT_ON_THREAD(mParent->GetSTSThread());
|
||||
// walk through all the MediaPipelines and call the shutdown
|
||||
// functions for transport. Must be on the STS thread.
|
||||
for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it =
|
||||
mPipelines.begin(); it != mPipelines.end();
|
||||
++it) {
|
||||
it->second->ShutdownTransport_s();
|
||||
}
|
||||
}
|
||||
|
||||
void LocalSourceStreamInfo::DetachMedia_m()
|
||||
{
|
||||
ASSERT_ON_THREAD(mParent->GetMainThread());
|
||||
// walk through all the MediaPipelines and call the shutdown
|
||||
// functions. Must be on the main thread.
|
||||
for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it =
|
||||
mPipelines.begin(); it != mPipelines.end();
|
||||
++it) {
|
||||
it->second->ShutdownMedia_m();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteSourceStreamInfo::DetachTransport_s()
|
||||
{
|
||||
ASSERT_ON_THREAD(mParent->GetSTSThread());
|
||||
// walk through all the MediaPipelines and call the shutdown
|
||||
// transport functions. Must be on the STS thread.
|
||||
for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it =
|
||||
mPipelines.begin(); it != mPipelines.end();
|
||||
++it) {
|
||||
it->second->ShutdownTransport_s();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteSourceStreamInfo::DetachMedia_m()
|
||||
{
|
||||
ASSERT_ON_THREAD(mParent->GetMainThread());
|
||||
|
||||
// walk through all the MediaPipelines and call the shutdown
|
||||
// media functions. Must be on the main thread.
|
||||
for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it =
|
||||
mPipelines.begin(); it != mPipelines.end();
|
||||
++it) {
|
||||
it->second->ShutdownMedia_m();
|
||||
}
|
||||
}
|
||||
|
||||
PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection()
|
||||
{
|
||||
PeerConnectionImpl *pc = new PeerConnectionImpl();
|
||||
|
@ -72,6 +121,9 @@ PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection()
|
|||
|
||||
nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers)
|
||||
{
|
||||
mMainThread = mParent->GetMainThread();
|
||||
mSTSThread = mParent->GetSTSThread();
|
||||
|
||||
// TODO(ekr@rtfm.com): need some way to set not offerer later
|
||||
// Looks like a bug in the NrIceCtx API.
|
||||
mIceCtx = NrIceCtx::Create("PC:" + mParent->GetHandle(), true);
|
||||
|
@ -96,6 +148,8 @@ nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_serv
|
|||
&PeerConnectionMedia::IceGatheringCompleted);
|
||||
mIceCtx->SignalCompleted.connect(this,
|
||||
&PeerConnectionMedia::IceCompleted);
|
||||
mIceCtx->SignalFailed.connect(this,
|
||||
&PeerConnectionMedia::IceFailed);
|
||||
|
||||
// Create three streams to start with.
|
||||
// One each for audio, video and DataChannel
|
||||
|
@ -131,17 +185,10 @@ nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_serv
|
|||
mIceStreams[i]->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady);
|
||||
}
|
||||
|
||||
// Start gathering
|
||||
nsresult res;
|
||||
mIceCtx->thread()->Dispatch(WrapRunnableRet(
|
||||
mIceCtx, &NrIceCtx::StartGathering, &res), NS_DISPATCH_SYNC
|
||||
);
|
||||
|
||||
if (NS_FAILED(res)) {
|
||||
CSFLogError(logTag, "%s: StartGathering failed: %u",
|
||||
__FUNCTION__, static_cast<uint32_t>(res));
|
||||
return res;
|
||||
}
|
||||
// TODO(ekr@rtfm.com): When we have a generic error reporting mechanism,
|
||||
// figure out how to report that StartGathering failed. Bug 827982.
|
||||
RUN_ON_THREAD(mIceCtx->thread(),
|
||||
WrapRunnable(mIceCtx, &NrIceCtx::StartGathering), NS_DISPATCH_NORMAL);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -184,7 +231,7 @@ PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream
|
|||
|
||||
// OK, we're good to add
|
||||
nsRefPtr<LocalSourceStreamInfo> localSourceStream =
|
||||
new LocalSourceStreamInfo(stream);
|
||||
new LocalSourceStreamInfo(stream, this);
|
||||
*stream_id = mLocalSourceStreams.Length();
|
||||
|
||||
if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
|
||||
|
@ -225,43 +272,63 @@ PeerConnectionMedia::RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *str
|
|||
void
|
||||
PeerConnectionMedia::SelfDestruct()
|
||||
{
|
||||
CSFLogDebug(logTag, "%s: Disconnecting media streams from PC", __FUNCTION__);
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
|
||||
DisconnectMediaStreams();
|
||||
CSFLogDebug(logTag, "%s: ", __FUNCTION__);
|
||||
|
||||
CSFLogDebug(logTag, "%s: Disconnecting transport", __FUNCTION__);
|
||||
// Shutdown the transport.
|
||||
RUN_ON_THREAD(mParent->GetSTSThread(), WrapRunnable(
|
||||
this, &PeerConnectionMedia::ShutdownMediaTransport), NS_DISPATCH_SYNC);
|
||||
// Shutdown the transport.
|
||||
RUN_ON_THREAD(mSTSThread, WrapRunnable(
|
||||
this, &PeerConnectionMedia::ShutdownMediaTransport_s),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
CSFLogDebug(logTag, "%s: Media shut down", __FUNCTION__);
|
||||
|
||||
// This should destroy the object.
|
||||
this->Release();
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::DisconnectMediaStreams()
|
||||
PeerConnectionMedia::SelfDestruct_m()
|
||||
{
|
||||
ASSERT_ON_THREAD(mMainThread);
|
||||
|
||||
CSFLogDebug(logTag, "%s: ", __FUNCTION__);
|
||||
|
||||
// Shut down the media
|
||||
for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) {
|
||||
mLocalSourceStreams[i]->Detach();
|
||||
mLocalSourceStreams[i]->DetachMedia_m();
|
||||
}
|
||||
|
||||
for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
|
||||
mRemoteSourceStreams[i]->Detach();
|
||||
mRemoteSourceStreams[i]->DetachMedia_m();
|
||||
}
|
||||
|
||||
mLocalSourceStreams.Clear();
|
||||
mRemoteSourceStreams.Clear();
|
||||
|
||||
// Final self-destruct.
|
||||
this->Release();
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::ShutdownMediaTransport()
|
||||
PeerConnectionMedia::ShutdownMediaTransport_s()
|
||||
{
|
||||
ASSERT_ON_THREAD(mSTSThread);
|
||||
|
||||
CSFLogDebug(logTag, "%s: ", __FUNCTION__);
|
||||
|
||||
for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) {
|
||||
mLocalSourceStreams[i]->DetachTransport_s();
|
||||
}
|
||||
|
||||
for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
|
||||
mRemoteSourceStreams[i]->DetachTransport_s();
|
||||
}
|
||||
|
||||
disconnect_all();
|
||||
mTransportFlows.clear();
|
||||
mIceStreams.clear();
|
||||
mIceCtx = NULL;
|
||||
|
||||
mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
LocalSourceStreamInfo*
|
||||
|
@ -314,6 +381,13 @@ PeerConnectionMedia::IceCompleted(NrIceCtx *aCtx)
|
|||
SignalIceCompleted(aCtx);
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::IceFailed(NrIceCtx *aCtx)
|
||||
{
|
||||
MOZ_ASSERT(aCtx);
|
||||
SignalIceFailed(aCtx);
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream)
|
||||
{
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче