Merge the last PGO-green inbound changeset to m-c.

This commit is contained in:
Ryan VanderMeulen 2012-08-26 22:22:56 -04:00
commit 80fff739e7
52 changed files with 1744 additions and 776 deletions

View File

@ -161,31 +161,6 @@ var WebConsoleUtils = {
return content;
},
/**
* Gets the window that has the given inner ID.
*
* @param integer aInnerId
* @param nsIDOMWindow [aHintWindow]
* Optional, the window object used to QueryInterface to
* nsIDOMWindowUtils. If this is not given,
* Services.wm.getMostRecentWindow() is used.
* @return nsIDOMWindow|null
* The window object with the given inner ID.
*/
getWindowByInnerId: function WCU_getWindowByInnerId(aInnerId, aHintWindow)
{
let someWindow = aHintWindow || Services.wm.getMostRecentWindow(null);
let content = null;
if (someWindow) {
let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
content = windowUtils.getInnerWindowWithId(aInnerId);
}
return content;
},
/**
* Abbreviates the given source URL so that it can be displayed flush-right
* without being too distracting.

View File

@ -1,4 +1,4 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 0.3.452
Current extension version is: 0.4.11

View File

@ -14,6 +14,7 @@ const MOZ_CENTRAL = true;
const PDFJS_EVENT_ID = 'pdf.js.message';
const PDF_CONTENT_TYPE = 'application/pdf';
const PREF_PREFIX = 'pdfjs';
const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
const MAX_DATABASE_LENGTH = 4096;
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
@ -122,9 +123,67 @@ function getLocalizedString(strings, id, property) {
return id;
}
// PDF data storage
function PdfDataListener(length) {
this.length = length; // less than 0, if length is unknown
this.data = new Uint8Array(length >= 0 ? length : 0x10000);
this.loaded = 0;
}
PdfDataListener.prototype = {
append: function PdfDataListener_append(chunk) {
var willBeLoaded = this.loaded + chunk.length;
if (this.length >= 0 && this.length < willBeLoaded) {
this.length = -1; // reset the length, server is giving incorrect one
}
if (this.length < 0 && this.data.length < willBeLoaded) {
// data length is unknown and new chunk will not fit in the existing
// buffer, resizing the buffer by doubling the its last length
var newLength = this.data.length;
for (; newLength < willBeLoaded; newLength *= 2) {}
var newData = new Uint8Array(newLength);
newData.set(this.data);
this.data = newData;
}
this.data.set(chunk, this.loaded);
this.loaded = willBeLoaded;
this.onprogress(this.loaded, this.length >= 0 ? this.length : void(0));
},
getData: function PdfDataListener_getData() {
var data = this.data;
if (this.loaded != data.length)
data = data.subarray(0, this.loaded);
delete this.data; // releasing temporary storage
return data;
},
finish: function PdfDataListener_finish() {
this.isDataReady = true;
if (this.oncompleteCallback) {
this.oncompleteCallback(this.getData());
}
},
error: function PdfDataListener_error(errorCode) {
this.errorCode = errorCode;
if (this.oncompleteCallback) {
this.oncompleteCallback(null, errorCode);
}
},
onprogress: function() {},
set oncomplete(value) {
this.oncompleteCallback = value;
if (this.isDataReady) {
value(this.getData());
}
if (this.errorCode) {
value(null, this.errorCode);
}
}
};
// All the priviledged actions.
function ChromeActions(domWindow) {
function ChromeActions(domWindow, dataListener) {
this.domWindow = domWindow;
this.dataListener = dataListener;
}
ChromeActions.prototype = {
@ -194,6 +253,38 @@ ChromeActions.prototype = {
getLocale: function() {
return getStringPref('general.useragent.locale', 'en-US');
},
getLoadingType: function() {
return this.dataListener ? 'passive' : 'active';
},
initPassiveLoading: function() {
if (!this.dataListener)
return false;
var domWindow = this.domWindow;
this.dataListener.onprogress =
function ChromeActions_dataListenerProgress(loaded, total) {
domWindow.postMessage({
pdfjsLoadAction: 'progress',
loaded: loaded,
total: total
}, '*');
};
this.dataListener.oncomplete =
function ChromeActions_dataListenerComplete(data, errorCode) {
domWindow.postMessage({
pdfjsLoadAction: 'complete',
data: data,
errorCode: errorCode
}, '*');
delete this.dataListener;
};
return true;
},
getStrings: function(data) {
try {
// Lazy initialization of localizedStrings
@ -219,9 +310,26 @@ ChromeActions.prototype = {
var strings = getLocalizedStrings('chrome.properties');
var message = getLocalizedString(strings, 'unsupported_feature');
var win = Services.wm.getMostRecentWindow('navigator:browser');
var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document);
var notificationBox = win.gBrowser.getNotificationBox(browser);
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) {
log('Unable to get a notification box for the fallback message');
return;
}
// Flag so we don't call the response callback twice, since if the user
// clicks open with different viewer both the button callback and
// eventCallback will be called.
@ -324,17 +432,21 @@ PdfStreamConverter.prototype = {
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
if (!isEnabled())
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
// Ignoring HTTP POST requests -- pdf.js has to repeat the request.
var skipConversion = false;
try {
var request = aCtxt;
request.QueryInterface(Ci.nsIHttpChannel);
skipConversion = (request.requestMethod !== 'GET');
} catch (e) {
// Non-HTTP request... continue normally.
var useFetchByChrome = getBoolPref(PREF_PREFIX + '.fetchByChrome', true);
if (!useFetchByChrome) {
// Ignoring HTTP POST requests -- pdf.js has to repeat the request.
var skipConversion = false;
try {
var request = aCtxt;
request.QueryInterface(Ci.nsIHttpChannel);
skipConversion = (request.requestMethod !== 'GET');
} catch (e) {
// Non-HTTP request... continue normally.
}
if (skipConversion)
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
if (skipConversion)
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
// Store the listener passed to us
this.listener = aListener;
@ -342,8 +454,14 @@ PdfStreamConverter.prototype = {
// nsIStreamListener::onDataAvailable
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
// Do nothing since all the data loading is handled by the viewer.
log('SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!');
if (!this.dataListener) {
// Do nothing since all the data loading is handled by the viewer.
return;
}
var binaryStream = this.binaryStream;
binaryStream.setInputStream(aInputStream);
this.dataListener.append(binaryStream.readByteArray(aCount));
},
// nsIRequestObserver::onStartRequest
@ -351,15 +469,27 @@ PdfStreamConverter.prototype = {
// 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 useFetchByChrome = getBoolPref(PREF_PREFIX + '.fetchByChrome', true);
var dataListener;
if (useFetchByChrome) {
// Creating storage for PDF data
var contentLength = aRequest.contentLength;
dataListener = new PdfDataListener(contentLength);
this.dataListener = dataListener;
this.binaryStream = Cc['@mozilla.org/binaryinputstream;1']
.createInstance(Ci.nsIBinaryInputStream);
} else {
// Cancel the request so the viewer can handle it.
aRequest.cancel(Cr.NS_BINDING_ABORTED);
}
// Create a new channel that is viewer loaded as a resource.
var ioService = Services.io;
var channel = ioService.newChannel(
'resource://pdf.js/web/viewer.html', null, null);
PDF_VIEWER_WEB_PAGE, null, null);
var listener = this.listener;
var self = this;
// Proxy all the request observer calls, when it gets to onStopRequest
// we can get the dom window.
var proxy = {
@ -373,8 +503,8 @@ PdfStreamConverter.prototype = {
var domWindow = getDOMWindow(channel);
// Double check the url is still the correct one.
if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
let requestListener = new RequestListener(
new ChromeActions(domWindow));
let actions = new ChromeActions(domWindow, dataListener);
let requestListener = new RequestListener(actions);
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
requestListener.receive(event);
}, false, true);
@ -386,11 +516,33 @@ PdfStreamConverter.prototype = {
// Keep the URL the same so the browser sees it as the same.
channel.originalURI = aRequest.URI;
channel.asyncOpen(proxy, aContext);
if (useFetchByChrome) {
// We can use resource principal when data is fetched by the chrome
// e.g. useful for NoScript
var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
.getService(Ci.nsIScriptSecurityManager);
var uri = ioService.newURI(PDF_VIEWER_WEB_PAGE, null, null);
// FF16 and below had getCodebasePrincipal (bug 774585)
var resourcePrincipal = 'getSimpleCodebasePrincipal' in securityManager ?
securityManager.getSimpleCodebasePrincipal(uri) :
securityManager.getCodebasePrincipal(uri);
channel.owner = resourcePrincipal;
}
},
// nsIRequestObserver::onStopRequest
onStopRequest: function(aRequest, aContext, aStatusCode) {
// Do nothing.
if (!this.dataListener) {
// Do nothing
return;
}
if (Components.isSuccessCode(aStatusCode))
this.dataListener.finish();
else
this.dataListener.error(aStatusCode);
delete this.dataListener;
delete this.binaryStream;
}
};

View File

@ -44,7 +44,7 @@ var FontInspector = (function FontInspectorClosure() {
}
}
return {
// Poperties/functions needed by PDFBug.
// Properties/functions needed by PDFBug.
id: 'FontInspector',
name: 'Font Inspector',
panel: null,
@ -140,7 +140,7 @@ var StepperManager = (function StepperManagerClosure() {
var stepperChooser = null;
var breakPoints = {};
return {
// Poperties/functions needed by PDFBug.
// Properties/functions needed by PDFBug.
id: 'Stepper',
name: 'Stepper',
panel: null,
@ -207,7 +207,7 @@ var StepperManager = (function StepperManagerClosure() {
var Stepper = (function StepperClosure() {
function Stepper(panel, pageIndex, initialBreakPoints) {
this.panel = panel;
this.len;
this.len = 0;
this.breakPoint = 0;
this.nextBreakPoint = null;
this.pageIndex = pageIndex;
@ -236,6 +236,7 @@ var Stepper = (function StepperClosure() {
headerRow.appendChild(c('th', 'fn'));
headerRow.appendChild(c('th', 'args'));
var self = this;
for (var i = 0; i < IRQueue.fnArray.length; i++) {
var line = c('tr');
line.className = 'line';
@ -249,7 +250,6 @@ var Stepper = (function StepperClosure() {
cbox.type = 'checkbox';
cbox.className = 'points';
cbox.checked = checked;
var self = this;
cbox.onclick = (function(x) {
return function() {
if (this.checked)
@ -298,7 +298,7 @@ var Stepper = (function StepperClosure() {
callback();
break;
}
}
};
dom.addEventListener('keydown', listener, false);
self.goTo(idx);
},
@ -331,7 +331,7 @@ var Stats = (function Stats() {
return false;
}
return {
// Poperties/functions needed by PDFBug.
// Properties/functions needed by PDFBug.
id: 'Stats',
name: 'Stats',
panel: null,
@ -429,12 +429,12 @@ var PDFBug = (function PDFBugClosure() {
// Initialize all the debugging tools.
var tools = this.tools;
var self = this;
for (var i = 0; i < tools.length; ++i) {
var tool = tools[i];
var panel = document.createElement('div');
var panelButton = document.createElement('button');
panelButton.textContent = tool.name;
var self = this;
panelButton.addEventListener('click', (function(selected) {
return function(event) {
event.preventDefault();

View File

@ -27,6 +27,50 @@ select {
display: none !important;
}
#viewerContainer:-webkit-full-screen {
top: 0px;
padding-top: 6px;
padding-bottom: 24px;
background-color: #404040;
background-image: url(images/texture.png);
width: 100%;
height: 100%;
overflow: auto;
}
:-webkit-full-screen #viewer {
margin: 0pt;
padding: 0pt;
height: 100%;
width: 100%;
overflow: hidden;
}
:-webkit-full-screen .page {
margin: 0px auto;
margin-bottom: 10px;
}
#viewerContainer:-moz-full-screen {
background-color: #404040;
background-image: url(images/texture.png);
width: 100%;
height: 100%;
overflow: hidden;
}
:-moz-full-screen .page:last-child {
margin-bottom: 40px;
}
#viewerContainer:full-screen {
top: 0px;
background-color: #404040;
background-image: url(images/texture.png);
width: 100%;
height: 100%;
}
/* outer/inner center provides horizontal center */
html[dir='ltr'] .outerCenter {
float: right;
@ -534,7 +578,7 @@ html[dir='rtl'] .dropdownToolbarButton {
margin:0;
padding:0;
border:none;
background: transparent;
background: rgba(0,0,0,0); /* Opera does not support 'transparent' <select> background */
}
.dropdownToolbarButton > select > option {
@ -609,6 +653,11 @@ html[dir='rtl'] .toolbarButton.pageDown::before {
content: url(images/toolbarButton-zoomIn.png);
}
.toolbarButton.fullscreen::before {
display: inline-block;
content: url(images/toolbarButton-fullscreen.png);
}
.toolbarButton.print::before {
display: inline-block;
content: url(images/toolbarButton-print.png);
@ -976,6 +1025,28 @@ canvas {
border-bottom-right-radius: 2px;
}
#loadingBar .progress.indeterminate {
width: 100%;
height: 25px;
background-image: -moz-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040);
background-image: -webkit-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040);
background-image: -ms-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040);
background-image: -o-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040);
background-size: 75px 25px;
-moz-animation: progressIndeterminate 1s linear infinite;
-webkit-animation: progressIndeterminate 1s linear infinite;
}
@-moz-keyframes progressIndeterminate {
from { background-position: 0px 0px; }
to { background-position: 75px 0px; }
}
@-webkit-keyframes progressIndeterminate {
from { background-position: 0px 0px; }
to { background-position: 75px 0px; }
}
.textLayer {
position: absolute;
left: 0;
@ -1113,6 +1184,17 @@ canvas {
font-size: 10px;
}
#viewer.textLayer-visible .textLayer > div,
#viewer.textLayer-hover .textLayer > div:hover {
background-color: white;
color: black;
}
#viewer.textLayer-shadow .textLayer > div {
background-color: rgba(255,255,255, .6);
color: black;
}
@page {
margin: 0;
}

View File

