2017-01-10 01:40:57 +09:00
|
|
|
/* Copyright 2017 Mozilla Foundation
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
2014-12-27 01:43:13 +09:00
|
|
|
|
2017-04-17 05:30:27 +09:00
|
|
|
import {
|
2018-05-15 12:10:32 +09:00
|
|
|
backtrackBeforeAllVisibleElements, binarySearchFirstItem, EventBus,
|
|
|
|
getPageSizeInches, getPDFFileNameFromURL, getVisibleElements,
|
|
|
|
isPortraitOrientation, isValidRotation, moveToEndOfArray,
|
|
|
|
waitOnEventOrTimeout, WaitOnType
|
2017-04-17 05:30:27 +09:00
|
|
|
} from '../../web/ui_utils';
|
2018-01-06 21:10:58 +09:00
|
|
|
import { createObjectURL } from '../../src/shared/util';
|
2019-11-10 23:10:18 +09:00
|
|
|
import { isNodeJS } from '../../src/shared/is_node';
|
2017-01-10 01:40:57 +09:00
|
|
|
|
2014-12-27 01:43:13 +09:00
|
|
|
describe('ui_utils', function() {
|
|
|
|
describe('binary search', function() {
|
|
|
|
function isTrue(boolean) {
|
|
|
|
return boolean;
|
|
|
|
}
|
|
|
|
function isGreater3(number) {
|
|
|
|
return number > 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
it('empty array', function() {
|
|
|
|
expect(binarySearchFirstItem([], isTrue)).toEqual(0);
|
|
|
|
});
|
|
|
|
it('single boolean entry', function() {
|
|
|
|
expect(binarySearchFirstItem([false], isTrue)).toEqual(1);
|
|
|
|
expect(binarySearchFirstItem([true], isTrue)).toEqual(0);
|
|
|
|
});
|
|
|
|
it('three boolean entries', function() {
|
|
|
|
expect(binarySearchFirstItem([true, true, true], isTrue)).toEqual(0);
|
|
|
|
expect(binarySearchFirstItem([false, true, true], isTrue)).toEqual(1);
|
|
|
|
expect(binarySearchFirstItem([false, false, true], isTrue)).toEqual(2);
|
|
|
|
expect(binarySearchFirstItem([false, false, false], isTrue)).toEqual(3);
|
|
|
|
});
|
|
|
|
it('three numeric entries', function() {
|
|
|
|
expect(binarySearchFirstItem([0, 1, 2], isGreater3)).toEqual(3);
|
|
|
|
expect(binarySearchFirstItem([2, 3, 4], isGreater3)).toEqual(2);
|
|
|
|
expect(binarySearchFirstItem([4, 5, 6], isGreater3)).toEqual(0);
|
|
|
|
});
|
|
|
|
});
|
2016-04-26 07:57:15 +09:00
|
|
|
|
2017-04-16 03:37:06 +09:00
|
|
|
describe('getPDFFileNameFromURL', function() {
|
|
|
|
it('gets PDF filename', function() {
|
|
|
|
// Relative URL
|
|
|
|
expect(getPDFFileNameFromURL('/pdfs/file1.pdf')).toEqual('file1.pdf');
|
|
|
|
// Absolute URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
'http://www.example.com/pdfs/file2.pdf')).toEqual('file2.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets fallback filename', function() {
|
|
|
|
// Relative URL
|
|
|
|
expect(getPDFFileNameFromURL('/pdfs/file1.txt')).toEqual('document.pdf');
|
|
|
|
// Absolute URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
'http://www.example.com/pdfs/file2.txt')).toEqual('document.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets custom fallback filename', function() {
|
|
|
|
// Relative URL
|
|
|
|
expect(getPDFFileNameFromURL('/pdfs/file1.txt', 'qwerty1.pdf')).
|
|
|
|
toEqual('qwerty1.pdf');
|
|
|
|
// Absolute URL
|
|
|
|
expect(getPDFFileNameFromURL('http://www.example.com/pdfs/file2.txt',
|
|
|
|
'qwerty2.pdf')).toEqual('qwerty2.pdf');
|
|
|
|
|
|
|
|
// An empty string should be a valid custom fallback filename.
|
|
|
|
expect(getPDFFileNameFromURL('/pdfs/file3.txt', '')).toEqual('');
|
|
|
|
});
|
|
|
|
|
2018-09-30 19:22:50 +09:00
|
|
|
it('gets fallback filename when url is not a string', function() {
|
|
|
|
expect(getPDFFileNameFromURL(null)).toEqual('document.pdf');
|
|
|
|
|
|
|
|
expect(getPDFFileNameFromURL(null, 'file.pdf')).toEqual('file.pdf');
|
|
|
|
});
|
|
|
|
|
2017-04-16 03:37:06 +09:00
|
|
|
it('gets PDF filename from URL containing leading/trailing whitespace',
|
|
|
|
function() {
|
|
|
|
// Relative URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
' /pdfs/file1.pdf ')).toEqual('file1.pdf');
|
|
|
|
// Absolute URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
' http://www.example.com/pdfs/file2.pdf ')).toEqual('file2.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets PDF filename from query string', function() {
|
|
|
|
// Relative URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
'/pdfs/pdfs.html?name=file1.pdf')).toEqual('file1.pdf');
|
|
|
|
// Absolute URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
'http://www.example.com/pdfs/pdf.html?file2.pdf')).toEqual('file2.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets PDF filename from hash string', function() {
|
|
|
|
// Relative URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
'/pdfs/pdfs.html#name=file1.pdf')).toEqual('file1.pdf');
|
|
|
|
// Absolute URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
'http://www.example.com/pdfs/pdf.html#file2.pdf')).toEqual('file2.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets correct PDF filename when multiple ones are present', function() {
|
|
|
|
// Relative URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
'/pdfs/file1.pdf?name=file.pdf')).toEqual('file1.pdf');
|
|
|
|
// Absolute URL
|
|
|
|
expect(getPDFFileNameFromURL(
|
|
|
|
'http://www.example.com/pdfs/file2.pdf#file.pdf')).toEqual('file2.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets PDF filename from URI-encoded data', function() {
|
|
|
|
var encodedUrl = encodeURIComponent(
|
|
|
|
'http://www.example.com/pdfs/file1.pdf');
|
|
|
|
expect(getPDFFileNameFromURL(encodedUrl)).toEqual('file1.pdf');
|
|
|
|
|
|
|
|
var encodedUrlWithQuery = encodeURIComponent(
|
|
|
|
'http://www.example.com/pdfs/file.txt?file2.pdf');
|
|
|
|
expect(getPDFFileNameFromURL(encodedUrlWithQuery)).toEqual('file2.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets PDF filename from data mistaken for URI-encoded', function() {
|
|
|
|
expect(getPDFFileNameFromURL('/pdfs/%AA.pdf')).toEqual('%AA.pdf');
|
|
|
|
|
|
|
|
expect(getPDFFileNameFromURL('/pdfs/%2F.pdf')).toEqual('%2F.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets PDF filename from (some) standard protocols', function() {
|
|
|
|
// HTTP
|
|
|
|
expect(getPDFFileNameFromURL('http://www.example.com/file1.pdf')).
|
|
|
|
toEqual('file1.pdf');
|
|
|
|
// HTTPS
|
|
|
|
expect(getPDFFileNameFromURL('https://www.example.com/file2.pdf')).
|
|
|
|
toEqual('file2.pdf');
|
|
|
|
// File
|
|
|
|
expect(getPDFFileNameFromURL('file:///path/to/files/file3.pdf')).
|
|
|
|
toEqual('file3.pdf');
|
|
|
|
// FTP
|
|
|
|
expect(getPDFFileNameFromURL('ftp://www.example.com/file4.pdf')).
|
|
|
|
toEqual('file4.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets PDF filename from query string appended to "blob:" URL',
|
|
|
|
function() {
|
2019-11-11 00:42:46 +09:00
|
|
|
if (isNodeJS) {
|
2017-04-25 05:07:00 +09:00
|
|
|
pending('Blob in not supported in Node.js.');
|
|
|
|
}
|
2017-04-16 03:37:06 +09:00
|
|
|
var typedArray = new Uint8Array([1, 2, 3, 4, 5]);
|
|
|
|
var blobUrl = createObjectURL(typedArray, 'application/pdf');
|
|
|
|
// Sanity check to ensure that a "blob:" URL was returned.
|
2019-01-18 23:05:23 +09:00
|
|
|
expect(blobUrl.startsWith('blob:')).toEqual(true);
|
2017-04-16 03:37:06 +09:00
|
|
|
|
|
|
|
expect(getPDFFileNameFromURL(blobUrl + '?file.pdf')).toEqual('file.pdf');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets fallback filename from query string appended to "data:" URL',
|
|
|
|
function() {
|
|
|
|
var typedArray = new Uint8Array([1, 2, 3, 4, 5]);
|
|
|
|
var dataUrl = createObjectURL(typedArray, 'application/pdf',
|
|
|
|
/* forceDataSchema = */ true);
|
|
|
|
// Sanity check to ensure that a "data:" URL was returned.
|
2019-01-18 23:05:23 +09:00
|
|
|
expect(dataUrl.startsWith('data:')).toEqual(true);
|
2017-04-16 03:37:06 +09:00
|
|
|
|
|
|
|
expect(getPDFFileNameFromURL(dataUrl + '?file1.pdf')).
|
|
|
|
toEqual('document.pdf');
|
|
|
|
|
|
|
|
// Should correctly detect a "data:" URL with leading whitespace.
|
|
|
|
expect(getPDFFileNameFromURL(' ' + dataUrl + '?file2.pdf')).
|
|
|
|
toEqual('document.pdf');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-26 07:57:15 +09:00
|
|
|
describe('EventBus', function () {
|
|
|
|
it('dispatch event', function () {
|
|
|
|
var eventBus = new EventBus();
|
|
|
|
var count = 0;
|
2018-09-21 21:12:01 +09:00
|
|
|
eventBus.on('test', function(evt) {
|
|
|
|
expect(evt).toEqual(undefined);
|
2016-04-26 07:57:15 +09:00
|
|
|
count++;
|
|
|
|
});
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
expect(count).toEqual(1);
|
|
|
|
});
|
2018-09-21 21:12:01 +09:00
|
|
|
it('dispatch event with arguments', function() {
|
|
|
|
const eventBus = new EventBus();
|
|
|
|
let count = 0;
|
|
|
|
eventBus.on('test', function(evt) {
|
|
|
|
expect(evt).toEqual({ abc: 123, });
|
|
|
|
count++;
|
|
|
|
});
|
|
|
|
eventBus.dispatch('test', {
|
|
|
|
abc: 123,
|
|
|
|
});
|
|
|
|
expect(count).toEqual(1);
|
|
|
|
});
|
2016-04-26 07:57:15 +09:00
|
|
|
it('dispatch different event', function () {
|
|
|
|
var eventBus = new EventBus();
|
|
|
|
var count = 0;
|
|
|
|
eventBus.on('test', function () {
|
|
|
|
count++;
|
|
|
|
});
|
|
|
|
eventBus.dispatch('nottest');
|
|
|
|
expect(count).toEqual(0);
|
|
|
|
});
|
|
|
|
it('dispatch event multiple times', function () {
|
|
|
|
var eventBus = new EventBus();
|
|
|
|
var count = 0;
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
eventBus.on('test', function () {
|
|
|
|
count++;
|
|
|
|
});
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
expect(count).toEqual(2);
|
|
|
|
});
|
|
|
|
it('dispatch event to multiple handlers', function () {
|
|
|
|
var eventBus = new EventBus();
|
|
|
|
var count = 0;
|
|
|
|
eventBus.on('test', function () {
|
|
|
|
count++;
|
|
|
|
});
|
|
|
|
eventBus.on('test', function () {
|
|
|
|
count++;
|
|
|
|
});
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
expect(count).toEqual(2);
|
|
|
|
});
|
|
|
|
it('dispatch to detached', function () {
|
|
|
|
var eventBus = new EventBus();
|
|
|
|
var count = 0;
|
|
|
|
var listener = function () {
|
|
|
|
count++;
|
|
|
|
};
|
|
|
|
eventBus.on('test', listener);
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
eventBus.off('test', listener);
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
expect(count).toEqual(1);
|
|
|
|
});
|
|
|
|
it('dispatch to wrong detached', function () {
|
|
|
|
var eventBus = new EventBus();
|
|
|
|
var count = 0;
|
|
|
|
eventBus.on('test', function () {
|
|
|
|
count++;
|
|
|
|
});
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
eventBus.off('test', function () {
|
|
|
|
count++;
|
|
|
|
});
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
expect(count).toEqual(2);
|
|
|
|
});
|
|
|
|
it('dispatch to detached during handling', function () {
|
|
|
|
var eventBus = new EventBus();
|
|
|
|
var count = 0;
|
|
|
|
var listener1 = function () {
|
|
|
|
eventBus.off('test', listener2);
|
|
|
|
count++;
|
|
|
|
};
|
|
|
|
var listener2 = function () {
|
|
|
|
eventBus.off('test', listener1);
|
|
|
|
count++;
|
|
|
|
};
|
|
|
|
eventBus.on('test', listener1);
|
|
|
|
eventBus.on('test', listener2);
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
eventBus.dispatch('test');
|
|
|
|
expect(count).toEqual(2);
|
|
|
|
});
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
|
|
|
|
it('should not, by default, re-dispatch to DOM', function(done) {
|
2019-11-11 00:42:46 +09:00
|
|
|
if (isNodeJS) {
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
pending('Document in not supported in Node.js.');
|
|
|
|
}
|
|
|
|
const eventBus = new EventBus();
|
|
|
|
let count = 0;
|
2018-09-21 21:12:01 +09:00
|
|
|
eventBus.on('test', function(evt) {
|
|
|
|
expect(evt).toEqual(undefined);
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
count++;
|
|
|
|
});
|
2018-09-21 06:08:38 +09:00
|
|
|
function domEventListener() {
|
|
|
|
done.fail('shall not dispatch DOM event.');
|
|
|
|
}
|
|
|
|
document.addEventListener('test', domEventListener);
|
|
|
|
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
eventBus.dispatch('test');
|
|
|
|
|
|
|
|
Promise.resolve().then(() => {
|
|
|
|
expect(count).toEqual(1);
|
2018-09-21 06:08:38 +09:00
|
|
|
|
|
|
|
document.removeEventListener('test', domEventListener);
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
it('should re-dispatch to DOM', function(done) {
|
2019-11-11 00:42:46 +09:00
|
|
|
if (isNodeJS) {
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
pending('Document in not supported in Node.js.');
|
|
|
|
}
|
|
|
|
const eventBus = new EventBus({ dispatchToDOM: true, });
|
|
|
|
let count = 0;
|
2018-09-21 21:12:01 +09:00
|
|
|
eventBus.on('test', function(evt) {
|
|
|
|
expect(evt).toEqual(undefined);
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
count++;
|
|
|
|
});
|
2018-09-21 21:12:01 +09:00
|
|
|
function domEventListener(evt) {
|
|
|
|
expect(evt.detail).toEqual({});
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
count++;
|
2018-09-21 06:08:38 +09:00
|
|
|
}
|
|
|
|
document.addEventListener('test', domEventListener);
|
|
|
|
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
eventBus.dispatch('test');
|
|
|
|
|
|
|
|
Promise.resolve().then(() => {
|
|
|
|
expect(count).toEqual(2);
|
2018-09-21 06:08:38 +09:00
|
|
|
|
2018-09-21 21:12:01 +09:00
|
|
|
document.removeEventListener('test', domEventListener);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
it('should re-dispatch to DOM, with arguments (without internal listeners)',
|
|
|
|
function(done) {
|
2019-11-11 00:42:46 +09:00
|
|
|
if (isNodeJS) {
|
2018-09-21 21:12:01 +09:00
|
|
|
pending('Document in not supported in Node.js.');
|
|
|
|
}
|
|
|
|
const eventBus = new EventBus({ dispatchToDOM: true, });
|
|
|
|
let count = 0;
|
|
|
|
function domEventListener(evt) {
|
|
|
|
expect(evt.detail).toEqual({ abc: 123, });
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
document.addEventListener('test', domEventListener);
|
|
|
|
|
|
|
|
eventBus.dispatch('test', {
|
|
|
|
abc: 123,
|
|
|
|
});
|
|
|
|
|
|
|
|
Promise.resolve().then(() => {
|
|
|
|
expect(count).toEqual(1);
|
|
|
|
|
2018-09-21 06:08:38 +09:00
|
|
|
document.removeEventListener('test', domEventListener);
|
Add general support for re-dispatching events, on `EventBus` instances, to the DOM
This patch is the first step to be able to eventually get rid of the `attachDOMEventsToEventBus` function, by allowing `EventBus` instances to simply re-dispatch most[1] events to the DOM.
Note that the re-dispatching is purposely implemented to occur *after* all registered `EventBus` listeners have been serviced, to prevent the ordering issues that necessitated the duplicated page/scale-change events.
The DOM events are currently necessary for the `mozilla-central` tests, see https://hg.mozilla.org/mozilla-central/file/tip/browser/extensions/pdfjs/test, and perhaps also for custom deployments of the PDF.js default viewer.
Once this have landed, and been successfully uplifted to `mozilla-central`, I intent to submit a patch to update the test-code to utilize the new preference. This will thus, eventually, make it possible to remove the `attachDOMEventsToEventBus` functionality.
*Please note:* I've successfully ran all `mozilla-central` tests locally, with these patches applied.
---
[1] The exception being events that originated on the `window` or `document`, since those are already globally available anyway.
2018-08-28 17:26:18 +09:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2016-04-26 07:57:15 +09:00
|
|
|
});
|
2017-08-12 23:06:20 +09:00
|
|
|
|
2017-08-19 21:23:40 +09:00
|
|
|
describe('isValidRotation', function() {
|
|
|
|
it('should reject non-integer angles', function() {
|
|
|
|
expect(isValidRotation()).toEqual(false);
|
|
|
|
expect(isValidRotation(null)).toEqual(false);
|
|
|
|
expect(isValidRotation(NaN)).toEqual(false);
|
|
|
|
expect(isValidRotation([90])).toEqual(false);
|
|
|
|
expect(isValidRotation('90')).toEqual(false);
|
|
|
|
expect(isValidRotation(90.5)).toEqual(false);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should reject non-multiple of 90 degree angles', function() {
|
|
|
|
expect(isValidRotation(45)).toEqual(false);
|
|
|
|
expect(isValidRotation(-123)).toEqual(false);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should accept valid angles', function() {
|
|
|
|
expect(isValidRotation(0)).toEqual(true);
|
|
|
|
expect(isValidRotation(90)).toEqual(true);
|
|
|
|
expect(isValidRotation(-270)).toEqual(true);
|
|
|
|
expect(isValidRotation(540)).toEqual(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-03-20 21:25:13 +09:00
|
|
|
describe('isPortraitOrientation', function() {
|
|
|
|
it('should be portrait orientation', function() {
|
|
|
|
expect(isPortraitOrientation({
|
|
|
|
width: 200,
|
|
|
|
height: 400,
|
|
|
|
})).toEqual(true);
|
|
|
|
|
|
|
|
expect(isPortraitOrientation({
|
|
|
|
width: 500,
|
|
|
|
height: 500,
|
|
|
|
})).toEqual(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be landscape orientation', function() {
|
|
|
|
expect(isPortraitOrientation({
|
|
|
|
width: 600,
|
|
|
|
height: 300,
|
|
|
|
})).toEqual(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-08-12 23:06:20 +09:00
|
|
|
describe('waitOnEventOrTimeout', function() {
|
|
|
|
let eventBus;
|
|
|
|
|
|
|
|
beforeAll(function(done) {
|
|
|
|
eventBus = new EventBus();
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
afterAll(function() {
|
|
|
|
eventBus = null;
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should reject invalid parameters', function(done) {
|
|
|
|
let invalidTarget = waitOnEventOrTimeout({
|
|
|
|
target: 'window',
|
|
|
|
name: 'DOMContentLoaded',
|
|
|
|
}).then(function() {
|
|
|
|
throw new Error('Should reject invalid parameters.');
|
|
|
|
}, function(reason) {
|
|
|
|
expect(reason instanceof Error).toEqual(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
let invalidName = waitOnEventOrTimeout({
|
|
|
|
target: eventBus,
|
|
|
|
name: '',
|
|
|
|
}).then(function() {
|
|
|
|
throw new Error('Should reject invalid parameters.');
|
|
|
|
}, function(reason) {
|
|
|
|
expect(reason instanceof Error).toEqual(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
let invalidDelay = waitOnEventOrTimeout({
|
|
|
|
target: eventBus,
|
|
|
|
name: 'pagerendered',
|
|
|
|
delay: -1000,
|
|
|
|
}).then(function() {
|
|
|
|
throw new Error('Should reject invalid parameters.');
|
|
|
|
}, function(reason) {
|
|
|
|
expect(reason instanceof Error).toEqual(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
Promise.all([invalidTarget, invalidName, invalidDelay]).then(done,
|
|
|
|
done.fail);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should resolve on event, using the DOM', function(done) {
|
2019-11-11 00:42:46 +09:00
|
|
|
if (isNodeJS) {
|
2017-08-12 23:06:20 +09:00
|
|
|
pending('Document in not supported in Node.js.');
|
|
|
|
}
|
|
|
|
let button = document.createElement('button');
|
|
|
|
|
|
|
|
let buttonClicked = waitOnEventOrTimeout({
|
|
|
|
target: button,
|
|
|
|
name: 'click',
|
|
|
|
delay: 10000,
|
|
|
|
});
|
|
|
|
// Immediately dispatch the expected event.
|
|
|
|
button.click();
|
|
|
|
|
|
|
|
buttonClicked.then(function(type) {
|
|
|
|
expect(type).toEqual(WaitOnType.EVENT);
|
|
|
|
done();
|
|
|
|
}, done.fail);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should resolve on timeout, using the DOM', function(done) {
|
2019-11-11 00:42:46 +09:00
|
|
|
if (isNodeJS) {
|
2017-08-12 23:06:20 +09:00
|
|
|
pending('Document in not supported in Node.js.');
|
|
|
|
}
|
|
|
|
let button = document.createElement('button');
|
|
|
|
|
|
|
|
let buttonClicked = waitOnEventOrTimeout({
|
|
|
|
target: button,
|
|
|
|
name: 'click',
|
|
|
|
delay: 10,
|
|
|
|
});
|
|
|
|
// Do *not* dispatch the event, and wait for the timeout.
|
|
|
|
|
|
|
|
buttonClicked.then(function(type) {
|
|
|
|
expect(type).toEqual(WaitOnType.TIMEOUT);
|
|
|
|
done();
|
|
|
|
}, done.fail);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should resolve on event, using the EventBus', function(done) {
|
|
|
|
let pageRendered = waitOnEventOrTimeout({
|
|
|
|
target: eventBus,
|
|
|
|
name: 'pagerendered',
|
|
|
|
delay: 10000,
|
|
|
|
});
|
|
|
|
// Immediately dispatch the expected event.
|
|
|
|
eventBus.dispatch('pagerendered');
|
|
|
|
|
|
|
|
pageRendered.then(function(type) {
|
|
|
|
expect(type).toEqual(WaitOnType.EVENT);
|
|
|
|
done();
|
|
|
|
}, done.fail);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should resolve on timeout, using the EventBus', function(done) {
|
|
|
|
let pageRendered = waitOnEventOrTimeout({
|
|
|
|
target: eventBus,
|
|
|
|
name: 'pagerendered',
|
|
|
|
delay: 10,
|
|
|
|
});
|
|
|
|
// Do *not* dispatch the event, and wait for the timeout.
|
|
|
|
|
|
|
|
pageRendered.then(function(type) {
|
|
|
|
expect(type).toEqual(WaitOnType.TIMEOUT);
|
|
|
|
done();
|
|
|
|
}, done.fail);
|
|
|
|
});
|
|
|
|
});
|
2018-03-20 01:27:04 +09:00
|
|
|
|
|
|
|
describe('getPageSizeInches', function () {
|
|
|
|
it('gets page size (in inches)', function() {
|
|
|
|
const page = {
|
|
|
|
view: [0, 0, 595.28, 841.89],
|
|
|
|
userUnit: 1.0,
|
|
|
|
rotate: 0,
|
|
|
|
};
|
|
|
|
const { width, height, } = getPageSizeInches(page);
|
|
|
|
|
|
|
|
expect(+width.toPrecision(3)).toEqual(8.27);
|
|
|
|
expect(+height.toPrecision(4)).toEqual(11.69);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('gets page size (in inches), for non-default /Rotate entry', function() {
|
|
|
|
const pdfPage1 = { view: [0, 0, 612, 792], userUnit: 1, rotate: 0, };
|
|
|
|
const { width: width1, height: height1, } = getPageSizeInches(pdfPage1);
|
|
|
|
|
|
|
|
expect(width1).toEqual(8.5);
|
|
|
|
expect(height1).toEqual(11);
|
|
|
|
|
|
|
|
const pdfPage2 = { view: [0, 0, 612, 792], userUnit: 1, rotate: 90, };
|
|
|
|
const { width: width2, height: height2, } = getPageSizeInches(pdfPage2);
|
|
|
|
|
|
|
|
expect(width2).toEqual(11);
|
|
|
|
expect(height2).toEqual(8.5);
|
|
|
|
});
|
|
|
|
});
|
2018-05-15 12:10:32 +09:00
|
|
|
|
|
|
|
describe('getVisibleElements', function() {
|
|
|
|
// These values are based on margin/border values in the CSS, but there
|
|
|
|
// isn't any real need for them to be; they just need to take *some* value.
|
|
|
|
const BORDER_WIDTH = 9;
|
|
|
|
const SPACING = 2 * BORDER_WIDTH - 7;
|
|
|
|
|
|
|
|
// This is a helper function for assembling an array of view stubs from an
|
|
|
|
// array of arrays of [width, height] pairs, which represents wrapped lines
|
|
|
|
// of pages. It uses the above constants to add realistic spacing between
|
|
|
|
// the pages and the lines.
|
|
|
|
//
|
|
|
|
// If you're reading a test that calls makePages, you should think of the
|
|
|
|
// inputs to makePages as boxes with no borders, being laid out in a
|
|
|
|
// container that has no margins, so that the top of the tallest page in
|
|
|
|
// the first row will be at y = 0, and the left of the first page in
|
|
|
|
// the first row will be at x = 0. The spacing between pages in a row, and
|
|
|
|
// the spacing between rows, is SPACING. If you wanted to construct an
|
|
|
|
// actual HTML document with the same layout, you should give each page
|
|
|
|
// element a margin-right and margin-bottom of SPACING, and add no other
|
|
|
|
// margins, borders, or padding.
|
|
|
|
//
|
|
|
|
// If you're reading makePages itself, you'll see a somewhat more
|
|
|
|
// complicated picture because this suite of tests is exercising
|
|
|
|
// getVisibleElements' ability to account for the borders that real page
|
|
|
|
// elements have. makePages tests this by subtracting a BORDER_WIDTH from
|
|
|
|
// offsetLeft/Top and adding it to clientLeft/Top. So the element stubs that
|
|
|
|
// getVisibleElements sees may, for example, actually have an offsetTop of
|
|
|
|
// -9. If everything is working correctly, this detail won't leak out into
|
|
|
|
// the tests themselves, and so the tests shouldn't use the value of
|
|
|
|
// BORDER_WIDTH at all.
|
|
|
|
function makePages(lines) {
|
|
|
|
const result = [];
|
|
|
|
let lineTop = 0, id = 0;
|
|
|
|
for (const line of lines) {
|
|
|
|
const lineHeight = line.reduce(function(maxHeight, pair) {
|
|
|
|
return Math.max(maxHeight, pair[1]);
|
|
|
|
}, 0);
|
|
|
|
let offsetLeft = -BORDER_WIDTH;
|
|
|
|
for (const [clientWidth, clientHeight] of line) {
|
|
|
|
const offsetTop =
|
|
|
|
lineTop + (lineHeight - clientHeight) / 2 - BORDER_WIDTH;
|
|
|
|
const div = {
|
|
|
|
offsetLeft, offsetTop, clientWidth, clientHeight,
|
|
|
|
clientLeft: BORDER_WIDTH, clientTop: BORDER_WIDTH,
|
|
|
|
};
|
|
|
|
result.push({ id, div, });
|
|
|
|
++id;
|
|
|
|
offsetLeft += clientWidth + SPACING;
|
|
|
|
}
|
|
|
|
lineTop += lineHeight + SPACING;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a reimplementation of getVisibleElements without the
|
|
|
|
// optimizations.
|
|
|
|
function slowGetVisibleElements(scroll, pages) {
|
|
|
|
const views = [];
|
|
|
|
const { scrollLeft, scrollTop, } = scroll;
|
|
|
|
const scrollRight = scrollLeft + scroll.clientWidth;
|
|
|
|
const scrollBottom = scrollTop + scroll.clientHeight;
|
|
|
|
for (const view of pages) {
|
|
|
|
const { div, } = view;
|
|
|
|
const viewLeft = div.offsetLeft + div.clientLeft;
|
|
|
|
const viewRight = viewLeft + div.clientWidth;
|
|
|
|
const viewTop = div.offsetTop + div.clientTop;
|
|
|
|
const viewBottom = viewTop + div.clientHeight;
|
|
|
|
|
|
|
|
if (viewLeft < scrollRight && viewRight > scrollLeft &&
|
|
|
|
viewTop < scrollBottom && viewBottom > scrollTop) {
|
|
|
|
const hiddenHeight = Math.max(0, scrollTop - viewTop) +
|
|
|
|
Math.max(0, viewBottom - scrollBottom);
|
|
|
|
const hiddenWidth = Math.max(0, scrollLeft - viewLeft) +
|
|
|
|
Math.max(0, viewRight - scrollRight);
|
|
|
|
const visibleArea = (div.clientHeight - hiddenHeight) *
|
|
|
|
(div.clientWidth - hiddenWidth);
|
|
|
|
const percent =
|
|
|
|
(visibleArea * 100 / div.clientHeight / div.clientWidth) | 0;
|
|
|
|
views.push({ id: view.id, x: viewLeft, y: viewTop, view, percent, });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { first: views[0], last: views[views.length - 1], views, };
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function takes a fixed layout of pages and compares the system under
|
|
|
|
// test to the slower implementation above, for a range of scroll viewport
|
|
|
|
// sizes and positions.
|
|
|
|
function scrollOverDocument(pages, horizontally = false) {
|
|
|
|
const size = pages.reduce(function(max, { div, }) {
|
|
|
|
return Math.max(
|
|
|
|
max,
|
|
|
|
horizontally ?
|
|
|
|
div.offsetLeft + div.clientLeft + div.clientWidth :
|
|
|
|
div.offsetTop + div.clientTop + div.clientHeight);
|
|
|
|
}, 0);
|
|
|
|
// The numbers (7 and 5) are mostly arbitrary, not magic: increase them to
|
|
|
|
// make scrollOverDocument tests faster, decrease them to make the tests
|
|
|
|
// more scrupulous, and keep them coprime to reduce the chance of missing
|
|
|
|
// weird edge case bugs.
|
|
|
|
for (let i = 0; i < size; i += 7) {
|
|
|
|
// The screen height (or width) here (j - i) doubles on each inner loop
|
|
|
|
// iteration; again, this is just to test an interesting range of cases
|
|
|
|
// without slowing the tests down to check every possible case.
|
|
|
|
for (let j = i + 5; j < size; j += (j - i)) {
|
|
|
|
const scroll = horizontally ? {
|
|
|
|
scrollTop: 0,
|
|
|
|
scrollLeft: i,
|
|
|
|
clientHeight: 10000,
|
|
|
|
clientWidth: j - i,
|
|
|
|
} : {
|
|
|
|
scrollTop: i,
|
|
|
|
scrollLeft: 0,
|
|
|
|
clientHeight: j - i,
|
|
|
|
clientWidth: 10000,
|
|
|
|
};
|
|
|
|
expect(getVisibleElements(scroll, pages, false, horizontally))
|
|
|
|
.toEqual(slowGetVisibleElements(scroll, pages));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
it('with pages of varying height', function() {
|
|
|
|
const pages = makePages([
|
|
|
|
[[50, 20], [20, 50]],
|
|
|
|
[[30, 12], [12, 30]],
|
|
|
|
[[20, 50], [50, 20]],
|
|
|
|
[[50, 20], [20, 50]],
|
|
|
|
]);
|
|
|
|
scrollOverDocument(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('widescreen challenge', function() {
|
|
|
|
const pages = makePages([
|
|
|
|
[[10, 50], [10, 60], [10, 70], [10, 80], [10, 90]],
|
|
|
|
[[10, 90], [10, 80], [10, 70], [10, 60], [10, 50]],
|
|
|
|
[[10, 50], [10, 60], [10, 70], [10, 80], [10, 90]],
|
|
|
|
]);
|
|
|
|
scrollOverDocument(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works with horizontal scrolling', function() {
|
|
|
|
const pages = makePages([
|
|
|
|
[[10, 50], [20, 20], [30, 10]],
|
|
|
|
]);
|
|
|
|
scrollOverDocument(pages, true);
|
|
|
|
});
|
|
|
|
|
2019-01-12 19:00:51 +09:00
|
|
|
it('handles `sortByVisibility` correctly', function() {
|
|
|
|
const scrollEl = {
|
|
|
|
scrollTop: 75,
|
|
|
|
scrollLeft: 0,
|
|
|
|
clientHeight: 750,
|
|
|
|
clientWidth: 1500,
|
|
|
|
};
|
|
|
|
const views = makePages([
|
|
|
|
[[100, 150]],
|
|
|
|
[[100, 150]],
|
|
|
|
[[100, 150]],
|
|
|
|
]);
|
|
|
|
|
|
|
|
const visible = getVisibleElements(scrollEl, views);
|
|
|
|
const visibleSorted = getVisibleElements(scrollEl, views,
|
|
|
|
/* sortByVisibility = */ true);
|
|
|
|
|
|
|
|
const viewsOrder = [], viewsSortedOrder = [];
|
|
|
|
for (const view of visible.views) {
|
|
|
|
viewsOrder.push(view.id);
|
|
|
|
}
|
|
|
|
for (const view of visibleSorted.views) {
|
|
|
|
viewsSortedOrder.push(view.id);
|
|
|
|
}
|
|
|
|
expect(viewsOrder).toEqual([0, 1, 2]);
|
|
|
|
expect(viewsSortedOrder).toEqual([1, 2, 0]);
|
|
|
|
});
|
|
|
|
|
Prevent `TypeError: views[index] is undefined` being throw in `getVisibleElements` when the viewer, or all pages, are hidden
Previously a couple of different attempts at fixing this problem has been rejected, given how *crucial* this code is for the correct function of the viewer, since no one has thus far provided any evidence that the problem actually affects the default viewer[1] nor an example using the viewer components directly (without another library on top).
The fact that none of the prior patches contained even a *simple* unit-test probably contributed to the unwillingness of a reviewer to sign off on the suggested changes.
However, it turns out that it's possible to create a reduced test-case, using the default viewer, that demonstrates the error[2]. Since this utilizes a hidden `<iframe>`, please note that this error will thus affect Firefox as well.
Note that while errors are thrown when the hidden `<iframe>` loads, the default viewer doesn't break completely since rendering does start working once the `<iframe>` becomes visible (although the errors do break the initial Toolbar state).
Before making any changes here, I carefully read through not just the immediately relevant code but also the rendering code in the viewer (given it's dependence on `getVisibleElements`). After concluding that the changes should be safe in general, the default viewer was tested without any issues found. (The above being much easier with significant prior experience of working with the viewer code.)
Finally the patch also adds new unit-tests, one of which explicitly triggers the relevant code-path and will thus fail with the current `master` branch.
This patch also makes `PDFViewerApplication` slightly more robust against errors during document opening, to ensure that viewer/document initialization always completes as expected.
Please keep in mind that even though this patch prevents an error in `getVisibleElements`, it's still not possible to set the initial position/zoom level/sidebar view etc. when the viewer is hidden since rendering and scrolling is completely dependent[3] on being able to actually access the DOM elements.
---
[1] And hence the PDF Viewer that's built-in to Firefox.
[2] Copy the HTML code below and save it as `iframe.html`, and place the file in the `web/` folder. Then start the server, with `gulp server`, and navigate to http://localhost:8888/web/iframe.html
```html
<!DOCTYPE html>
<html>
<head>
<title>Iframe test</title>
<script>
window.onload = function() {
const button = document.getElementById('button1');
const frame = document.getElementById('frame1');
button.addEventListener('click', function(evt) {
frame.hidden = !frame.hidden;
});
};
</script>
</head>
<body>
<button id="button1">Toggle iframe</button>
<br>
<iframe id="frame1" width="800" height="600" src="http://localhost:8888/web/viewer.html" hidden="true"></iframe>
</body>
</html>
```
[3] This is an old, pre-exisiting, issue that's not relevant to this patch as such (and it's already being tracked elsewhere).
2019-01-12 02:25:10 +09:00
|
|
|
it('handles views being empty', function() {
|
|
|
|
const scrollEl = {
|
|
|
|
scrollTop: 10,
|
|
|
|
scrollLeft: 0,
|
|
|
|
clientHeight: 750,
|
|
|
|
clientWidth: 1500,
|
|
|
|
};
|
|
|
|
const views = [];
|
|
|
|
|
|
|
|
expect(getVisibleElements(scrollEl, views)).toEqual({
|
|
|
|
first: undefined, last: undefined, views: [],
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles all views being hidden (without errors)', function() {
|
|
|
|
const scrollEl = {
|
|
|
|
scrollTop: 100000,
|
|
|
|
scrollLeft: 0,
|
|
|
|
clientHeight: 750,
|
|
|
|
clientWidth: 1500,
|
|
|
|
};
|
|
|
|
const views = makePages([
|
|
|
|
[[100, 150]],
|
|
|
|
[[100, 150]],
|
|
|
|
[[100, 150]],
|
|
|
|
]);
|
|
|
|
|
|
|
|
expect(getVisibleElements(scrollEl, views)).toEqual({
|
|
|
|
first: undefined, last: undefined, views: [],
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-05-15 12:10:32 +09:00
|
|
|
// This sub-suite is for a notionally internal helper function for
|
|
|
|
// getVisibleElements.
|
|
|
|
describe('backtrackBeforeAllVisibleElements', function() {
|
|
|
|
// Layout elements common to all tests
|
|
|
|
const tallPage = [10, 50];
|
|
|
|
const shortPage = [10, 10];
|
|
|
|
|
|
|
|
// A scroll position that ensures that only the tall pages in the second
|
|
|
|
// row are visible
|
|
|
|
const top1 =
|
|
|
|
20 + SPACING + // height of the first row
|
|
|
|
40; // a value between 30 (so the short pages on the second row are
|
|
|
|
// hidden) and 50 (so the tall pages are visible)
|
|
|
|
|
|
|
|
// A scroll position that ensures that all of the pages in the second row
|
|
|
|
// are visible, but the tall ones are a tiny bit cut off
|
|
|
|
const top2 = 20 + SPACING + // height of the first row
|
|
|
|
10; // a value greater than 0 but less than 30
|
|
|
|
|
|
|
|
// These tests refer to cases enumerated in the comments of
|
|
|
|
// backtrackBeforeAllVisibleElements.
|
|
|
|
it('handles case 1', function() {
|
|
|
|
const pages = makePages([
|
|
|
|
[[10, 20], [10, 20], [10, 20], [10, 20]],
|
|
|
|
[tallPage, shortPage, tallPage, shortPage],
|
|
|
|
[[10, 50], [10, 50], [10, 50], [10, 50]],
|
|
|
|
[[10, 20], [10, 20], [10, 20], [10, 20]],
|
|
|
|
[[10, 20]],
|
|
|
|
]);
|
|
|
|
// binary search would land on the second row, first page
|
|
|
|
const bsResult = 4;
|
|
|
|
expect(backtrackBeforeAllVisibleElements(bsResult, pages, top1))
|
|
|
|
.toEqual(4);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles case 2', function() {
|
|
|
|
const pages = makePages([
|
|
|
|
[[10, 20], [10, 20], [10, 20], [10, 20]],
|
|
|
|
[tallPage, shortPage, tallPage, tallPage],
|
|
|
|
[[10, 50], [10, 50], [10, 50], [10, 50]],
|
|
|
|
[[10, 20], [10, 20], [10, 20], [10, 20]],
|
|
|
|
]);
|
|
|
|
// binary search would land on the second row, third page
|
|
|
|
const bsResult = 6;
|
|
|
|
expect(backtrackBeforeAllVisibleElements(bsResult, pages, top1))
|
|
|
|
.toEqual(4);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles case 3', function() {
|
|
|
|
const pages = makePages([
|
|
|
|
[[10, 20], [10, 20], [10, 20], [10, 20]],
|
|
|
|
[tallPage, shortPage, tallPage, shortPage],
|
|
|
|
[[10, 50], [10, 50], [10, 50], [10, 50]],
|
|
|
|
[[10, 20], [10, 20], [10, 20], [10, 20]],
|
|
|
|
]);
|
|
|
|
// binary search would land on the third row, first page
|
|
|
|
const bsResult = 8;
|
|
|
|
expect(backtrackBeforeAllVisibleElements(bsResult, pages, top1))
|
|
|
|
.toEqual(4);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles case 4', function() {
|
|
|
|
const pages = makePages([
|
|
|
|
[[10, 20], [10, 20], [10, 20], [10, 20]],
|
|
|
|
[tallPage, shortPage, tallPage, shortPage],
|
|
|
|
[[10, 50], [10, 50], [10, 50], [10, 50]],
|
|
|
|
[[10, 20], [10, 20], [10, 20], [10, 20]],
|
|
|
|
]);
|
|
|
|
// binary search would land on the second row, first page
|
|
|
|
const bsResult = 4;
|
|
|
|
expect(backtrackBeforeAllVisibleElements(bsResult, pages, top2))
|
|
|
|
.toEqual(4);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('moveToEndOfArray', function() {
|
|
|
|
it('works on empty arrays', function() {
|
|
|
|
const data = [];
|
|
|
|
moveToEndOfArray(data, function() {});
|
|
|
|
expect(data).toEqual([]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when moving everything', function() {
|
|
|
|
const data = [1, 2, 3, 4, 5];
|
|
|
|
moveToEndOfArray(data, function() {
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
expect(data).toEqual([1, 2, 3, 4, 5]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when moving some things', function() {
|
|
|
|
const data = [1, 2, 3, 4, 5];
|
|
|
|
moveToEndOfArray(data, function(x) {
|
|
|
|
return x % 2 === 0;
|
|
|
|
});
|
|
|
|
expect(data).toEqual([1, 3, 5, 2, 4]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when moving one thing', function() {
|
|
|
|
const data = [1, 2, 3, 4, 5];
|
|
|
|
moveToEndOfArray(data, function(x) {
|
|
|
|
return x === 1;
|
|
|
|
});
|
|
|
|
expect(data).toEqual([2, 3, 4, 5, 1]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when moving nothing', function() {
|
|
|
|
const data = [1, 2, 3, 4, 5];
|
|
|
|
moveToEndOfArray(data, function(x) {
|
|
|
|
return x === 0;
|
|
|
|
});
|
|
|
|
expect(data).toEqual([1, 2, 3, 4, 5]);
|
|
|
|
});
|
|
|
|
});
|
2014-12-27 01:43:13 +09:00
|
|
|
});
|