@ -4,11 +4,9 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>PDF.js viewer</title>
<!-- This snippet is used in firefox extension, see Makefile -->
<base href="resource://pdf.js/web/" />
<script type="application/l10n">
<!-- PDFJSSCRIPT_LOCALE_DATA -->
</script>
<script type="text/javascript" src="l10n.js"></script>
<script type="text/javascript" id="PDFJS_SCRIPT_TAG">
<!--
@ -23,9 +21,9 @@ var PDFJS = {};
// Use strict in our context only - users might not want it
'use strict';
PDFJS.build = 'c757eed';
PDFJS.build =
'cb05144';
// Files are inserted below - see Makefile
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
@ -57,9 +55,7 @@ function getPdf(arg, callback) {
var params = arg;
if (typeof arg === 'string')
params = { url: arg };
var xhr = new XMLHttpRequest();
xhr.open('GET', params.url);
var headers = params.headers;
@ -73,15 +69,23 @@ function getPdf(arg, callback) {
}
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
var protocol = params.url.indexOf(':') < 0 ? window.location.protocol :
params.url.substring(0, params.url.indexOf(':') + 1);
var protocol = params.url.substring(0, params.url.indexOf(':') + 1);
xhr.expected = (protocol === 'http:' || protocol === 'https:') ? 200 : 0;
if ('progress' in params)
xhr.onprogress = params.progress || undefined;
if ('error' in params)
xhr.onerror = params.error || undefined;
var calledErrorBack = false;
if ('error' in params) {
xhr.onerror = function errorBack() {
if (!calledErrorBack) {
calledErrorBack = true;
params.error();
}
}
}
xhr.onreadystatechange = function getPdfOnreadystatechange(e) {
if (xhr.readyState === 4) {
@ -89,7 +93,8 @@ function getPdf(arg, callback) {
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
xhr.responseArrayBuffer || xhr.response);
callback(data);
} else if (params.error) {
} else if (params.error && !calledErrorBack) {
calledErrorBack = true;
params.error(e);
}
}
@ -422,14 +427,37 @@ var PDFDocument = (function PDFDocumentClosure() {
return true; /* found */
}
var DocumentInfoValidators = {
get entries() {
// Lazily build this since all the validation functions below are not
// defined until after this file loads.
return shadow(this, 'entries', {
Title: isString,
Author: isString,
Subject: isString,
Keywords: isString,
Creator: isString,
Producer: isString,
CreationDate: isString,
ModDate: isString,
Trapped: isName
});
}
};
PDFDocument.prototype = {
get linearization() {
var length = this.stream.length;
var linearization = false;
if (length) {
linearization = new Linearization(this.stream);
if (linearization.length != length)
linearization = false;
try {
linearization = new Linearization(this.stream);
if (linearization.length != length)
linearization = false;
} catch (err) {
warn('The linearization data is not available ' +
'or unreadable pdf data is found');
}
}
// shadow the prototype getter with a data property
return shadow(this, 'linearization', linearization);
@ -509,18 +537,27 @@ var PDFDocument = (function PDFDocumentClosure() {
return shadow(this, 'numPages', num);
},
getDocumentInfo: function PDFDocument_getDocumentInfo() {
var info;
var docInfo;
if (this.xref.trailer.has('Info')) {
var infoDict = this.xref.trailer.get('Info');
info = {};
infoDict.forEach(function(key, value) {
info[key] = typeof value !== 'string' ? value :
stringToPDFString(value);
});
docInfo = {};
var validEntries = DocumentInfoValidators.entries;
// Only fill the document info with valid entries from the spec.
for (var key in validEntries) {
if (infoDict.has(key)) {
var value = infoDict.get(key);
// Make sure the value conforms to the spec.
if (validEntries[key](value)) {
docInfo[key] = typeof value !== 'string' ? value :
stringToPDFString(value);
} else {
info('Bad value in document info for "' + key + '"');
}
}
}
}
return shadow(this, 'getDocumentInfo', info);
return shadow(this, 'getDocumentInfo', docInfo);
},
getFingerprint: function PDFDocument_getFingerprint() {
var xref = this.xref, fileID;
@ -611,6 +648,28 @@ function assert(cond, msg) {
error(msg);
}
// Combines two URLs. The baseUrl shall be absolute URL. If the url is an
// absolute URL, it will be returned as is.
function combineUrl(baseUrl, url) {
if (url.indexOf(':') >= 0)
return url;
if (url.charAt(0) == '/') {
// absolute path
var i = baseUrl.indexOf('://');
i = baseUrl.indexOf('/', i + 3);
return baseUrl.substring(0, i) + url;
} else {
// relative path
var pathLength = baseUrl.length, i;
i = baseUrl.lastIndexOf('#');
pathLength = i >= 0 ? i : pathLength;
i = baseUrl.lastIndexOf('?', pathLength);
pathLength = i >= 0 ? i : pathLength;
var prefixLength = baseUrl.lastIndexOf('/', pathLength);
return baseUrl.substring(0, prefixLength + 1) + url;
}
}
// In a well-formed PDF, |cond| holds. If it doesn't, subsequent
// behavior is undefined.
function assertWellFormed(cond, msg) {
@ -1188,63 +1247,36 @@ var StatTimer = (function StatTimerClosure() {
* @return {Promise} A promise that is resolved with {PDFDocumentProxy} object.
*/
PDFJS.getDocument = function getDocument(source) {
var url, data, headers, password, parameters = {}, workerInitializedPromise,
workerReadyPromise, transport;
var workerInitializedPromise, workerReadyPromise, transport;
if (typeof source === 'string') {
url = source;
source = { url: source };
} else if (isArrayBuffer(source)) {
data = source;
} else if (typeof source === 'object') {
url = source.url;
data = source.data;
headers = source.httpHeaders;
password = source.password;
parameters.password = password || null;
if (!url && !data)
error('Invalid parameter array, need either .data or .url');
} else {
source = { data: source };
} else if (typeof source !== 'object') {
error('Invalid parameter in getDocument, need either Uint8Array, ' +
'string or a parameter object');
}
if (!source.url && !source.data)
error('Invalid parameter array, need either .data or .url');
// copy/use all keys as is except 'url' -- full path is required
var params = {};
for (var key in source) {
if (key === 'url' && typeof window !== 'undefined') {
params[key] = combineUrl(window.location.href, source[key]);
continue;
}
params[key] = source[key];
}
workerInitializedPromise = new PDFJS.Promise();
workerReadyPromise = new PDFJS.Promise();
transport = new WorkerTransport(workerInitializedPromise, workerReadyPromise);
if (data) {
// assuming the data is array, instantiating directly from it
transport.sendData(data, parameters);
} else if (url) {
// fetch url
PDFJS.getPdf(
{
url: url,
progress: function getPDFProgress(evt) {
if (evt.lengthComputable) {
workerReadyPromise.progress({
loaded: evt.loaded,
total: evt.total
});
}
},
error: function getPDFError(e) {
workerReadyPromise.reject('Unexpected server response of ' +
e.target.status + '.');
},
headers: headers
},
function getPDFLoad(data) {
// sometimes the pdf has finished downloading before the web worker-test
// has finished. In that case the rendering of the final pdf would cause
// errors. We have to wait for the WorkerTransport to finalize worker-
// support detection
workerInitializedPromise.then(function workerInitialized() {
transport.sendData(data, parameters);
});
});
}
workerInitializedPromise.then(function transportInitialized() {
transport.fetchDocument(params);
});
return workerReadyPromise;
};
@ -1627,19 +1659,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
try {
var worker;
if (PDFJS.isFirefoxExtension) {
// The firefox extension can't load the worker from the resource://
// url so we have to inline the script and then use the blob loader.
var bb = new MozBlobBuilder();
bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent);
var blobUrl = window.URL.createObjectURL(bb.getBlob());
worker = new Worker(blobUrl);
} else {
// Some versions of FF can't create a worker on localhost, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
worker = new Worker(workerSrc);
}
// The firefox extension can't load the worker from the resource://
// url so we have to inline the script and then use the blob loader.
var bb = new MozBlobBuilder();
bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent);
var blobUrl = window.URL.createObjectURL(bb.getBlob());
worker = new Worker(blobUrl);
var messageHandler = new MessageHandler('main', worker);
this.messageHandler = messageHandler;
@ -1771,6 +1796,17 @@ var WorkerTransport = (function WorkerTransportClosure() {
}
}, this);
messageHandler.on('DocProgress', function transportDocProgress(data) {
this.workerReadyPromise.progress({
loaded: data.loaded,
total: data.total
});
}, this);
messageHandler.on('DocError', function transportDocError(data) {
this.workerReadyPromise.reject(data);
}, this);
messageHandler.on('PageError', function transportError(data) {
var page = this.pageCache[data.pageNum - 1];
if (page.displayReadyPromise)
@ -1815,11 +1851,11 @@ var WorkerTransport = (function WorkerTransportClosure() {
});
},
sendData: function WorkerTransport_sendData(data, params) {
this.messageHandler.send('GetDocRequest', {data: data, params: params});
fetchDocument: function WorkerTransport_fetchDocument(source) {
this.messageHandler.send('GetDocRequest', {source: source});
},
getData: function WorkerTransport_sendData(promise) {
getData: function WorkerTransport_getData(promise) {
this.messageHandler.send('GetData', null, function(data) {
promise.resolve(data);
});
@ -2003,6 +2039,7 @@ var CanvasExtraState = (function CanvasExtraStateClosure() {
this.wordSpacing = 0;
this.textHScale = 1;
this.textRenderingMode = TextRenderingMode.FILL;
this.textRise = 0;
// Color spaces
this.fillColorSpace = new DeviceGrayCS();
this.fillColorSpaceObj = null;
@ -2447,7 +2484,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.current.textRenderingMode = mode;
},
setTextRise: function CanvasGraphics_setTextRise(rise) {
TODO('text rise: ' + rise);
this.current.textRise = rise;
},
moveText: function CanvasGraphics_moveText(x, y) {
this.current.x = this.current.lineX += x;
@ -2474,7 +2511,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
ctx.transform.apply(ctx, current.textMatrix);
ctx.scale(1, -1);
ctx.translate(current.x, -1 * current.y);
ctx.translate(current.x, -current.y - current.textRise);
ctx.transform.apply(ctx, fontMatrix);
ctx.scale(textHScale, 1);
},
@ -3042,20 +3079,20 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
// Marked content
markPoint: function CanvasGraphics_markPoint(tag) {
TODO('Marked content');
// TODO Marked content.
},
markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
TODO('Marked content');
// TODO Marked content.
},
beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
TODO('Marked content');
// TODO Marked content.
},
beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
tag, properties) {
TODO('Marked content');
// TODO Marked content.
},
endMarkedContent: function CanvasGraphics_endMarkedContent() {
TODO('Marked content');
// TODO Marked content.
},
// Compatibility
@ -3103,7 +3140,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
return CanvasGraphics;
})();
if (!isWorker) {
function checkPutBinaryImageDataCompatibility() {
// Feature detection if the browser can use an Uint8Array directly as imgData.
var canvas = document.createElement('canvas');
canvas.width = 1;
@ -3124,20 +3161,26 @@ if (!isWorker) {
} catch (e) {
CanvasGraphics.prototype.putBinaryImageData =
function CanvasGraphicsPutBinaryImageDataShim(ctx, imgData, w, h) {
var tmpImgData = ctx.getImageData(0, 0, w, h);
var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
ctx.getImageData(0, 0, w, h);
// Copy over the imageData pixel by pixel.
var tmpImgDataPixels = tmpImgData.data;
var len = tmpImgDataPixels.length;
while (len--) {
tmpImgDataPixels[len] = imgData.data[len];
var data = imgData.data;
if ('set' in tmpImgDataPixels)
tmpImgDataPixels.set(data);
else {
// Copy over the imageData pixel by pixel.
for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
tmpImgDataPixels[i] = data[i];
}
ctx.putImageData(tmpImgData, 0, 0);
};
}
}
if (!isWorker) {
checkPutBinaryImageDataCompatibility();
}
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
@ -3765,7 +3808,7 @@ var XRef = (function XRefClosure() {
var e = this.entries[i];
if (e === null)
return null;
return e.free ? null : e; // returns null is the entry is free
return e.free || !e.offset ? null : e; // returns null if entry is free
},
fetchIfRef: function XRef_fetchIfRef(obj) {
if (!isRef(obj))
@ -4076,11 +4119,12 @@ var PDFFunction = (function PDFFunctionClosure() {
var size = dict.get('Size');
var bps = dict.get('BitsPerSample');
var order = dict.get('Order');
if (!order)
order = 1;
if (order !== 1)
error('No support for cubic spline interpolation: ' + order);
var order = dict.get('Order') || 1;
if (order !== 1) {
// No description how cubic spline interpolation works in PDF32000:2008
// As in poppler, ignoring order, linear interpolation may work as good
TODO('No support for cubic spline interpolation: ' + order);
}
var encode = dict.get('Encode');
if (!encode) {
@ -12030,6 +12074,9 @@ var ColorSpace = (function ColorSpaceClosure() {
var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
var hiVal = cs[2] + 1;
var lookup = xref.fetchIfRef(cs[3]);
if (isStream(lookup)) {
lookup = lookup.getBytes();
}
return ['IndexedCS', baseIndexedCS, hiVal, lookup];
case 'Separation':
case 'DeviceN':
@ -12151,14 +12198,18 @@ var IndexedCS = (function IndexedCSClosure() {
var baseNumComps = base.numComps;
var length = baseNumComps * highVal;
var lookupArray = new Uint8Array(length);
var lookupArray;
if (isStream(lookup)) {
lookupArray = new Uint8Array(length);
var bytes = lookup.getBytes(length);
lookupArray.set(bytes);
} else if (isString(lookup)) {
lookupArray = new Uint8Array(length);
for (var i = 0; i < length; ++i)
lookupArray[i] = lookup.charCodeAt(i);
} else if (lookup instanceof Uint8Array) {
lookupArray = lookup;
} else {
error('Unrecognized lookup table: ' + lookup);
}
@ -13840,7 +13891,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (widths) {
var start = 0, end = 0;
for (var i = 0, ii = widths.length; i < ii; i++) {
var code = widths[i];
var code = xref.fetchIfRef(widths[i]);
if (isArray(code)) {
for (var j = 0, jj = code.length; j < jj; j++)
glyphsWidths[start++] = code[j];
@ -14482,7 +14533,8 @@ var FontLoader = {
document.documentElement.removeEventListener(
'pdfjsFontLoad', checkFontsLoaded, false);
callback();
// Use timeout to fix chrome intermittent failures on font loading.
setTimeout(callback, 0);
return true;
}
@ -17356,6 +17408,14 @@ var Font = (function FontClosure() {
return Font;
})();
var CallothersubrCmd = (function CallothersubrCmdClosure() {
function CallothersubrCmd(index) {
this.index = index;
}
return CallothersubrCmd;
})();
/*
* Type1Parser encapsulate the needed code for parsing a Type1 font
* program. Some of its logic depends on the Type2 charstrings
@ -17455,8 +17515,8 @@ var Type1Parser = function type1Parser() {
'1': 'vstem',
'2': 'hstem',
'6': 'endchar', // seac
// Type1 only command with command not (yet) built-in ,throw an error
'6': -1, // seac
'7': -1, // sbw
'11': 'sub',
@ -17486,6 +17546,43 @@ var Type1Parser = function type1Parser() {
var kEscapeCommand = 12;
// Breaks up the stack by arguments and also calculates the value.
function breakUpArgs(stack, numArgs) {
var args = [];
var index = stack.length - 1;
for (var i = 0; i < numArgs; i++) {
if (index < 0) {
args.unshift({ arg: [0],
value: 0,
offset: 0 });
warn('Malformed charstring stack: not enough values on stack.');
continue;
}
var token = stack[index];
if (token === 'div') {
var a = stack[index - 2];
var b = stack[index - 1];
if (!isInt(a) || !isInt(b)) {
warn('Malformed charsting stack: expected ints on stack for div.');
a = 0;
b = 1;
}
args.unshift({ arg: [a, b, 'div'],
value: a / b,
offset: index - 2 });
index -= 3;
} else if (isInt(token)) {
args.unshift({ arg: stack.slice(index, index + 1),
value: token,
offset: index });
index--;
} else {
warn('Malformed charsting stack: found bad token ' + token + '.');
}
}
return args;
}
function decodeCharString(array) {
var charstring = [];
var lsb = 0;
@ -17524,10 +17621,20 @@ var Type1Parser = function type1Parser() {
i++;
continue;
}
assert(argc == 0, 'callothersubr with arguments is not supported');
charstring.push(new CallothersubrCmd(index));
continue;
} else if (escape == 17 || escape == 33) {
// pop or setcurrentpoint commands can be ignored
// since we are not doing callothersubr
continue;
} else if (escape == 6) {
// seac is like type 2's special endchar but it doesn't use the
// first argument asb, so remove it.
var args = breakUpArgs(charstring, 5);
var arg0 = args[0];
charstring.splice(arg0.offset, arg0.arg.length);
} else if (!kHintingEnabled && (escape == 1 || escape == 2)) {
charstring.push('drop', 'drop', 'drop', 'drop', 'drop', 'drop');
continue;
@ -17535,25 +17642,17 @@ var Type1Parser = function type1Parser() {
command = charStringDictionary['12'][escape];
} else {
// TODO Clean this code
if (value == 13) { // hsbw
if (charstring.length == 2) {
lsb = charstring[0];
width = charstring[1];
charstring.splice(0, 1);
} else if (charstring.length == 4 && charstring[3] == 'div') {
lsb = charstring[0];
width = charstring[1] / charstring[2];
charstring.splice(0, 1);
} else if (charstring.length == 4 && charstring[2] == 'div') {
lsb = charstring[0] / charstring[1];
width = charstring[3];
charstring.splice(0, 3);
} else {
error('Unsupported hsbw format: ' + charstring);
}
charstring.push(lsb, 'hmoveto');
var args = breakUpArgs(charstring, 2);
var arg0 = args[0];
var arg1 = args[1];
lsb = arg0.value;
width = arg1.value;
// To convert to type2 we have to move the width value to the first
// part of the charstring and then use hmoveto with lsb.
charstring = arg1.arg;
charstring = charstring.concat(arg0.arg);
charstring.push('hmoveto');
continue;
} else if (value == 10) { // callsubr
if (charstring[charstring.length - 1] < 3) { // subr #0..2
@ -17763,6 +17862,10 @@ var Type1Parser = function type1Parser() {
case '/OtherBlues':
case '/FamilyBlues':
case '/FamilyOtherBlues':
var blueArray = readNumberArray(eexecStr, i + 1);
if (blueArray.length > 0 && (blueArray.length % 2) == 0)
program.properties.privateData[token.substring(1)] = blueArray;
break;
case '/StemSnapH':
case '/StemSnapV':
program.properties.privateData[token.substring(1)] =
@ -18045,23 +18148,23 @@ Type1Font.prototype = {
},
getType2Charstrings: function Type1Font_getType2Charstrings(
type1Charstrings) {
type1Subrs) {
var type2Charstrings = [];
var count = type1Charstrings.length;
for (var i = 0; i < count; i++) {
var charstring = type1Charstrings[i].charstring;
type2Charstrings.push(this.flattenCharstring(charstring.slice(),
this.commandsMap));
}
var count = type1Subrs.length;
var type1Charstrings = [];
for (var i = 0; i < count; i++)
type1Charstrings.push(type1Subrs[i].charstring.slice());
for (var i = 0; i < count; i++)
type2Charstrings.push(this.flattenCharstring(type1Charstrings, i));
return type2Charstrings;
},
getType2Subrs: function Type1Font_getType2Subrs(type1Subrs) {
var bias = 0;
var count = type1Subrs.length;
if (count < 1240)
if (count < 1133)
bias = 107;
else if (count < 33900)
else if (count < 33769)
bias = 1131;
else
bias = 32768;
@ -18072,11 +18175,7 @@ Type1Font.prototype = {
type2Subrs.push([0x0B]);
for (var i = 0; i < count; i++) {
var subr = type1Subrs[i];
if (!subr)
subr = [0x0B];
type2Subrs.push(this.flattenCharstring(subr, this.commandsMap));
type2Subrs.push(this.flattenCharstring(type1Subrs, i));
}
return type2Subrs;
@ -18108,11 +18207,15 @@ Type1Font.prototype = {
'hvcurveto': 31
},
flattenCharstring: function Type1Font_flattenCharstring(charstring, map) {
flattenCharstring: function Type1Font_flattenCharstring(charstrings, index) {
var charstring = charstrings[index];
if (!charstring)
return [0x0B];
var map = this.commandsMap;
// charstring changes size - can't cache .length in loop
for (var i = 0; i < charstring.length; i++) {
var command = charstring[i];
if (command.charAt) {
if (typeof command === 'string') {
var cmd = map[command];
assert(cmd, 'Unknow command: ' + command);
@ -18120,6 +18223,17 @@ Type1Font.prototype = {
charstring.splice(i++, 1, cmd[0], cmd[1]);
else
charstring[i] = cmd;
} else if (command instanceof CallothersubrCmd) {
var otherSubrCharstring = charstrings[command.index];
if (otherSubrCharstring) {
var lastCommand = otherSubrCharstring.indexOf('return');
if (lastCommand >= 0)
otherSubrCharstring = otherSubrCharstring.slice(0, lastCommand);
charstring.splice.apply(charstring,
[i, 1].concat(otherSubrCharstring));
} else
charstring.splice(i, 1); // ignoring empty subr call
i--;
} else {
// Type1 charstring use a division for number above 32000
if (command > 32000) {
@ -24062,6 +24176,10 @@ var PDFImage = (function PDFImageClosure() {
var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
var imgArray = this.getImageBytes(originalHeight * rowBytes);
// imgArray can be incomplete (e.g. after CCITT fax encoding)
var actualHeight = 0 | (imgArray.length / rowBytes *
height / originalHeight);
var comps = this.colorSpace.getRgbBuffer(
this.getComponents(imgArray), bpc);
if (originalWidth != width || originalHeight != height)
@ -24070,7 +24188,7 @@ var PDFImage = (function PDFImageClosure() {
var compsPos = 0;
var opacity = this.getOpacity(width, height);
var opacityPos = 0;
var length = width * height * 4;
var length = width * actualHeight * 4;
for (var i = 0; i < length; i += 4) {
buffer[i] = comps[compsPos++];
@ -27762,6 +27880,7 @@ var Pattern = (function PatternClosure() {
// Both radial and axial shadings are handled by RadialAxial shading.
return new Shadings.RadialAxial(dict, matrix, xref, res);
default:
TODO('Unsupported shading type: ' + type);
return new Shadings.Dummy();
}
};
@ -27898,7 +28017,12 @@ Shadings.Dummy = (function DummyClosure() {
}
Dummy.fromIR = function Dummy_fromIR() {
return 'hotpink';
return {
type: 'Pattern',
getPattern: function Dummy_fromIR_getPattern() {
return 'hotpink';
}
};
};
Dummy.prototype = {
@ -28880,7 +29004,7 @@ var JpegStream = (function JpegStreamClosure() {
*/
JpegStream.prototype.isNativelySupported =
function JpegStream_isNativelySupported(xref, res) {
var cs = ColorSpace.parse(this.dict.get('ColorSpace'), xref, res);
var cs = ColorSpace.parse(this.dict.get('ColorSpace', 'CS'), xref, res);
// when bug 674619 lands, let's check if browser can do
// normal cmyk and then we won't need to decode in JS
if (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB')
@ -28895,7 +29019,7 @@ var JpegStream = (function JpegStreamClosure() {
*/
JpegStream.prototype.isNativelyDecodable =
function JpegStream_isNativelyDecodable(xref, res) {
var cs = ColorSpace.parse(this.dict.get('ColorSpace'), xref, res);
var cs = ColorSpace.parse(this.dict.get('ColorSpace', 'CS'), xref, res);
var numComps = cs.numComps;
if (numComps == 1 || numComps == 3)
return true;
@ -30451,15 +30575,10 @@ var WorkerMessageHandler = {
setup: function wphSetup(handler) {
var pdfModel = null;
handler.on('test', function wphSetupTest(data) {
handler.send('test', data instanceof Uint8Array);
});
handler.on('GetDocRequest', function wphSetupDoc(data) {
function loadDocument(pdfData, pdfModelSource) {
// Create only the model of the PDFDoc, which is enough for
// processing the content of the pdf.
var pdfData = data.data;
var pdfPassword = data.params.password;
var pdfPassword = pdfModelSource.password;
try {
pdfModel = new PDFDocument(new Stream(pdfData), pdfPassword);
} catch (e) {
@ -30489,6 +30608,38 @@ var WorkerMessageHandler = {
encrypted: !!pdfModel.xref.encrypt
};
handler.send('GetDoc', {pdfInfo: doc});
}
handler.on('test', function wphSetupTest(data) {
handler.send('test', data instanceof Uint8Array);
});
handler.on('GetDocRequest', function wphSetupDoc(data) {
var source = data.source;
if (source.data) {
// the data is array, instantiating directly from it
loadDocument(source.data, source);
return;
}
PDFJS.getPdf(
{
url: source.url,
progress: function getPDFProgress(evt) {
handler.send('DocProgress', {
loaded: evt.loaded,
total: evt.lengthComputable ? evt.total : void(0)
});
},
error: function getPDFError(e) {
handler.send('DocError', 'Unexpected server response of ' +
e.target.status + '.');
},
headers: source.httpHeaders
},
function getPDFLoad(data) {
loadDocument(data, source);
});
});
handler.on('GetPageRequest', function wphSetupGetPage(data) {
@ -35023,7 +35174,10 @@ var Metadata = PDFJS.Metadata = (function MetadataClosure() {
return Metadata;
})();
}).call((typeof window === 'undefined') ? this : window);
-->
</script>
<script type="text/javascript">
@ -35032,10 +35186,14 @@ var Metadata = PDFJS.Metadata = (function MetadataClosure() {
PDFJS.workerSrc = 'none';
</script>
<link rel="stylesheet" href="viewer.css"/>
<!-- PDFJSSCRIPT_INCLUDE_BUILD -->
<script type="text/javascript" src="debugger.js"></script>
<script type="text/javascript" src="viewer.js"></script>
</head>
@ -35096,19 +35254,25 @@ var Metadata = PDFJS.Metadata = (function MetadataClosure() {
</div>
<div id="toolbarViewerRight">
<input id="fileInput" class="fileInput" type="file" oncontextmenu="return false;" style="visibility: hidden; position: fixed; right: 0; top: 0" />
<button id="openFile" class="toolbarButton openFile" title="Open File" tabindex="11" data-l10n-id="open_file" onclick="document.getElementById('fileInput').click()">
<button id="fullscreen" class="toolbarButton fullscreen" title="Fullscreen" tabindex="11" data-l10n-id="fullscreen" onclick="PDFView.fullscreen();">
<span data-l10n-id="fullscreen_label">Fullscreen</span>
</button>
<button id="openFile" class="toolbarButton openFile" title="Open File" tabindex="12" data-l10n-id="open_file" onclick="document.getElementById('fileInput').click()">
<span data-l10n-id="open_file_label">Open</span>
</button>
<button id="print" class="toolbarButton print" title="Print" tabindex="11" data-l10n-id="print" onclick="window.print()">
<button id="print" class="toolbarButton print" title="Print" tabindex="13" data-l10n-id="print" onclick="window.print()">
<span data-l10n-id="print_label">Print</span>
</button>
<button id="download" class="toolbarButton download" title="Download" onclick="PDFView.download();" tabindex="12" data-l10n-id="download">
<button id="download" class="toolbarButton download" title="Download" onclick="PDFView.download();" tabindex="14" data-l10n-id="download">
<span data-l10n-id="download_label">Download</span>
</button>
<!-- <div class="toolbarButtonSpacer"></div> -->
<a href="#" id="viewBookmark" class="toolbarButton bookmark" title="Current view (copy or open in new window)" tabindex="13" data-l10n-id="bookmark"><span data-l10n-id="bookmark_label">Current View</span></a>
<a href="#" id="viewBookmark" class="toolbarButton bookmark" title="Current view (copy or open in new window)" tabindex="15" data-l10n-id="bookmark"><span data-l10n-id="bookmark_label">Current View</span></a>
</div>
<div class="outerCenter">
<div class="innerCenter" id="toolbarViewerMiddle">
@ -35175,3 +35339,4 @@ var Metadata = PDFJS.Metadata = (function MetadataClosure() {
<div id="printContainer"></div>
</body>
</html>

View File

@ -60,7 +60,6 @@ var ProgressBar = (function ProgressBarClosure() {
this.height = opts.height || 100;
this.width = opts.width || 100;
this.units = opts.units || '%';
this.percent = opts.percent || 0;
// Initialize heights
this.div.style.height = this.height + this.units;
@ -69,10 +68,18 @@ var ProgressBar = (function ProgressBarClosure() {
ProgressBar.prototype = {
updateBar: function ProgressBar_updateBar() {
if (this._indeterminate) {
this.div.classList.add('indeterminate');
return;
}
var progressSize = this.width * this._percent / 100;
if (this._percent > 95)
this.div.classList.add('full');
else
this.div.classList.remove('full');
this.div.classList.remove('indeterminate');
this.div.style.width = progressSize + this.units;
},
@ -82,6 +89,7 @@ var ProgressBar = (function ProgressBarClosure() {
},
set percent(val) {
this._indeterminate = isNaN(val);
this._percent = clamp(val, 0, 100);
this.updateBar();
}
@ -151,6 +159,7 @@ var FirefoxCom = (function FirefoxComClosure() {
};
})();
// Settings Manager - This is a utility for saving settings
// First we see if localStorage is available
// If not, we use FUEL in FF
@ -167,17 +176,10 @@ var Settings = (function SettingsClosure() {
}
})();
var isFirefoxExtension = PDFJS.isFirefoxExtension;
function Settings(fingerprint) {
var database = null;
var index;
if (isFirefoxExtension)
database = FirefoxCom.requestSync('getDatabase', null) || '{}';
else if (isLocalStorageEnabled)
database = localStorage.getItem('database') || '{}';
else
return false;
database = FirefoxCom.requestSync('getDatabase', null) || '{}';
database = JSON.parse(database);
if (!('files' in database))
@ -205,10 +207,7 @@ var Settings = (function SettingsClosure() {
var file = this.file;
file[name] = val;
var database = JSON.stringify(this.database);
if (isFirefoxExtension)
FirefoxCom.requestSync('setDatabase', database);
else if (isLocalStorageEnabled)
localStorage.setItem('database', database);
FirefoxCom.requestSync('setDatabase', database);
},
get: function settingsGet(name, defaultValue) {
@ -241,6 +240,8 @@ var PDFView = {
sidebarOpen: false,
pageViewScroll: null,
thumbnailViewScroll: null,
isFullscreen: false,
previousScale: null,
// called once when the document is loaded
initialize: function pdfViewInitialize() {
@ -307,6 +308,7 @@ var PDFView = {
var container = this.container;
var currentPage = this.pages[this.page - 1];
var pageWidthScale = (container.clientWidth - kScrollbarPadding) /
currentPage.width * currentPage.scale / kCssUnits;
var pageHeightScale = (container.clientHeight - kScrollbarPadding) /
@ -348,9 +350,10 @@ var PDFView = {
set page(val) {
var pages = this.pages;
var input = document.getElementById('pageNumber');
var event = document.createEvent('UIEvents');
event.initUIEvent('pagechange', false, false, window, 0);
if (!(0 < val && val <= pages.length)) {
var event = document.createEvent('UIEvents');
event.initUIEvent('pagechange', false, false, window, 0);
event.pageNumber = this.page;
window.dispatchEvent(event);
return;
@ -358,8 +361,6 @@ var PDFView = {
pages[val - 1].updateStats();
currentPageNumber = val;
var event = document.createEvent('UIEvents');
event.initUIEvent('pagechange', false, false, window, 0);
event.pageNumber = val;
window.dispatchEvent(event);
@ -390,11 +391,53 @@ var PDFView = {
return value;
},
get supportsFullscreen() {
var doc = document.documentElement;
var support = doc.requestFullScreen || doc.mozRequestFullScreen ||
doc.webkitRequestFullScreen;
Object.defineProperty(this, 'supportsFullScreen', { value: support,
enumerable: true,
configurable: true,
writable: false });
return support;
},
initPassiveLoading: function pdfViewInitPassiveLoading() {
if (!PDFView.loadingBar) {
PDFView.loadingBar = new ProgressBar('#loadingBar', {});
}
window.addEventListener('message', function window_message(e) {
var args = e.data;
if (typeof args !== 'object' || !('pdfjsLoadAction' in args))
return;
switch (args.pdfjsLoadAction) {
case 'progress':
PDFView.progress(args.loaded / args.total);
break;
case 'complete':
if (!args.data) {
PDFView.error(mozL10n.get('loading_error', null,
'An error occurred while loading the PDF.'), e);
break;
}
PDFView.open(args.data, 0);
break;
}
});
FirefoxCom.requestSync('initPassiveLoading', null);
},
setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
this.url = url;
document.title = decodeURIComponent(getFileName(url)) || url;
},
open: function pdfViewOpen(url, scale, password) {
var parameters = {password: password};
if (typeof url === 'string') { // URL
this.url = url;
document.title = decodeURIComponent(getFileName(url)) || url;
this.setTitleUsingUrl(url);
parameters.url = url;
} else if (url && 'byteLength' in url) { // ArrayBuffer
parameters.data = url;
@ -446,41 +489,34 @@ var PDFView = {
}
var url = this.url.split('#')[0];
if (PDFJS.isFirefoxExtension) {
// Document isn't ready just try to download with the url.
if (!this.pdfDocument) {
noData();
return;
}
this.pdfDocument.getData().then(
function getDataSuccess(data) {
var bb = new MozBlobBuilder();
bb.append(data.buffer);
var blobUrl = window.URL.createObjectURL(
bb.getBlob('application/pdf'));
FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url },
function response(err) {
if (err) {
// This error won't really be helpful because it's likely the
// fallback won't work either (or is already open).
PDFView.error('PDF failed to download.');
}
window.URL.revokeObjectURL(blobUrl);
}
);
},
noData // Error ocurred try downloading with just the url.
);
} else {
url += '#pdfjs.action=download', '_parent';
window.open(url, '_parent');
// Document isn't ready just try to download with the url.
if (!this.pdfDocument) {
noData();
return;
}
this.pdfDocument.getData().then(
function getDataSuccess(data) {
var bb = new MozBlobBuilder();
bb.append(data.buffer);
var blobUrl = window.URL.createObjectURL(
bb.getBlob('application/pdf'));
FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url },
function response(err) {
if (err) {
// This error won't really be helpful because it's likely the
// fallback won't work either (or is already open).
PDFView.error('PDF failed to download.');
}
window.URL.revokeObjectURL(blobUrl);
}
);
},
noData // Error occurred try downloading with just the url.
);
},
fallback: function pdfViewFallback() {
if (!PDFJS.isFirefoxExtension)
return;
// Only trigger the fallback once so we don't spam the user with messages
// for one PDF.
if (this.fellback)
@ -543,9 +579,7 @@ var PDFView = {
* @param {String} anchor The anchor hash include the #.
*/
getAnchorUrl: function getAnchorUrl(anchor) {
if (PDFJS.isFirefoxExtension)
return this.url.split('#')[0] + anchor;
return anchor;
return this.url.split('#')[0] + anchor;
},
/**
@ -579,40 +613,12 @@ var PDFView = {
}
}
}
if (PDFJS.isFirefoxExtension) {
console.error(message + '\n' + moreInfoText);
this.fallback();
return;
}
var errorWrapper = document.getElementById('errorWrapper');
errorWrapper.removeAttribute('hidden');
var errorMessage = document.getElementById('errorMessage');
errorMessage.textContent = message;
var loadingBox = document.getElementById('loadingBox');
loadingBox.setAttribute('hidden', 'true');
var closeButton = document.getElementById('errorClose');
closeButton.onclick = function() {
errorWrapper.setAttribute('hidden', 'true');
};
var errorMoreInfo = document.getElementById('errorMoreInfo');
var moreInfoButton = document.getElementById('errorShowMore');
var lessInfoButton = document.getElementById('errorShowLess');
moreInfoButton.onclick = function() {
errorMoreInfo.removeAttribute('hidden');
moreInfoButton.setAttribute('hidden', 'true');
lessInfoButton.removeAttribute('hidden');
};
lessInfoButton.onclick = function() {
errorMoreInfo.setAttribute('hidden', 'true');
moreInfoButton.removeAttribute('hidden');
lessInfoButton.setAttribute('hidden', 'true');
};
moreInfoButton.removeAttribute('hidden');
lessInfoButton.setAttribute('hidden', 'true');
errorMoreInfo.value = moreInfoText;
errorMoreInfo.rows = moreInfoText.split('\n').length - 1;
console.error(message + '\n' + moreInfoText);
this.fallback();
},
progress: function pdfViewProgress(level) {
@ -771,7 +777,7 @@ var PDFView = {
}
},
getHighestPriority: function pdfViewGetHighestPriority(visibleViews, views,
getHighestPriority: function pdfViewGetHighestPriority(visible, views,
scrolledDown) {
// The state has changed figure out which page has the highest priority to
// render next (if any).
@ -779,35 +785,35 @@ var PDFView = {
// 1 visible pages
// 2 if last scrolled down page after the visible pages
// 2 if last scrolled up page before the visible pages
var visibleViews = visible.views;
var numVisible = visibleViews.length;
if (numVisible === 0) {
info('No visible views.');
return false;
}
for (var i = 0; i < numVisible; ++i) {
var view = visibleViews[i].view;
if (!this.isViewFinshed(view))
if (!this.isViewFinished(view))
return view;
}
// All the visible views have rendered, try to render next/previous pages.
if (scrolledDown) {
var lastVisible = visibleViews[visibleViews.length - 1];
var nextPageIndex = lastVisible.id;
var nextPageIndex = visible.last.id;
// ID's start at 1 so no need to add 1.
if (views[nextPageIndex] && !this.isViewFinshed(views[nextPageIndex]))
if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex]))
return views[nextPageIndex];
} else {
var previousPageIndex = visibleViews[0].id - 2;
var previousPageIndex = visible.first.id - 2;
if (views[previousPageIndex] &&
!this.isViewFinshed(views[previousPageIndex]))
!this.isViewFinished(views[previousPageIndex]))
return views[previousPageIndex];
}
// Everything that needs to be rendered has been.
return false;
},
isViewFinshed: function pdfViewNeedsRendering(view) {
isViewFinished: function pdfViewNeedsRendering(view) {
return view.renderingState === RenderingStates.FINISHED;
},
@ -836,14 +842,14 @@ var PDFView = {
search: function pdfViewStartSearch() {
// Limit this function to run every <SEARCH_TIMEOUT>ms.
var SEARCH_TIMEOUT = 250;
var lastSeach = this.lastSearch;
var lastSearch = this.lastSearch;
var now = Date.now();
if (lastSeach && (now - lastSeach) < SEARCH_TIMEOUT) {
if (lastSearch && (now - lastSearch) < SEARCH_TIMEOUT) {
if (!this.searchTimer) {
this.searchTimer = setTimeout(function resumeSearch() {
PDFView.search();
},
SEARCH_TIMEOUT - (now - lastSeach)
SEARCH_TIMEOUT - (now - lastSearch)
);
}
return;
@ -912,7 +918,6 @@ var PDFView = {
}
if ('page' in params) {
var pageNumber = (params.page | 0) || 1;
this.page = pageNumber;
if ('zoom' in params) {
var zoomArgs = params.zoom.split(','); // scale,left,top
// building destination array
@ -928,9 +933,9 @@ var PDFView = {
(zoomArgs[2] | 0), zoomArg];
var currentPage = this.pages[pageNumber - 1];
currentPage.scrollIntoView(dest);
} else
this.page = params.page; // simple page
return;
} else {
this.page = pageNumber; // simple page
}
}
} else if (/^\d+$/.test(hash)) // page number
this.page = hash;
@ -1001,13 +1006,13 @@ var PDFView = {
extractPageText(pageIndex + 1);
}
);
};
}
extractPageText(0);
},
getVisiblePages: function pdfViewGetVisiblePages() {
return this.getVisibleElements(this.container,
this.pages);
this.pages, true);
},
getVisibleThumbs: function pdfViewGetVisibleThumbs() {
@ -1016,11 +1021,12 @@ var PDFView = {
},
// Generic helper to find out what elements are visible within a scroll pane.
getVisibleElements: function pdfViewGetVisibleElements(scrollEl, views) {
getVisibleElements: function pdfViewGetVisibleElements(
scrollEl, views, sortByVisibility) {
var currentHeight = 0, view;
var top = scrollEl.scrollTop;
for (var i = 1; i <= views.length; ++i) {
for (var i = 1, ii = views.length; i <= ii; ++i) {
view = views[i - 1];
currentHeight = view.el.offsetTop;
if (currentHeight + view.el.clientHeight > top)
@ -1029,16 +1035,47 @@ var PDFView = {
}
var visible = [];
var bottom = top + scrollEl.clientHeight;
for (; i <= views.length && currentHeight < bottom; ++i) {
view = views[i - 1];
currentHeight = view.el.offsetTop;
visible.push({ id: view.id, y: currentHeight,
view: view });
currentHeight += view.el.clientHeight;
// 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};
}
return 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;
nextHeight = currentHeight + viewHeight;
hidden = Math.max(0, top - currentHeight) +
Math.max(0, nextHeight - bottom);
percent = Math.floor((viewHeight - hidden) * 100.0 / viewHeight);
visible.push({ id: view.id, y: currentHeight,
view: view, percent: percent });
currentHeight = nextHeight;
}
var first = visible[0];
var last = visible[visible.length - 1];
if (sortByVisibility) {
visible.sort(function(a, b) {
var pc = a.percent - b.percent;
if (Math.abs(pc) > 0.001)
return -pc;
return a.id - b.id; // ensure stability
});
}
return {first: first, last: last, views: visible};
},
// Helper function to parse query string (e.g. ?param1=value&parm2=...).
@ -1072,6 +1109,44 @@ var PDFView = {
var div = document.getElementById('printContainer');
while (div.hasChildNodes())
div.removeChild(div.lastChild);
},
fullscreen: function pdfViewFullscreen() {
var isFullscreen = document.fullscreen || document.mozFullScreen ||
document.webkitIsFullScreen;
if (isFullscreen) {
return false;
}
var wrapper = document.getElementById('viewerContainer');
if (document.documentElement.requestFullScreen) {
wrapper.requestFullScreen();
} else if (document.documentElement.mozRequestFullScreen) {
wrapper.mozRequestFullScreen();
} else if (document.documentElement.webkitRequestFullScreen) {
wrapper.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} else {
return false;
}
this.isFullscreen = true;
var currentPage = this.pages[this.page - 1];
this.previousScale = this.currentScaleValue;
this.parseScale('page-fit', true);
// Wait for fullscreen to take effect
setTimeout(function() {
currentPage.scrollIntoView();
}, 0);
return true;
},
exitFullscreen: function pdfViewExitFullscreen() {
this.isFullscreen = false;
this.parseScale(this.previousScale);
this.page = this.page;
}
};
@ -1637,13 +1712,13 @@ var CustomStyle = (function CustomStyleClosure() {
//if all fails then set to undefined
return (_cache[propName] = 'undefined');
}
};
CustomStyle.setProp = function set(propName, element, str) {
var prop = this.getProp(propName);
if (prop != 'undefined')
element.style[prop] = str;
}
};
return CustomStyle;
})();
@ -1665,11 +1740,15 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
var renderInterval = 0;
var resumeInterval = 500; // in ms
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// Render the text layer, one div at a time
function renderTextLayer() {
if (textDivs.length === 0) {
clearInterval(renderTimer);
renderingDone = true;
self.textLayerDiv = textLayerDiv = canvas = ctx = null;
return;
}
var textDiv = textDivs.shift();
@ -1678,9 +1757,12 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
if (textDiv.dataset.textLength > 1) { // avoid div by zero
// Adjust div width to match canvas text
// Due to the .offsetWidth calls, this is slow
// This needs to come after appending to the DOM
var textScale = textDiv.dataset.canvasWidth / textDiv.offsetWidth;
ctx.font = textDiv.style.fontSize + ' sans-serif';
var width = ctx.measureText(textDiv.textContent).width;
var textScale = textDiv.dataset.canvasWidth / width;
CustomStyle.setProp('transform' , textDiv,
'scale(' + textScale + ', 1)');
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
@ -1706,7 +1788,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
// Resume rendering
renderTimer = setInterval(renderTextLayer, renderInterval);
}, resumeInterval);
}; // textLayerOnScroll
} // textLayerOnScroll
window.addEventListener('scroll', textLayerOnScroll, false);
}; // endLayout
@ -1730,19 +1812,13 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
};
};
window.addEventListener('load', function webViewerLoad(evt) {
document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
PDFView.initialize();
var params = PDFView.parseQueryString(document.location.search.substring(1));
var file = PDFJS.isFirefoxExtension ?
window.location.toString() : params.file || kDefaultURL;
var file = window.location.toString()
if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader ||
!window.FileList || !window.Blob) {
document.getElementById('openFile').setAttribute('hidden', 'true');
} else {
document.getElementById('fileInput').value = null;
}
document.getElementById('openFile').setAttribute('hidden', 'true');
// Special debugging flags in the hash section of the URL.
var hash = document.location.hash.substring(1);
@ -1751,18 +1827,22 @@ window.addEventListener('load', function webViewerLoad(evt) {
if ('disableWorker' in hashParams)
PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
if (!PDFJS.isFirefoxExtension) {
var locale = navigator.language;
if ('locale' in hashParams)
locale = hashParams['locale'];
mozL10n.language.code = locale;
if ('textLayer' in hashParams) {
switch (hashParams['textLayer']) {
case 'off':
PDFJS.disableTextLayer = true;
break;
case 'visible':
case 'shadow':
case 'hover':
var viewer = document.getElementById('viewer');
viewer.classList.add('textLayer-' + hashParams['textLayer']);
break;
}
}
if ('disableTextLayer' in hashParams)
PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true');
if ('pdfBug' in hashParams &&
(!PDFJS.isFirefoxExtension || FirefoxCom.requestSync('pdfBugEnabled'))) {
if ('pdfBug' in hashParams && FirefoxCom.requestSync('pdfBugEnabled')) {
PDFJS.pdfBug = true;
var pdfBug = hashParams['pdfBug'];
var enabled = pdfBug.split(',');
@ -1770,8 +1850,7 @@ window.addEventListener('load', function webViewerLoad(evt) {
PDFBug.init();
}
if (!PDFJS.isFirefoxExtension ||
(PDFJS.isFirefoxExtension && FirefoxCom.requestSync('searchEnabled'))) {
if (FirefoxCom.requestSync('searchEnabled')) {
document.querySelector('#viewSearch').classList.remove('hidden');
}
@ -1779,6 +1858,10 @@ window.addEventListener('load', function webViewerLoad(evt) {
document.getElementById('print').classList.add('hidden');
}
if (!PDFView.supportsFullscreen) {
document.getElementById('fullscreen').classList.add('hidden');
}
// Listen for warnings to trigger the fallback UI. Errors should be caught
// and call PDFView.error() so we don't need to listen for those.
PDFJS.LogManager.addLogger({
@ -1807,21 +1890,49 @@ window.addEventListener('load', function webViewerLoad(evt) {
PDFView.renderHighestPriority();
});
if (FirefoxCom.requestSync('getLoadingType') == 'passive') {
PDFView.setTitleUsingUrl(file);
PDFView.initPassiveLoading();
return;
}
PDFView.open(file, 0);
}, true);
function updateViewarea() {
if (!PDFView.initialized)
return;
var visiblePages = PDFView.getVisiblePages();
var visible = PDFView.getVisiblePages();
var visiblePages = visible.views;
PDFView.renderHighestPriority();
updateViewarea.inProgress = true; // used in "set page"
var currentId = PDFView.page;
var firstPage = visiblePages[0];
PDFView.page = firstPage.id;
updateViewarea.inProgress = false;
var firstPage = visible.first;
for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
i < ii; ++i) {
var page = visiblePages[i];
if (page.percent < 100)
break;
if (page.id === PDFView.page) {
stillFullyVisible = true;
break;
}
}
if (!stillFullyVisible) {
currentId = visiblePages[0].id;
}
if (!PDFView.isFullscreen) {
updateViewarea.inProgress = true; // used in "set page"
PDFView.page = currentId;
updateViewarea.inProgress = false;
}
var currentScale = PDFView.currentScale;
var currentScaleValue = PDFView.currentScaleValue;
@ -1867,21 +1978,14 @@ window.addEventListener('change', function webViewerChange(evt) {
// Read the local file into a Uint8Array.
var fileReader = new FileReader();
fileReader.onload = function webViewerChangeFileReaderOnload(evt) {
var data = evt.target.result;
var buffer = new ArrayBuffer(data.length);
var buffer = evt.target.result;
var uint8Array = new Uint8Array(buffer);
for (var i = 0; i < data.length; i++)
uint8Array[i] = data.charCodeAt(i);
PDFView.open(uint8Array, 0);
};
// Read as a binary string since "readAsArrayBuffer" is not yet
// implemented in Firefox.
var file = files[0];
fileReader.readAsBinaryString(file);
document.title = file.name;
fileReader.readAsArrayBuffer(file);
PDFView.setTitleUsingUrl(file.name);
// URL does not reflect proper document location - hiding some icons.
document.getElementById('viewBookmark').setAttribute('hidden', 'true');
@ -1938,13 +2042,13 @@ window.addEventListener('pagechange', function pagechange(evt) {
var thumbnail = document.getElementById('thumbnailContainer' + page);
thumbnail.classList.add('selected');
var visibleThumbs = PDFView.getVisibleThumbs();
var numVisibleThumbs = visibleThumbs.length;
var numVisibleThumbs = visibleThumbs.views.length;
// If the thumbnail isn't currently visible scroll it into view.
if (numVisibleThumbs > 0) {
var first = visibleThumbs[0].id;
var first = visibleThumbs.first.id;
// Account for only one thumbnail being visible.
var last = numVisibleThumbs > 1 ?
visibleThumbs[numVisibleThumbs - 1].id : first;
visibleThumbs.last.id : first;
if (page <= first || page >= last)
thumbnail.scrollIntoView();
}
@ -2007,7 +2111,7 @@ window.addEventListener('keydown', function keydown(evt) {
return;
var controlsElement = document.getElementById('controls');
while (curElement) {
if (curElement === controlsElement)
if (curElement === controlsElement && !PDFView.isFullscreen)
return; // ignoring if the 'controls' element is focused
curElement = curElement.parentNode;
}
@ -2026,6 +2130,13 @@ window.addEventListener('keydown', function keydown(evt) {
PDFView.page++;
handled = true;
break;
case 32: // spacebar
if (PDFView.isFullscreen) {
PDFView.page++;
handled = true;
}
break;
}
}
@ -2041,3 +2152,20 @@ window.addEventListener('beforeprint', function beforePrint(evt) {
window.addEventListener('afterprint', function afterPrint(evt) {
PDFView.afterPrint();
});
(function fullscreenClosure() {
function fullscreenChange(e) {
var isFullscreen = document.fullscreen || document.mozFullScreen ||
document.webkitIsFullScreen;
if (!isFullscreen) {
PDFView.exitFullscreen();
}
}
window.addEventListener('fullscreenchange', fullscreenChange, false);
window.addEventListener('mozfullscreenchange', fullscreenChange, false);
window.addEventListener('webkitfullscreenchange', fullscreenChange, false);
})();

View File

@ -9,6 +9,7 @@ content/web/images/loading-icon.gif
content/web/images/texture.png
content/web/images/toolbarButton-bookmark.png
content/web/images/toolbarButton-download.png
content/web/images/toolbarButton-fullscreen.png
content/web/images/toolbarButton-menuArrows.png
content/web/images/toolbarButton-openFile.png
content/web/images/toolbarButton-pageDown-rtl.png

View File

@ -264,6 +264,9 @@ class Automation(object):
# Open database and create table
permDB = sqlite3.connect(os.path.join(profileDir, "permissions.sqlite"))
cursor = permDB.cursor();
cursor.execute("PRAGMA user_version=3");
# SQL copied from nsPermissionManager.cpp
cursor.execute("""CREATE TABLE moz_hosts (
id INTEGER PRIMARY KEY,
@ -271,14 +274,16 @@ class Automation(object):
type TEXT,
permission INTEGER,
expireType INTEGER,
expireTime INTEGER)""")
expireTime INTEGER,
appId INTEGER,
isInBrowserElement INTEGER)""")
# Insert desired permissions
c = 0
for perm in permissions.keys():
for host,allow in permissions[perm]:
c += 1
cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)",
cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0, 0, 0)",
(c, host, perm, 1 if allow else 2))
# Commit and close

View File

@ -389,6 +389,8 @@ function runTest() {
checkIFrame(this, data);
};
iframe.addChild = function() {
SpecialPowers.addPermission("browser", true, iframe.contentDocument);
var childFrame = document.createElement('iframe');
if (data.child.app) {
@ -429,7 +431,6 @@ function runTest() {
var gTestRunner = runTest();
SpecialPowers.addPermission("browser", true, "http://example.org");
SpecialPowers.pushPrefEnv({'set':[["dom.mozBrowserFramesEnabled", true]]},
function() { gTestRunner.next(); });

View File

@ -1771,8 +1771,8 @@ protected:
// We hold a strong reference to mNodeInfoManager through mNodeInfo
nsNodeInfoManager* mNodeInfoManager; // [STRONG]
nsRefPtr<mozilla::css::Loader> mCSSLoader;
mozilla::css::ImageLoader* mStyleImageLoader; // [STRONG]
nsHTMLStyleSheet* mAttrStyleSheet;
nsRefPtr<mozilla::css::ImageLoader> mStyleImageLoader;
nsRefPtr<nsHTMLStyleSheet> mAttrStyleSheet;
// The set of all object, embed, applet, video and audio elements for
// which this is the owner document. (They might not be in the document.)

View File

@ -1622,7 +1622,6 @@ nsDocument::~nsDocument()
}
if (mAttrStyleSheet) {
mAttrStyleSheet->SetOwningDocument(nullptr);
NS_RELEASE(mAttrStyleSheet);
}
if (mStyleAttrStyleSheet)
mStyleAttrStyleSheet->SetOwningDocument(nullptr);
@ -1643,7 +1642,6 @@ nsDocument::~nsDocument()
if (mStyleImageLoader) {
mStyleImageLoader->DropDocumentReference();
NS_RELEASE(mStyleImageLoader);
}
delete mHeaderData;
@ -2001,7 +1999,6 @@ nsDocument::Init()
mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
mStyleImageLoader = new mozilla::css::ImageLoader(this);
NS_ADDREF(mStyleImageLoader);
mNodeInfoManager = new nsNodeInfoManager();
nsresult rv = mNodeInfoManager->Init(this);
@ -2278,7 +2275,6 @@ nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
mAttrStyleSheet->Reset(aURI);
} else {
mAttrStyleSheet = new nsHTMLStyleSheet(aURI, this);
NS_ADDREF(mAttrStyleSheet);
}
// Don't use AddStyleSheet, since it'll put the sheet into style

View File

@ -18,6 +18,11 @@ function runTest() {
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
appId: principal.appId,
isInBrowserElement: true });
iframe = document.createElement('iframe');
iframe.mozbrowser = true;
@ -55,6 +60,12 @@ function finish() {
// expected, but if we don't remove our listener, then we'll end up causing
// the /next/ test to fail!
iframe.removeEventListener('mozbrowsershowmodalprompt', checkMessage);
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
appId: principal.appId,
isInBrowserElement: true });
SimpleTest.finish();
}

View File

@ -12,6 +12,11 @@ function runTest() {
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
appId: principal.appId,
isInBrowserElement: true });
var iframe = document.createElement('iframe');
iframe.mozbrowser = true;
@ -35,7 +40,7 @@ function runTest() {
SimpleTest.executeSoon(function() {
SimpleTest.executeSoon(function() {
SimpleTest.executeSoon(function() {
SimpleTest.finish();
finish();
});
});
});
@ -50,4 +55,13 @@ function runTest() {
document.body.appendChild(iframe);
}
function finish() {
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
appId: principal.appId,
isInBrowserElement: true });
SimpleTest.finish();
}
runTest();

View File

@ -95,6 +95,9 @@
#include "nsIRemoteBlob.h"
#include "StructuredCloneUtils.h"
#include "URIUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsContentUtils.h"
#include "nsIPrincipal.h"
using namespace mozilla::docshell;
using namespace mozilla::dom::devicestorage;
@ -847,7 +850,20 @@ ContentChild::RecvAddPermission(const IPC::Permission& permission)
NS_ABORT_IF_FALSE(permissionManager,
"We have no permissionManager in the Content process !");
permissionManager->AddInternal(nsCString(permission.host),
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + nsCString(permission.host));
NS_ENSURE_TRUE(uri, true);
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
MOZ_ASSERT(secMan);
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = secMan->GetAppCodebasePrincipal(uri, permission.appId,
permission.isInBrowserElement,
getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, true);
permissionManager->AddInternal(principal,
nsCString(permission.type),
permission.capability,
0,

View File

@ -784,6 +784,10 @@ ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissio
nsCString host;
perm->GetHost(host);
uint32_t appId;
perm->GetAppId(&appId);
bool isInBrowserElement;
perm->GetIsInBrowserElement(&isInBrowserElement);
nsCString type;
perm->GetType(type);
uint32_t capability;
@ -793,8 +797,10 @@ ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissio
int64_t expireTime;
perm->GetExpireTime(&expireTime);
aPermissions->AppendElement(IPC::Permission(host, type, capability,
expireType, expireTime));
aPermissions->AppendElement(IPC::Permission(host, appId,
isInBrowserElement, type,
capability, expireType,
expireTime));
}
// Ask for future changes

View File

@ -10,6 +10,8 @@
NS_IMPL_ISUPPORTS1(nsPermission, nsIPermission)
nsPermission::nsPermission(const nsACString &aHost,
uint32_t aAppId,
bool aIsInBrowserElement,
const nsACString &aType,
uint32_t aCapability,
uint32_t aExpireType,
@ -19,10 +21,8 @@ nsPermission::nsPermission(const nsACString &aHost,
, mCapability(aCapability)
, mExpireType(aExpireType)
, mExpireTime(aExpireTime)
{
}
nsPermission::~nsPermission()
, mAppId(aAppId)
, mIsInBrowserElement(aIsInBrowserElement)
{
}
@ -33,6 +33,20 @@ nsPermission::GetHost(nsACString &aHost)
return NS_OK;
}
NS_IMETHODIMP
nsPermission::GetAppId(uint32_t* aAppId)
{
*aAppId = mAppId;
return NS_OK;
}
NS_IMETHODIMP
nsPermission::GetIsInBrowserElement(bool* aIsInBrowserElement)
{
*aIsInBrowserElement = mIsInBrowserElement;
return NS_OK;
}
NS_IMETHODIMP
nsPermission::GetType(nsACString &aType)
{

View File

@ -19,19 +19,23 @@ public:
NS_DECL_NSIPERMISSION
nsPermission(const nsACString &aHost,
const nsACString &aType,
uint32_t aAppId,
bool aIsInBrowserElement,
const nsACString &aType,
uint32_t aCapability,
uint32_t aExpireType,
int64_t aExpireTime);
virtual ~nsPermission();
virtual ~nsPermission() {};
protected:
nsCString mHost;
nsCString mType;
uint32_t mCapability;
uint32_t mExpireType;
int64_t mExpireTime;
uint32_t mAppId;
bool mIsInBrowserElement;
};
#endif // nsPermission_h__

View File

@ -23,6 +23,7 @@
#include "nsXULAppAPI.h"
#include "nsIPrincipal.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
static nsPermissionManager *gPermissionManager = nullptr;
@ -70,42 +71,66 @@ ChildProcess()
////////////////////////////////////////////////////////////////////////////////
#define PL_ARENA_CONST_ALIGN_MASK 3
#include "plarena.h"
namespace {
static PLArenaPool *gHostArena = nullptr;
// making sHostArena 512b for nice allocation
// growing is quite cheap
#define HOST_ARENA_SIZE 512
// equivalent to strdup() - does no error checking,
// we're assuming we're only called with a valid pointer
static char *
ArenaStrDup(const char* str, PLArenaPool* aArena)
nsresult
GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement,
nsIPrincipal** aPrincipal)
{
void* mem;
const uint32_t size = strlen(str) + 1;
PL_ARENA_ALLOCATE(mem, aArena, size);
if (mem)
memcpy(mem, str, size);
return static_cast<char*>(mem);
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
nsCOMPtr<nsIURI> uri;
// NOTE: we use "http://" as a protocal but we will just use the host so it
// doesn't really matter.
NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost);
return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal);
}
nsHostEntry::nsHostEntry(const char* aHost)
nsresult
GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal)
{
mHost = ArenaStrDup(aHost, gHostArena);
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal);
}
// XXX this can fail on OOM
nsHostEntry::nsHostEntry(const nsHostEntry& toCopy)
: mHost(toCopy.mHost)
, mPermissions(toCopy.mPermissions)
nsresult
GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal)
{
return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal);
}
nsresult
GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
uri = NS_GetInnermostURI(uri);
NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
rv = uri->GetAsciiHost(aHost);
if (NS_FAILED(rv) || aHost.IsEmpty()) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
} // anonymous namespace
////////////////////////////////////////////////////////////////////////////////
nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal)
{
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
}
/**
* Simple callback used by |AsyncClose| to trigger a treatment once
@ -217,7 +242,7 @@ NS_IMETHODIMP DeleteFromMozHostListener::HandleCompletion(uint16_t aReason)
// nsPermissionManager Implementation
static const char kPermissionsFileName[] = "permissions.sqlite";
#define HOSTS_SCHEMA_VERSION 2
#define HOSTS_SCHEMA_VERSION 3
static const char kHostpermFileName[] = "hostperm.1";
@ -268,7 +293,7 @@ nsPermissionManager::Init()
{
nsresult rv;
mHostTable.Init();
mPermissionTable.Init();
mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
if (NS_SUCCEEDED(rv)) {
@ -283,7 +308,12 @@ nsPermissionManager::Init()
for (uint32_t i = 0; i < perms.Length(); i++) {
const IPC::Permission &perm = perms[i];
AddInternal(perm.host, perm.type, perm.capability, 0, perm.expireType,
nsCOMPtr<nsIPrincipal> principal;
rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
perm.expireTime, eNotify, eNoDBOperation);
}
@ -374,31 +404,33 @@ nsPermissionManager::InitDB(bool aRemoveFile)
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"ALTER TABLE moz_hosts ADD expireTime INTEGER"));
NS_ENSURE_SUCCESS(rv, rv);
}
// fall through to the next upgrade
// TODO: we want to make default version as version 2 in order to fix bug 784875.
case 0:
case 2:
{
// Add appId/isInBrowserElement fields.
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"ALTER TABLE moz_hosts ADD appId INTEGER"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
NS_ENSURE_SUCCESS(rv, rv);
}
// fall through to the next upgrade
// current version.
case HOSTS_SCHEMA_VERSION:
break;
case 0:
{
NS_WARNING("couldn't get schema version!");
// the table may be usable; someone might've just clobbered the schema
// version. we can treat this case like a downgrade using the codepath
// below, by verifying the columns we care about are all there. for now,
// re-set the schema version in the db, in case the checks succeed (if
// they don't, we're dropping the table anyway).
rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
NS_ENSURE_SUCCESS(rv, rv);
}
// fall through to downgrade check
// downgrading.
// if columns have been added to the table, we can still use the ones we
// understand safely. if columns have been deleted or altered, just
@ -410,7 +442,7 @@ nsPermissionManager::InitDB(bool aRemoveFile)
// check if all the expected columns exist
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT host, type, permission, expireType, expireTime FROM moz_hosts"),
"SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"),
getter_AddRefs(stmt));
if (NS_SUCCEEDED(rv))
break;
@ -432,8 +464,8 @@ nsPermissionManager::InitDB(bool aRemoveFile)
// cache frequently used statements (for insertion, deletion, and updating)
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"INSERT INTO moz_hosts "
"(id, host, type, permission, expireType, expireTime) "
"VALUES (?1, ?2, ?3, ?4, ?5, ?6)"), getter_AddRefs(mStmtInsert));
"(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) "
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
@ -473,6 +505,8 @@ nsPermissionManager::CreateTable()
",permission INTEGER"
",expireType INTEGER"
",expireTime INTEGER"
",appId INTEGER"
",isInBrowserElement INTEGER"
")"));
}
@ -483,28 +517,13 @@ nsPermissionManager::Add(nsIURI *aURI,
uint32_t aExpireType,
int64_t aExpireTime)
{
ENSURE_NOT_CHILD_PROCESS;
NS_ENSURE_ARG_POINTER(aURI);
NS_ENSURE_ARG_POINTER(aType);
NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
aExpireType == nsIPermissionManager::EXPIRE_TIME ||
aExpireType == nsIPermissionManager::EXPIRE_SESSION,
NS_ERROR_INVALID_ARG);
nsresult rv;
// Skip addition if the permission is already expired.
if (aExpireType == nsIPermissionManager::EXPIRE_TIME &&
aExpireTime <= PR_Now() / 1000)
return NS_OK;
nsCAutoString host;
rv = GetHost(aURI, host);
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
return AddInternal(host, nsDependentCString(aType), aPermission, 0,
aExpireType, aExpireTime, eNotify, eWriteToDB);
return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime);
}
NS_IMETHODIMP
@ -512,7 +531,19 @@ nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
const char* aType, uint32_t aPermission,
uint32_t aExpireType, int64_t aExpireTime)
{
ENSURE_NOT_CHILD_PROCESS;
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_ARG_POINTER(aType);
NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
aExpireType == nsIPermissionManager::EXPIRE_TIME ||
aExpireType == nsIPermissionManager::EXPIRE_SESSION,
NS_ERROR_INVALID_ARG);
// Skip addition if the permission is already expired.
if (aExpireType == nsIPermissionManager::EXPIRE_TIME &&
aExpireTime <= (PR_Now() / 1000)) {
return NS_OK;
}
// We don't add the system principal because it actually has no URI and we
// always allow action for them.
@ -520,14 +551,12 @@ nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
return NS_OK;
}
nsCOMPtr<nsIURI> uri;
aPrincipal->GetURI(getter_AddRefs(uri));
return Add(uri, aType, aPermission, aExpireType, aExpireTime);
return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
aExpireType, aExpireTime, eNotify, eWriteToDB);
}
nsresult
nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
const nsAFlatCString &aType,
uint32_t aPermission,
int64_t aID,
@ -536,9 +565,20 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
NotifyOperationType aNotifyOperation,
DBOperationType aDBOperation)
{
nsCAutoString host;
nsresult rv = GetHostForPrincipal(aPrincipal, host);
NS_ENSURE_SUCCESS(rv, rv);
if (!IsChildProcess()) {
IPC::Permission permission((aHost),
(aType),
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
bool isInBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
NS_ENSURE_SUCCESS(rv, rv);
IPC::Permission permission(host, appId, isInBrowserElement, aType,
aPermission, aExpireType, aExpireTime);
nsTArray<ContentParent*> cplist;
@ -550,23 +590,17 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
}
}
if (!gHostArena) {
gHostArena = new PLArenaPool;
if (!gHostArena)
return NS_ERROR_OUT_OF_MEMORY;
PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
}
// look up the type index
int32_t typeIndex = GetTypeIndex(aType.get(), true);
NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
// When an entry already exists, PutEntry will return that, instead
// of adding a new one
nsHostEntry *entry = mHostTable.PutEntry(aHost.get());
nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal);
PermissionHashKey* entry = mPermissionTable.PutEntry(key);
if (!entry) return NS_ERROR_FAILURE;
if (!entry->GetKey()) {
mHostTable.RawRemoveEntry(entry);
mPermissionTable.RawRemoveEntry(entry);
return NS_ERROR_OUT_OF_MEMORY;
}
@ -580,7 +614,7 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
op = eOperationAdding;
} else {
nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
// remove the permission if the permission is UNKNOWN, update the
// permission if its value or expire type have changed OR if the time has
@ -618,13 +652,24 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
id = aID;
}
entry->GetPermissions().AppendElement(nsPermissionEntry(typeIndex, aPermission, id, aExpireType, aExpireTime));
entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime));
if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
UpdateDB(op, mStmtInsert, id, aHost, aType, aPermission, aExpireType, aExpireTime);
if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
bool isInBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
NS_ENSURE_SUCCESS(rv, rv);
UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement);
}
if (aNotifyOperation == eNotify) {
NotifyObserversWithPermission(aHost,
NotifyObserversWithPermission(host,
entry->GetKey()->mAppId,
entry->GetKey()->mIsInBrowserElement,
mTypeArray[typeIndex],
aPermission,
aExpireType,
@ -637,20 +682,24 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
case eOperationRemoving:
{
nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
id = oldPermissionEntry.mID;
entry->GetPermissions().RemoveElementAt(index);
// If no more types are present, remove the entry
if (entry->GetPermissions().IsEmpty())
mHostTable.RawRemoveEntry(entry);
mPermissionTable.RawRemoveEntry(entry);
if (aDBOperation == eWriteToDB)
UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
nsIPermissionManager::EXPIRE_NEVER, 0);
// We care only about the id here so we pass dummy values for all other
// parameters.
UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
nsIPermissionManager::EXPIRE_NEVER, 0, 0, false);
if (aNotifyOperation == eNotify) {
NotifyObserversWithPermission(aHost,
NotifyObserversWithPermission(host,
entry->GetKey()->mAppId,
entry->GetKey()->mIsInBrowserElement,
mTypeArray[typeIndex],
oldPermissionEntry.mPermission,
oldPermissionEntry.mExpireType,
@ -667,10 +716,15 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
entry->GetPermissions()[index].mPermission = aPermission;
if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), aPermission, aExpireType, aExpireTime);
// We care only about the id, the permission and expireType/expireTime here.
// We pass dummy values for all other parameters.
UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
aPermission, aExpireType, aExpireTime, 0, false);
if (aNotifyOperation == eNotify) {
NotifyObserversWithPermission(aHost,
NotifyObserversWithPermission(host,
entry->GetKey()->mAppId,
entry->GetKey()->mIsInBrowserElement,
mTypeArray[typeIndex],
aPermission,
aExpireType,
@ -689,12 +743,28 @@ NS_IMETHODIMP
nsPermissionManager::Remove(const nsACString &aHost,
const char *aType)
{
ENSURE_NOT_CHILD_PROCESS;
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(aHost, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
return RemoveFromPrincipal(principal, aType);
}
NS_IMETHODIMP
nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
const char* aType)
{
ENSURE_NOT_CHILD_PROCESS;
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_ARG_POINTER(aType);
// System principals are never added to the database, no need to remove them.
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
return NS_OK;
}
// AddInternal() handles removal, just let it do the work
return AddInternal(PromiseFlatCString(aHost),
return AddInternal(aPrincipal,
nsDependentCString(aType),
nsIPermissionManager::UNKNOWN_ACTION,
0,
@ -704,27 +774,6 @@ nsPermissionManager::Remove(const nsACString &aHost,
eWriteToDB);
}
NS_IMETHODIMP
nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
const char* aType)
{
NS_ENSURE_ARG_POINTER(aPrincipal);
// System principals are never added to the database, no need to remove them.
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
return NS_OK;
}
nsCOMPtr<nsIURI> uri;
aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
nsCAutoString host;
uri->GetHost(host);
return Remove(host, aType);
}
NS_IMETHODIMP
nsPermissionManager::RemoveAll()
{
@ -786,35 +835,11 @@ nsPermissionManager::TestExactPermission(nsIURI *aURI,
const char *aType,
uint32_t *aPermission)
{
return CommonTestPermission(aURI, aType, aPermission, true);
}
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
NS_IMETHODIMP
nsPermissionManager::TestPermission(nsIURI *aURI,
const char *aType,
uint32_t *aPermission)
{
return CommonTestPermission(aURI, aType, aPermission, false);
}
NS_IMETHODIMP
nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
const char* aType,
uint32_t* aPermission)
{
NS_ENSURE_ARG_POINTER(aPrincipal);
// System principals do not have URI so we can't try to get
// retro-compatibility here.
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
*aPermission = nsIPermissionManager::ALLOW_ACTION;
return NS_OK;
}
nsCOMPtr<nsIURI> uri;
aPrincipal->GetURI(getter_AddRefs(uri));
return TestPermission(uri, aType, aPermission);
return TestExactPermissionFromPrincipal(principal, aType, aPermission);
}
NS_IMETHODIMP
@ -831,31 +856,62 @@ nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
return NS_OK;
}
nsCOMPtr<nsIURI> uri;
aPrincipal->GetURI(getter_AddRefs(uri));
return CommonTestPermission(aPrincipal, aType, aPermission, true);
}
return TestExactPermission(uri, aType, aPermission);
NS_IMETHODIMP
nsPermissionManager::TestPermission(nsIURI *aURI,
const char *aType,
uint32_t *aPermission)
{
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
return TestPermissionFromPrincipal(principal, aType, aPermission);
}
NS_IMETHODIMP
nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
const char* aType,
uint32_t* aPermission)
{
NS_ENSURE_ARG_POINTER(aPrincipal);
// System principals do not have URI so we can't try to get
// retro-compatibility here.
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
*aPermission = nsIPermissionManager::ALLOW_ACTION;
return NS_OK;
}
return CommonTestPermission(aPrincipal, aType, aPermission, false);
}
nsresult
nsPermissionManager::CommonTestPermission(nsIURI *aURI,
nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
const char *aType,
uint32_t *aPermission,
bool aExactHostMatch)
{
NS_ENSURE_ARG_POINTER(aURI);
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_ARG_POINTER(aType);
// set the default
*aPermission = nsIPermissionManager::UNKNOWN_ACTION;
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString host;
nsresult rv = GetHost(aURI, host);
rv = GetHostForPrincipal(aPrincipal, host);
// No host doesn't mean an error. Just return the default. Unless this is
// a file uri. In that case use a magic host.
if (NS_FAILED(rv)) {
bool isFile;
rv = aURI->SchemeIs("file", &isFile);
rv = uri->SchemeIs("file", &isFile);
NS_ENSURE_SUCCESS(rv, rv);
if (isFile) {
host.AssignLiteral("<file>");
@ -864,44 +920,74 @@ nsPermissionManager::CommonTestPermission(nsIURI *aURI,
return NS_OK;
}
}
int32_t typeIndex = GetTypeIndex(aType, false);
// If type == -1, the type isn't known,
// so just return NS_OK
if (typeIndex == -1) return NS_OK;
nsHostEntry *entry = GetHostEntry(host, typeIndex, aExactHostMatch);
if (entry)
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
bool isInBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
NS_ENSURE_SUCCESS(rv, rv);
PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
typeIndex, aExactHostMatch);
if (entry) {
*aPermission = entry->GetPermission(typeIndex).mPermission;
}
return NS_OK;
}
// Get hostentry for given host string and permission type.
// walk up the domain if needed.
// return null if nothing found.
// Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
// This is not simply using PermissionKey because we will walk-up domains in
// case of |host| contains sub-domains.
// Returns null if nothing found.
// Also accepts host on the format "<foo>". This will perform an exact match
// lookup as the string doesn't contain any dots.
nsHostEntry *
nsPermissionManager::GetHostEntry(const nsAFlatCString &aHost,
uint32_t aType,
bool aExactHostMatch)
nsPermissionManager::PermissionHashKey*
nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
uint32_t aAppId,
bool aIsInBrowserElement,
uint32_t aType,
bool aExactHostMatch)
{
uint32_t offset = 0;
nsHostEntry *entry;
PermissionHashKey* entry;
int64_t now = PR_Now() / 1000;
do {
entry = mHostTable.GetEntry(aHost.get() + offset);
nsRefPtr<PermissionKey> key = new PermissionKey(Substring(aHost, offset), aAppId, aIsInBrowserElement);
entry = mPermissionTable.GetEntry(key);
if (!entry) {
// This is a temporary fix to have Gaia working and allow a time frame to
// update profiles. With this hack, if a permission isn't found for an app
// the check will be done for the same host outside of any app.
// TODO: remove this with bug 785632.
key = new PermissionKey(Substring(aHost, offset), nsIScriptSecurityManager::NO_APP_ID, false);
entry = mPermissionTable.GetEntry(key);
}
if (entry) {
nsPermissionEntry permEntry = entry->GetPermission(aType);
PermissionEntry permEntry = entry->GetPermission(aType);
// if the entry is expired, remove and keep looking for others.
if (permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME &&
permEntry.mExpireTime <= now)
Remove(aHost, mTypeArray[aType].get());
else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION)
permEntry.mExpireTime <= now) {
nsCOMPtr<nsIPrincipal> principal;
if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) {
return nullptr;
}
RemoveFromPrincipal(principal, mTypeArray[aType].get());
} else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION) {
break;
}
// reset entry, to be able to return null on failure
entry = nullptr;
@ -929,14 +1015,16 @@ struct nsGetEnumeratorData
};
static PLDHashOperator
AddPermissionsToList(nsHostEntry *entry, void *arg)
AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
{
nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
nsPermissionEntry &permEntry = entry->GetPermissions()[i];
nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
nsPermission *perm = new nsPermission(entry->GetHost(),
nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
entry->GetKey()->mAppId,
entry->GetKey()->mIsInBrowserElement,
data->types->ElementAt(permEntry.mType),
permEntry.mPermission,
permEntry.mExpireType,
@ -954,7 +1042,7 @@ NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
nsCOMArray<nsIPermission> array;
nsGetEnumeratorData data(&array, &mTypeArray);
mHostTable.EnumerateEntries(AddPermissionsToList, &data);
mPermissionTable.EnumerateEntries(AddPermissionsToList, &data);
return NS_NewArrayEnumerator(aEnum, array);
}
@ -992,12 +1080,8 @@ nsPermissionManager::RemoveAllFromMemory()
{
mLargestID = 0;
mTypeArray.Clear();
mHostTable.Clear();
if (gHostArena) {
PL_FinishArenaPool(gHostArena);
delete gHostArena;
}
gHostArena = nullptr;
mPermissionTable.Clear();
return NS_OK;
}
@ -1029,6 +1113,8 @@ nsPermissionManager::GetTypeIndex(const char *aType,
// set into an nsIPermission.
void
nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
uint32_t aAppId,
bool aIsInBrowserElement,
const nsCString &aType,
uint32_t aPermission,
uint32_t aExpireType,
@ -1036,7 +1122,8 @@ nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
const PRUnichar *aData)
{
nsCOMPtr<nsIPermission> permission =
new nsPermission(aHost, aType, aPermission, aExpireType, aExpireTime);
new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission,
aExpireType, aExpireTime);
if (permission)
NotifyObservers(permission, aData);
}
@ -1086,7 +1173,7 @@ nsPermissionManager::Read()
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id, host, type, permission, expireType, expireTime "
"SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement "
"FROM moz_hosts"), getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
@ -1095,6 +1182,8 @@ nsPermissionManager::Read()
uint32_t permission;
uint32_t expireType;
int64_t expireTime;
uint32_t appId;
bool isInBrowserElement;
bool hasResult;
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
// explicitly set our entry id counter for use in AddInternal(),
@ -1115,7 +1204,15 @@ nsPermissionManager::Read()
// convert into int64_t value (milliseconds)
expireTime = stmt->AsInt64(5);
rv = AddInternal(host, type, permission, id, expireType, expireTime,
MOZ_ASSERT(stmt->AsInt64(6) >= 0);
appId = static_cast<uint32_t>(stmt->AsInt64(6));
isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
rv = AddInternal(principal, type, permission, id, expireType, expireTime,
eDontNotify, eNoDBOperation);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1185,7 +1282,11 @@ nsPermissionManager::Import()
continue;
}
rv = AddInternal(lineArray[3], lineArray[1], permission, 0,
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
rv = AddInternal(principal, lineArray[1], permission, 0,
nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1210,20 +1311,6 @@ nsPermissionManager::NormalizeToACE(nsCString &aHost)
return mIDNService->ConvertUTF8toACE(aHost, aHost);
}
nsresult
nsPermissionManager::GetHost(nsIURI *aURI, nsACString &aResult)
{
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
if (!innerURI) return NS_ERROR_FAILURE;
nsresult rv = innerURI->GetAsciiHost(aResult);
if (NS_FAILED(rv) || aResult.IsEmpty())
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
void
nsPermissionManager::UpdateDB(OperationType aOp,
mozIStorageStatement* aStmt,
@ -1232,7 +1319,9 @@ nsPermissionManager::UpdateDB(OperationType aOp,
const nsACString &aType,
uint32_t aPermission,
uint32_t aExpireType,
int64_t aExpireTime)
int64_t aExpireTime,
uint32_t aAppId,
bool aIsInBrowserElement)
{
ENSURE_NOT_CHILD_PROCESS_NORET;
@ -1261,6 +1350,12 @@ nsPermissionManager::UpdateDB(OperationType aOp,
if (NS_FAILED(rv)) break;
rv = aStmt->BindInt64ByIndex(5, aExpireTime);
if (NS_FAILED(rv)) break;
rv = aStmt->BindInt64ByIndex(6, aAppId);
if (NS_FAILED(rv)) break;
rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement);
break;
}

View File

@ -16,6 +16,8 @@
#include "nsTArray.h"
#include "nsString.h"
#include "nsPermission.h"
#include "nsHashKeys.h"
#include "nsAutoPtr.h"
class nsIPermission;
class nsIIDNService;
@ -24,107 +26,132 @@ class mozIStorageStatement;
////////////////////////////////////////////////////////////////////////////////
class nsPermissionEntry
{
public:
nsPermissionEntry(uint32_t aType, uint32_t aPermission, int64_t aID,
uint32_t aExpireType, int64_t aExpireTime)
: mType(aType)
, mPermission(aPermission)
, mID(aID)
, mExpireType(aExpireType)
, mExpireTime(aExpireTime) {}
uint32_t mType;
uint32_t mPermission;
int64_t mID;
uint32_t mExpireType;
int64_t mExpireTime;
};
class nsHostEntry : public PLDHashEntryHdr
{
public:
// Hash methods
typedef const char* KeyType;
typedef const char* KeyTypePointer;
nsHostEntry(const char* aHost);
nsHostEntry(const nsHostEntry& toCopy);
~nsHostEntry()
{
}
KeyType GetKey() const
{
return mHost;
}
bool KeyEquals(KeyTypePointer aKey) const
{
return !strcmp(mHost, aKey);
}
static KeyTypePointer KeyToPointer(KeyType aKey)
{
return aKey;
}
static PLDHashNumber HashKey(KeyTypePointer aKey)
{
// PL_DHashStringKey doesn't use the table parameter, so we can safely
// pass nullptr
return PL_DHashStringKey(nullptr, aKey);
}
// force the hashtable to use the copy constructor when shuffling entries
// around, otherwise the Auto part of our nsAutoTArray won't be happy!
enum { ALLOW_MEMMOVE = false };
// Permissions methods
inline const nsDependentCString GetHost() const
{
return nsDependentCString(mHost);
}
inline nsTArray<nsPermissionEntry> & GetPermissions()
{
return mPermissions;
}
inline int32_t GetPermissionIndex(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return i;
return -1;
}
inline nsPermissionEntry GetPermission(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return mPermissions[i];
// unknown permission... return relevant data
nsPermissionEntry unk = nsPermissionEntry(aType, nsIPermissionManager::UNKNOWN_ACTION,
-1, nsIPermissionManager::EXPIRE_NEVER, 0);
return unk;
}
private:
const char *mHost;
nsAutoTArray<nsPermissionEntry, 1> mPermissions;
};
class nsPermissionManager : public nsIPermissionManager,
public nsIObserver,
public nsSupportsWeakReference
{
public:
class PermissionEntry
{
public:
PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission,
uint32_t aExpireType, int64_t aExpireTime)
: mID(aID)
, mType(aType)
, mPermission(aPermission)
, mExpireType(aExpireType)
, mExpireTime(aExpireTime)
{}
int64_t mID;
uint32_t mType;
uint32_t mPermission;
uint32_t mExpireType;
int64_t mExpireTime;
};
/**
* PermissionKey is the key used by PermissionHashKey hash table.
*
* NOTE: It could be implementing nsIHashable but there is no reason to worry
* with XPCOM interfaces while we don't need to.
*/
class PermissionKey
{
public:
PermissionKey(nsIPrincipal* aPrincipal);
PermissionKey(const nsACString& aHost,
uint32_t aAppId,
bool aIsInBrowserElement)
: mHost(aHost)
, mAppId(aAppId)
, mIsInBrowserElement(aIsInBrowserElement)
{
}
bool operator==(const PermissionKey& aKey) const {
return mHost.Equals(aKey.mHost) &&
mAppId == aKey.mAppId &&
mIsInBrowserElement == aKey.mIsInBrowserElement;
}
PLDHashNumber GetHashCode() const {
nsCAutoString str;
str.Assign(mHost);
str.AppendInt(mAppId);
str.AppendInt(static_cast<int32_t>(mIsInBrowserElement));
return mozilla::HashString(str);
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey);
nsCString mHost;
uint32_t mAppId;
bool mIsInBrowserElement;
private:
// Default ctor shouldn't be used.
PermissionKey() MOZ_DELETE;
// Dtor shouldn't be used outside of the class.
~PermissionKey() {};
};
class PermissionHashKey : public nsRefPtrHashKey<PermissionKey>
{
public:
PermissionHashKey(const PermissionKey* aPermissionKey)
: nsRefPtrHashKey<PermissionKey>(aPermissionKey)
{}
PermissionHashKey(const PermissionHashKey& toCopy)
: nsRefPtrHashKey<PermissionKey>(toCopy)
, mPermissions(toCopy.mPermissions)
{}
bool KeyEquals(const PermissionKey* aKey) const
{
return *aKey == *GetKey();
}
static PLDHashNumber HashKey(const PermissionKey* aKey)
{
return aKey->GetHashCode();
}
// Force the hashtable to use the copy constructor when shuffling entries
// around, otherwise the Auto part of our nsAutoTArray won't be happy!
enum { ALLOW_MEMMOVE = false };
inline nsTArray<PermissionEntry> & GetPermissions()
{
return mPermissions;
}
inline int32_t GetPermissionIndex(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return i;
return -1;
}
inline PermissionEntry GetPermission(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return mPermissions[i];
// unknown permission... return relevant data
return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
nsIPermissionManager::EXPIRE_NEVER, 0);
}
private:
nsAutoTArray<PermissionEntry, 1> mPermissions;
};
// nsISupports
NS_DECL_ISUPPORTS
@ -154,7 +181,7 @@ public:
eNotify
};
nsresult AddInternal(const nsAFlatCString &aHost,
nsresult AddInternal(nsIPrincipal* aPrincipal,
const nsAFlatCString &aType,
uint32_t aPermission,
int64_t aID,
@ -164,15 +191,16 @@ public:
DBOperationType aDBOperation);
private:
int32_t GetTypeIndex(const char *aTypeString,
bool aAdd);
nsHostEntry *GetHostEntry(const nsAFlatCString &aHost,
uint32_t aType,
bool aExactHostMatch);
PermissionHashKey* GetPermissionHashKey(const nsACString& aHost,
uint32_t aAppId,
bool aIsInBrowserElement,
uint32_t aType,
bool aExactHostMatch);
nsresult CommonTestPermission(nsIURI *aURI,
nsresult CommonTestPermission(nsIPrincipal* aPrincipal,
const char *aType,
uint32_t *aPermission,
bool aExactHostMatch);
@ -182,6 +210,8 @@ private:
nsresult Import();
nsresult Read();
void NotifyObserversWithPermission(const nsACString &aHost,
uint32_t aAppId,
bool aIsInBrowserElement,
const nsCString &aType,
uint32_t aPermission,
uint32_t aExpireType,
@ -196,7 +226,6 @@ private:
nsresult RemoveAllInternal(bool aNotifyObservers);
nsresult RemoveAllFromMemory();
nsresult NormalizeToACE(nsCString &aHost);
nsresult GetHost(nsIURI *aURI, nsACString &aResult);
static void UpdateDB(OperationType aOp,
mozIStorageStatement* aStmt,
int64_t aID,
@ -204,7 +233,9 @@ private:
const nsACString &aType,
uint32_t aPermission,
uint32_t aExpireType,
int64_t aExpireTime);
int64_t aExpireTime,
uint32_t aAppId,
bool aIsInBrowserElement);
nsCOMPtr<nsIObserverService> mObserverService;
nsCOMPtr<nsIIDNService> mIDNService;
@ -214,7 +245,7 @@ private:
nsCOMPtr<mozIStorageStatement> mStmtDelete;
nsCOMPtr<mozIStorageStatement> mStmtUpdate;
nsTHashtable<nsHostEntry> mHostTable;
nsTHashtable<PermissionHashKey> mPermissionTable;
// a unique, monotonically increasing id used to identify each database entry
int64_t mLargestID;

View File

@ -52,6 +52,10 @@ MOCHITEST_FILES = \
file_loopback_inner.html \
$(NULL)
MOCHITEST_CHROME_FILES = \
test_permissionmanager_app_isolation.html \
$(NULL)
MOCHITEST_BROWSER_FILES = \
browser_test_favicon.js \
$(NULL)

View File

@ -0,0 +1,168 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=758258
-->
<head>
<meta charset="utf-8">
<title>Test for nsIPrincipal extendedOrigin, appStatus and appId</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=758258">Mozilla Bug 758258</a>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
/** Test for Bug 758258 **/
var Ci = Components.interfaces;
var Cc = Components.classes;
SimpleTest.waitForExplicitFinish();
var permManager = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
const gPermName = 'foobar';
var previousPrefs = {
mozBrowserFramesEnabled: undefined,
};
try {
previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
} catch(e)
{
}
SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
// We use http://test/ as url so all apps use the same url and app isolation is
// more obvious.
var gData = [
// APP 1
{
app: 'http://example.org/manifest.webapp',
action: 'read-no',
src: 'http://test/',
},
{
app: 'http://example.org/manifest.webapp',
action: 'write',
src: 'http://test/',
},
{
app: 'http://example.org/manifest.webapp',
action: 'read-yes',
src: 'http://test/',
},
// APP 2
{
app: 'https://example.com/manifest.webapp',
action: 'read-no',
src: 'http://test/',
},
{
app: 'https://example.com/manifest.webapp',
action: 'write',
src: 'http://test/',
},
{
app: 'https://example.com/manifest.webapp',
action: 'read-yes',
src: 'http://test/',
},
// Browser
{
browser: true,
action: 'read-no',
src: 'http://test/',
},
{
browser: true,
action: 'write',
src: 'http://test/',
},
{
browser: true,
action: 'read-yes',
src: 'http://test/',
},
];
function runTest() {
for (var i in gData) {
var iframe = document.createElement('iframe');
var data = gData[i];
if (data.app) {
iframe.setAttribute('mozbrowser', '');
iframe.setAttribute('mozapp', data.app);
} else if (data.browser) {
iframe.setAttribute('mozbrowser', '');
}
if (data.app || data.browser) {
iframe.addEventListener('load', function(e) {
var principal = iframe.contentDocument.nodePrincipal;
switch (data.action) {
case 'read-no':
is(permManager.testPermissionFromPrincipal(principal, gPermName),
Ci.nsIPermissionManager.UNKNOWN_ACTION,
"Permission should not be set yet");
is(permManager.testExactPermissionFromPrincipal(principal, gPermName),
Ci.nsIPermissionManager.UNKNOWN_ACTION,
"Permission should not be set yet");
break;
case 'write':
permManager.addFromPrincipal(principal, gPermName, Ci.nsIPermissionManager.ALLOW_ACTION);
break;
case 'read-yes':
is(permManager.testPermissionFromPrincipal(principal, gPermName),
Ci.nsIPermissionManager.ALLOW_ACTION,
"Permission should be set");
is(permManager.testExactPermissionFromPrincipal(principal, gPermName),
Ci.nsIPermissionManager.ALLOW_ACTION,
"Permission should be set");
break;
default:
ok(false, "shouldn't be there");
}
// Calling removeChild() produces an error that creates failures.
//document.getElementById('content').removeChild(iframe);
i++;
if (i >= gData.length) {
if (previousPrefs.mozBrowserFramesEnabled !== undefined) {
SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
}
SimpleTest.finish();
} else {
gTestRunner.next();
}
});
}
iframe.src = data.src;
document.getElementById('content').appendChild(iframe);
yield;
}
}
var gTestRunner = runTest();
gTestRunner.next();
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,52 @@
/* 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/. */
load(libdir + "eqArrayHelper.js");
function assertEqParallelArray(a, b) {
assertEq(a instanceof ParallelArray, true);
assertEq(b instanceof ParallelArray, true);
var shape = a.shape;
assertEqArray(shape, b.shape);
function bump(indices) {
var d = indices.length - 1;
while (d >= 0) {
if (++indices[d] < shape[d])
break;
indices[d] = 0;
d--;
}
return d >= 0;
}
var iv = shape.map(function () { return 0; });
do {
var e1 = a.get(iv);
var e2 = b.get(iv);
if (e1 instanceof ParallelArray && e2 instanceof ParallelArray)
assertEqParallelArray(e1, e2);
else if (e1 instanceof Array && e2 instanceof Array)
assertEqArray(e1, e2);
else
assertEq(e1, e2);
} while (bump(iv));
}
function assertParallelArrayModesCommute(modes, pa, op) {
var args = Array.slice(arguments, 3);
var acc;
modes.forEach(function (mode) {
var result = op.apply(pa, args.concat([{ mode: mode, expect: "success" }]));
if (acc) {
if (acc instanceof ParallelArray)
assertEqParallelArray(acc, result);
else
assertEq(acc, result);
} else {
acc = result;
}
});
}

View File

@ -1,9 +1,10 @@
load(libdir + "parallelarray-helpers.js")
function buildComprehension() {
// 1D comprehension
// 1D comprehension
var p = new ParallelArray(10, function (idx) { return idx; });
var a = [0,1,2,3,4,5,6,7,8,9];
assertEq(p.toString(), "<" + a.join(",") + ">");
var a = new ParallelArray([0,1,2,3,4,5,6,7,8,9]);
assertEqParallelArray(p, a);
}
buildComprehension();

View File

@ -1,9 +1,12 @@
load(libdir + "parallelarray-helpers.js");
load(libdir + "eqArrayHelper.js");
function buildMultidim() {
// 2D comprehension
var p = new ParallelArray([2,2], function (i,j) { return i + j; });
assertEq(p.shape.toString(), [2,2].toString());
assertEq(p.toString(), "<<0,1>,<1,2>>");
var a = new ParallelArray([0,1,1,2]).partition(2);
assertEqArray(p.shape, [2,2]);
assertEqParallelArray(p, a);
}
buildMultidim();

View File

@ -1,3 +1,4 @@
load(libdir + "parallelarray-helpers.js");
function buildComprehension() {
var H = 96;
@ -5,27 +6,16 @@ function buildComprehension() {
var d = 4;
// 3D 96x96x4 texture-like PA
var p = new ParallelArray([H,W,d], function (i,j,k) { return i + j + k; });
var a = "<";
var a = [];
for (var i = 0; i < H; i++) {
a += "<";
for (var j = 0; j < W; j++) {
a += "<";
for (var k = 0; k < d; k++) {
a += i+j+k;
if (k !== d - 1)
a += ",";
a.push(i+j+k);
}
a += ">";
if (j !== W - 1)
a += ","
}
a += ">";
if (i !== H - 1)
a += ","
}
a += ">"
assertEq(p.toString(), a);
var p2 = new ParallelArray(a).partition(d).partition(H);
assertEqParallelArray(p, p2);
}
buildComprehension();

View File

@ -1,16 +1,14 @@
function bracket(s) {
return "<" + s + ">";
}
load(libdir + "eqArrayHelper.js");
function buildSimple() {
// Simple constructor
var a = [1,2,3,4,5];
var p = new ParallelArray(a);
var e = a.join(",");
assertEq(p.toString(), bracket(e));
assertEqArray(p, a);
var a2 = a.slice();
a[0] = 9;
// No sharing
assertEq(p.toString(), bracket(e));
assertEqArray(p, a2);
}
buildSimple();

View File

@ -1,9 +1,10 @@
load(libdir + "parallelarray-helpers.js");
function buildPA() {
// Construct copying from PA
var p1 = new ParallelArray([1,2,3,4]);
var p2 = new ParallelArray(p1);
assertEq(p1.toString(), p2.toString());
assertEqParallelArray(p1, p2);
var p1d = new ParallelArray([2,2], function(i,j) { return i + j; });
var p2d = new ParallelArray(p1d);
assertEq(p1d.toString(), p2d.toString());

View File

@ -1,7 +1,10 @@
load(libdir + "parallelarray-helpers.js");
function testElement() {
// Test getting element from higher dimension
var p = new ParallelArray([2,2,2], function () { return 0; });
assertEq(p[0].toString(), "<<0,0>,<0,0>>");
var p0 = new ParallelArray([2,2], function () { return 0; });
assertEqParallelArray(p[0], p0);
// Should create new wrapper
assertEq(p[0] !== p[0], true);
// Test out of bounds

View File

@ -1,13 +1,14 @@
load(libdir + "parallelarray-helpers.js");
function testFilterAll() {
// Test filtering everything (leaving everything in)
var p = new ParallelArray([0,1,2,3,4]);
var all = p.map(function (i) { return true; });
var r = p.filter(all);
assertEq(r.toString(), "<0,1,2,3,4>");
assertEqParallelArray(r, p);
var p = new ParallelArray([5,2], function(i,j) { return i+j; });
var r = p.filter(all);
assertEq(r.toString(), "<<0,1>,<1,2>,<2,3>,<3,4>,<4,5>>");
assertEqParallelArray(r, new ParallelArray(p));
}
testFilterAll();

View File

@ -1,12 +1,14 @@
load(libdir + "parallelarray-helpers.js");
function testFilterNone() {
// Test filtering (removing everything)
var p = new ParallelArray([0,1,2,3,4]);
var none = p.map(function () { return false; });
var r = p.filter(none);
assertEq(r.toString(), "<>");
assertEqParallelArray(r, new ParallelArray);
var p = new ParallelArray([5,2], function(i,j) { return i+j; });
var r = p.filter(none);
assertEq(r.toString(), "<>");
assertEqParallelArray(r, new ParallelArray);
}
testFilterNone();

View File

@ -1,16 +1,14 @@
function bracket(s) {
return "<" + s + ">";
}
load(libdir + "parallelarray-helpers.js");
function testFilterSome() {
var p = new ParallelArray([0,1,2,3,4]);
var evenBelowThree = p.map(function (i) { return ((i%2) === 0) && (i < 3); });
var r = p.filter(evenBelowThree);
assertEq(r.toString(), bracket([0,2].join(",")));
assertEqParallelArray(r, new ParallelArray([0,2]));
var p = new ParallelArray([5,2], function (i,j) { return i; });
var evenBelowThree = p.map(function (i) { return ((i[0]%2) === 0) && (i[0] < 3); });
var r = p.filter(evenBelowThree);
assertEq(r.toString(), bracket(["<0,0>","<2,2>"].join(",")));
assertEqParallelArray(r, new ParallelArray([p[0], p[2]]));
}
testFilterSome();

View File

@ -1,18 +1,16 @@
function bracket(s) {
return "<" + s + ">";
}
load(libdir + "parallelarray-helpers.js");
function testFilterMisc() {
var p = new ParallelArray([0,1,2]);
// Test array
var r = p.filter([true, false, true]);
assertEq(r.toString(), bracket([0,2].join(",")));
assertEqParallelArray(r, new ParallelArray([p[0], p[2]]));
// Test array-like
var r = p.filter({ 0: true, 1: false, 2: true, length: 3 });
assertEq(r.toString(), bracket([0,2].join(",")));
assertEqParallelArray(r, new ParallelArray([p[0], p[2]]));
// Test truthy
var r = p.filter([1, "", {}]);
assertEq(r.toString(), bracket([0,2].join(",")));
assertEqParallelArray(r, new ParallelArray([p[0], p[2]]));
}
testFilterMisc();

View File

@ -1,10 +1,12 @@
load(libdir + "eqArrayHelper.js");
function testFlatten() {
var shape = [5];
for (var i = 0; i < 7; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function(i,j) { return i+j; });
var flatShape = ([shape[0] * shape[1]]).concat(shape.slice(2));
assertEq(p.flatten().shape.toString(), flatShape.toString());
assertEqArray(p.flatten().shape, flatShape);
}
}

View File

@ -1,7 +1,9 @@
load(libdir + "parallelarray-helpers.js");
function testFlatten() {
var p = new ParallelArray([2,2], function(i,j) { return i+j; });
var p2 = new ParallelArray([0,1,1,2]);
assertEq(p.flatten().toString(), p2.toString());
assertEqParallelArray(p.flatten(), p2);
}
testFlatten();

View File

@ -1,10 +1,12 @@
load(libdir + "parallelarray-helpers.js");
function testGet() {
var p = new ParallelArray([2,2,2], function(i,j,k) { return i+j+k; });
assertEq(p.get([1,1,1]), 1+1+1);
var p2 = new ParallelArray([2], function(i) { return 1+1+i; });
assertEq(p.get([1,1]).toString(), p2.toString());
assertEqParallelArray(p.get([1,1]), p2);
var p3 = new ParallelArray([2,2], function(i,j) { return 1+i+j; });
assertEq(p.get([1]).toString(), p3.toString());
assertEqParallelArray(p.get([1]), p3);
}
testGet();

View File

@ -1,11 +1,10 @@
function bracket(s) {
return "<" + s + ">";
}
load(libdir + "parallelarray-helpers.js");
function testMap() {
var p = new ParallelArray([0,1,2,3,4]);
var m = p.map(function (v) { return v+1; });
assertEq(m.toString(), bracket([1,2,3,4,5].join(",")));
var p = new ParallelArray([0,1,2,3,4]);
var m = p.map(function (v) { return v+1; });
var p2 = new ParallelArray([1,2,3,4,5]);
assertEqParallelArray(m, p2);
}
testMap();

View File

@ -1,8 +1,11 @@
load(libdir + "parallelarray-helpers.js");
function testMap() {
// Test mapping higher dimensional
var p = new ParallelArray([2,2], function (i,j) { return i+j; });
var m = p.map(function(x) { return x.toString(); });
assertEq(m.toString(), "<<0,1>,<1,2>>");
var m = p.map(function(x) { return x; });
var p2 = new ParallelArray(p);
assertEqParallelArray(m, p2);
}
testMap();

View File

@ -1,12 +1,14 @@
load(libdir + "eqArrayHelper.js");
function testPartition() {
var p = new ParallelArray([1,2,3,4,5,6,7,8]);
var pp = p.partition(2);
var ppp = pp.partition(2);
var ppShape = [p.shape[0] / 2, 2].concat(p.shape.slice(1));
var pppShape = [pp.shape[0] / 2, 2].concat(pp.shape.slice(1));
assertEq(pp.shape.toString(), ppShape.toString())
assertEqArray(pp.shape, ppShape);
assertEq(pp.toString(), "<<1,2>,<3,4>,<5,6>,<7,8>>");
assertEq(ppp.shape.toString(), pppShape.toString())
assertEqArray(ppp.shape, pppShape);
assertEq(ppp.toString(), "<<<1,2>,<3,4>>,<<5,6>,<7,8>>>");
}

View File

@ -10,7 +10,7 @@ function testScan() {
var r = p.reduce(f);
var s = p.scan(f)
for (var j = 0; j < s.length; j++)
assertEq(s[0].shape.length, i + 1);
assertEq(s[j].shape.length, i + 1);
assertEq(r.shape.length, i + 1);
}
}

View File

@ -1,8 +1,9 @@
load(libdir + "parallelarray-helpers.js");
function testScatterIdentity() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,1,2,3,4]);
assertEq(p.toString(), r.toString());
assertEqParallelArray(p, r);
}
testScatterIdentity();

View File

@ -1,9 +1,10 @@
load(libdir + "parallelarray-helpers.js");
function testScatter() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([1,0,3,2,4]);
var p2 = new ParallelArray([2,1,4,3,5]);
assertEq(r.toString(), p2.toString());
assertEqParallelArray(r, p2);
}
testScatter();

View File

@ -1,11 +1,9 @@
function bracket(s) {
return "<" + s + ">";
}
load(libdir + "parallelarray-helpers.js");
function testScatterDefault() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,2,4], 9);
assertEq(r.toString(), bracket([1,9,2,9,3].join(",")));
assertEqParallelArray(r, new ParallelArray([1,9,2,9,3]));
}
testScatterDefault();

View File

@ -1,11 +1,9 @@
function bracket(s) {
return "<" + s + ">";
}
load(libdir + "parallelarray-helpers.js");
function testScatterConflict() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; });
assertEq(r.toString(), bracket([4,2,9,4,5].join(",")));
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; });
assertEqParallelArray(r, new ParallelArray([4,2,9,4,5]));
}
testScatterConflict();

View File

@ -1,3 +1,4 @@
load(libdir + "parallelarray-helpers.js");
function testScatterIdentity() {
var shape = [5];
@ -5,7 +6,8 @@ function testScatterIdentity() {
shape.push(i+1);
var p = new ParallelArray(shape, function(k) { return k; });
var r = p.scatter([0,1,2,3,4]);
assertEq(p.toString(), r.toString());
var p2 = new ParallelArray([p[0], p[1], p[2], p[3], p[4]]);
assertEqParallelArray(p2, r);
}
}

View File

@ -1,3 +1,4 @@
load(libdir + "parallelarray-helpers.js");
function testScatter() {
var shape = [5];
@ -6,7 +7,7 @@ function testScatter() {
var p = new ParallelArray(shape, function(k) { return k; });
var r = p.scatter([1,0,3,2,4]);
var p2 = new ParallelArray([p[1], p[0], p[3], p[2], p[4]]);
assertEq(p2.toString(), r.toString());
assertEqParallelArray(p2, r);
}
}

View File

@ -1,7 +1,9 @@
load(libdir + "parallelarray-helpers.js");
function testScatter() {
// Ignore the rest of the scatter vector if longer than source
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([1,0,3,2,4,1,2,3]);
var p2 = new ParallelArray([2,1,4,3,5]);
assertEq(r.toString(), p2.toString());
assertEqParallelArray(r, p2);
}

View File

@ -1,3 +1,5 @@
load(libdir + "eqArrayHelper.js");
function testShape() {
// Test higher dimension shape up to 8D
var shape = [];
@ -7,7 +9,7 @@ function testShape() {
// Test shape identity and shape
assertEq(p.shape, p.shape);
assertEq(p.shape !== shape, true);
assertEq(p.shape.toString(), shape.toString());
assertEqArray(p.shape, shape);
}
}

View File

@ -6,7 +6,7 @@
#include "nsISupports.idl"
[scriptable, uuid(5036f0f6-f77b-4168-9d57-a1c0dd66cf02)]
[scriptable, uuid(cfb08e46-193c-4be7-a467-d7775fb2a31e)]
/**
* This interface defines a "permission" object,
* used to specify allowed/blocked objects from
@ -20,6 +20,16 @@ interface nsIPermission : nsISupports
*/
readonly attribute AUTF8String host;
/**
* The id of the app for which the permission is set.
*/
readonly attribute unsigned long appId;
/**
* Whether the permission has been set to a page inside a browser element.
*/
readonly attribute boolean isInBrowserElement;
/**
* a case-sensitive ASCII string, indicating the type of permission
* (e.g., "cookie", "image", etc).

View File

@ -20,9 +20,13 @@ struct Permission
nsCString host, type;
uint32_t capability, expireType;
int64_t expireTime;
uint32_t appId;
bool isInBrowserElement;
Permission() { }
Permission(const nsCString& aHost,
const uint32_t aAppId,
const bool aIsInBrowserElement,
const nsCString& aType,
const uint32_t aCapability,
const uint32_t aExpireType,
@ -30,7 +34,10 @@ struct Permission
type(aType),
capability(aCapability),
expireType(aExpireType),
expireTime(aExpireTime) { }
expireTime(aExpireTime),
appId(aAppId),
isInBrowserElement(aIsInBrowserElement)
{}
};
template<>
@ -43,6 +50,8 @@ struct ParamTraits<Permission>
WriteParam(aMsg, aParam.capability);
WriteParam(aMsg, aParam.expireType);
WriteParam(aMsg, aParam.expireTime);
WriteParam(aMsg, aParam.appId);
WriteParam(aMsg, aParam.isInBrowserElement);
}
static bool Read(const Message* aMsg, void** aIter, Permission* aResult)
@ -51,12 +60,26 @@ struct ParamTraits<Permission>
ReadParam(aMsg, aIter, &aResult->type) &&
ReadParam(aMsg, aIter, &aResult->capability) &&
ReadParam(aMsg, aIter, &aResult->expireType) &&
ReadParam(aMsg, aIter, &aResult->expireTime);
ReadParam(aMsg, aIter, &aResult->expireTime) &&
ReadParam(aMsg, aIter, &aResult->appId) &&
ReadParam(aMsg, aIter, &aResult->isInBrowserElement);
}
static void Log(const Permission& aParam, std::wstring* aLog)
static void Log(const Permission& p, std::wstring* l)
{
aLog->append(StringPrintf(L"[%s]", aParam.host.get()));
l->append(L"(");
LogParam(p.host, l);
l->append(L", ");
LogParam(p.appId, l);
l->append(L", ");
LogParam(p.isInBrowserElement, l);
l->append(L", ");
LogParam(p.capability, l);
l->append(L", ");
LogParam(p.expireTime, l);
l->append(L", ");
LogParam(p.expireType, l);
l->append(L")");
}
};

View File

@ -231,6 +231,9 @@ class Permissions(object):
# Open database and create table
permDB = sqlite3.connect(os.path.join(self._profileDir, "permissions.sqlite"))
cursor = permDB.cursor();
cursor.execute("PRAGMA user_version=3");
# SQL copied from
# http://mxr.mozilla.org/mozilla-central/source/extensions/cookie/nsPermissionManager.cpp
cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts (
@ -239,7 +242,9 @@ class Permissions(object):
type TEXT,
permission INTEGER,
expireType INTEGER,
expireTime INTEGER)""")
expireTime INTEGER,
appId INTEGER,
isInBrowserElement INTEGER)""")
for location in locations:
# set the permissions
@ -250,7 +255,7 @@ class Permissions(object):
permission_type = 1
else:
permission_type = 2
cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)",
cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0, 0, 0)",
(self._num_permissions, location.host, perm,
permission_type))

View File

@ -165,7 +165,7 @@ endif
#
# OpenBSD/ARM
#
ifneq (,$(filter OpenBSDarmish OpenBSDzaurus,$(OS_ARCH)$(OS_TEST)))
ifeq ($(OS_ARCH)$(OS_TEST),OpenBSDarm)
CPPSRCS := xptcinvoke_arm_openbsd.cpp xptcstubs_arm_openbsd.cpp
endif