From ba23a9e8f963ccb1d3786ce17804151c808fb92c Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Fri, 16 Aug 2013 09:53:05 -0500 Subject: [PATCH] Adds initial telemetry probes --- .../firefox/components/PdfStreamConverter.js | 51 ++++++++++++- .../firefox/content/PdfJsTelemetry-addon.jsm | 72 +++++++++++++++++++ extensions/firefox/content/PdfJsTelemetry.jsm | 59 +++++++++++++++ make.js | 4 ++ src/core/core.js | 20 +++++- web/viewer.js | 34 +++++++++ 6 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 extensions/firefox/content/PdfJsTelemetry-addon.jsm create mode 100644 extensions/firefox/content/PdfJsTelemetry.jsm diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index 270a73a38..db84e8254 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -16,7 +16,7 @@ */ /* jshint esnext:true */ /* globals Components, Services, XPCOMUtils, NetUtil, PrivateBrowsingUtils, - dump, NetworkManager */ + dump, NetworkManager, PdfJsTelemetry */ 'use strict'; @@ -42,6 +42,9 @@ Cu.import('resource://pdf.js/network.js'); XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils', 'resource://gre/modules/PrivateBrowsingUtils.jsm'); +XPCOMUtils.defineLazyModuleGetter(this, 'PdfJsTelemetry', + 'resource://pdf.js/PdfJsTelemetry.jsm'); + var Svc = {}; XPCOMUtils.defineLazyServiceGetter(Svc, 'mime', '@mozilla.org/mime;1', @@ -194,6 +197,12 @@ PdfDataListener.prototype = { function ChromeActions(domWindow, contentDispositionFilename) { this.domWindow = domWindow; this.contentDispositionFilename = contentDispositionFilename; + this.telemetryState = { + documentInfo: false, + firstPageInfo: false, + streamTypesUsed: [], + startAt: Date.now() + }; } ChromeActions.prototype = { @@ -321,12 +330,49 @@ ChromeActions.prototype = { supportsDocumentColors: function() { return getBoolPref('browser.display.use_document_colors', true); }, + reportTelemetry: function (data) { + var probeInfo = JSON.parse(data); + switch (probeInfo.type) { + case 'documentInfo': + if (!this.telemetryState.documentInfo) { + PdfJsTelemetry.onDocumentVersion(probeInfo.version | 0); + PdfJsTelemetry.onDocumentGenerator(probeInfo.generator | 0); + if (probeInfo.formType) { + PdfJsTelemetry.onForm(probeInfo.formType === 'acroform'); + } + this.telemetryState.documentInfo = true; + } + break; + case 'pageInfo': + if (!this.telemetryState.firstPageInfo) { + var duration = Date.now() - this.telemetryState.startAt; + PdfJsTelemetry.onTimeToView(duration); + this.telemetryState.firstPageInfo = true; + } + break; + case 'streamInfo': + if (!Array.isArray(probeInfo.streamTypes)) { + break; + } + for (var i = 0; i < probeInfo.streamTypes.length; i++) { + var streamTypeId = probeInfo.streamTypes[i] | 0; + if (streamTypeId >= 0 && streamTypeId < 10 && + !this.telemetryState.streamTypesUsed[streamTypeId]) { + PdfJsTelemetry.onStreamType(streamTypeId); + this.telemetryState.streamTypesUsed[streamTypeId] = true; + } + } + break; + } + }, fallback: function(url, sendResponse) { var self = this; var domWindow = this.domWindow; var strings = getLocalizedStrings('chrome.properties'); var message = getLocalizedString(strings, 'unsupported_feature'); + PdfJsTelemetry.onFallback(); + var notificationBox = null; try { // Based on MDN's "Working with windows in chrome code" @@ -730,6 +776,9 @@ PdfStreamConverter.prototype = { false); } + PdfJsTelemetry.onViewerIsUsed(); + PdfJsTelemetry.onDocumentSize(aRequest.contentLength); + if (!rangeRequest) { // Creating storage for PDF data var contentLength = aRequest.contentLength; diff --git a/extensions/firefox/content/PdfJsTelemetry-addon.jsm b/extensions/firefox/content/PdfJsTelemetry-addon.jsm new file mode 100644 index 000000000..e340065da --- /dev/null +++ b/extensions/firefox/content/PdfJsTelemetry-addon.jsm @@ -0,0 +1,72 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* jshint esnext:true */ + +'use strict'; + +this.EXPORTED_SYMBOLS = ['PdfJsTelemetry']; + +const Cu = Components.utils; +Cu.import('resource://gre/modules/Services.jsm'); + +const ADDON_ID = "uriloader@pdf.js"; + +var Telemetry = Services.telemetry; +Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_USED", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); +Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_FALLBACK_SHOWN", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); +Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_VERSION", 1, 10, 11, Telemetry.HISTOGRAM_LINEAR); +Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_GENERATOR", 1, 25, 26, Telemetry.HISTOGRAM_LINEAR); +Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_SIZE_KB", 2, 64 * 1024, 20, Telemetry.HISTOGRAM_EXPONENTIAL); +Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_FORM", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); +Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_STREAM_TYPES", 1, 9, 10, Telemetry.HISTOGRAM_LINEAR); +Telemetry.registerAddonHistogram(ADDON_ID, "PDF_VIEWER_TIME_TO_VIEW_MS", 1, 10000, 50, Telemetry.HISTOGRAM_EXPONENTIAL); + + +this.PdfJsTelemetry = { + onViewerIsUsed: function () { + let histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_VIEWER_USED"); + histogram.add(true); + }, + onFallback: function () { + let histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_VIEWER_FALLBACK_SHOWN"); + histogram.add(true); + }, + onDocumentSize: function (size) { + let histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_SIZE_KB"); + histogram.add(size / 1024); + }, + onDocumentVersion: function (versionId) { + let histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_VERSION"); + histogram.add(versionId); + }, + onDocumentGenerator: function (generatorId) { + let histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_VIEWER_DOCUMENT_GENERATOR"); + histogram.add(generatorId); + }, + onForm: function (isAcroform) { + let histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_VIEWER_FORM"); + histogram.add(isAcroform); + }, + onStreamType: function (streamTypeId) { + let histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_VIEWER_STREAM_TYPES"); + histogram.add(streamTypeId); + }, + onTimeToView: function (ms) { + let histogram = Telemetry.getAddonHistogram(ADDON_ID, "PDF_VIEWER_TIME_TO_VIEW_MS"); + histogram.add(ms); + } +}; diff --git a/extensions/firefox/content/PdfJsTelemetry.jsm b/extensions/firefox/content/PdfJsTelemetry.jsm new file mode 100644 index 000000000..17ba05761 --- /dev/null +++ b/extensions/firefox/content/PdfJsTelemetry.jsm @@ -0,0 +1,59 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* jshint esnext:true */ + +'use strict'; + +this.EXPORTED_SYMBOLS = ['PdfJsTelemetry']; + +const Cu = Components.utils; +Cu.import('resource://gre/modules/Services.jsm'); + +this.PdfJsTelemetry = { + onViewerIsUsed: function () { + let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_USED"); + histogram.add(true); + }, + onFallback: function () { + let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_FALLBACK_SHOWN"); + histogram.add(true); + }, + onDocumentSize: function (size) { + let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_DOCUMENT_SIZE_KB"); + histogram.add(size / 1024); + }, + onDocumentVersion: function (versionId) { + let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_DOCUMENT_VERSION"); + histogram.add(versionId); + }, + onDocumentGenerator: function (generatorId) { + let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_DOCUMENT_GENERATOR"); + histogram.add(generatorId); + }, + onForm: function (isAcroform) { + let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_FORM"); + histogram.add(isAcroform); + }, + onStreamType: function (streamTypeId) { + let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_STREAM_TYPES"); + histogram.add(streamTypeId); + }, + onTimeToView: function (ms) { + let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_TIME_TO_VIEW_MS"); + histogram.add(ms); + } +}; diff --git a/make.js b/make.js index e01a5abf7..f5e08e491 100644 --- a/make.js +++ b/make.js @@ -417,6 +417,9 @@ target.firefox = function() { cp(FIREFOX_CONTENT_DIR + 'PdfJs-stub.jsm', FIREFOX_BUILD_CONTENT_DIR + 'PdfJs.jsm'); + cp(FIREFOX_CONTENT_DIR + 'PdfJsTelemetry-addon.jsm', + FIREFOX_BUILD_CONTENT_DIR + 'PdfJsTelemetry.jsm'); + // Copy extension files cd(FIREFOX_EXTENSION_DIR); cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + FIREFOX_BUILD_DIR); @@ -528,6 +531,7 @@ target.mozcentral = function() { mkdir('-p', MOZCENTRAL_CONTENT_DIR + '/web'); cp(FIREFOX_CONTENT_DIR + 'PdfJs.jsm', MOZCENTRAL_CONTENT_DIR); + cp(FIREFOX_CONTENT_DIR + 'PdfJsTelemetry.jsm', MOZCENTRAL_CONTENT_DIR); // Copy extension files cd('extensions/firefox'); diff --git a/src/core/core.js b/src/core/core.js index 24b50ccb9..f22f7fd3c 100644 --- a/src/core/core.js +++ b/src/core/core.js @@ -326,7 +326,22 @@ var PDFDocument = (function PDFDocumentClosure() { PDFDocument.prototype = { parse: function PDFDocument_parse(recoveryMode) { this.setup(recoveryMode); - this.acroForm = this.catalog.catDict.get('AcroForm'); + try { + // checking if AcroForm is present + this.acroForm = this.catalog.catDict.get('AcroForm'); + if (this.acroForm) { + this.xfa = this.acroForm.get('XFA'); + var fields = this.acroForm.get('Fields'); + if ((!fields || !isArray(fields) || fields.length === 0) && + !this.xfa) { + // no fields and no XFA -- not a form (?) + this.acroForm = null; + } + } + } catch (ex) { + info('Something wrong with AcroForm entry'); + this.acroForm = null; + } }, get linearization() { @@ -438,7 +453,8 @@ var PDFDocument = (function PDFDocumentClosure() { get documentInfo() { var docInfo = { PDFFormatVersion: this.pdfFormatVersion, - IsAcroFormPresent: !!this.acroForm + IsAcroFormPresent: !!this.acroForm, + IsXFAPresent: !!this.xfa }; var infoDict; try { diff --git a/web/viewer.js b/web/viewer.js index dce31614b..43ab509de 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1020,6 +1020,34 @@ var PDFView = { console.warn('Warning: AcroForm/XFA is not supported'); PDFView.fallback(); } + +//#if (FIREFOX || MOZCENTRAL) +// var versionId = String(info.PDFFormatVersion).slice(-1) | 0; +// var generatorId = 0; +// var KNOWN_GENERATORS = ["acrobat distiller", "acrobat pdfwritter", +// "adobe livecycle", "adobe pdf library", "adobe photoshop", "ghostscript", +// "tcpdf", "cairo", "dvipdfm", "dvips", "pdftex", "pdfkit", "itext", +// "prince", "quarkxpress", "mac os x", "microsoft", "openoffice", "oracle", +// "luradocument", "pdf-xchange", "antenna house", "aspose.cells", "fpdf"]; +// var generatorId = 0; +// if (info.Producer) { +// KNOWN_GENERATORS.some(function (generator, s, i) { +// if (generator.indexOf(s) < 0) { +// return false; +// } +// generatorId = i + 1; +// return true; +// }.bind(null, info.Producer.toLowerCase())); +// } +// var formType = !info.IsAcroFormPresent ? null : info.IsXFAPresent ? +// 'xfa' : 'acroform'; +// FirefoxCom.request('reportTelemetry', JSON.stringify({ +// type: 'documentInfo', +// version: versionId, +// generator: generatorId, +// formType: formType +// })); +//#endif }); }, @@ -1928,6 +1956,12 @@ var PageView = function pageView(container, id, scale, }); div.dispatchEvent(event); +//#if (FIREFOX || MOZCENTRAL) +// FirefoxCom.request('reportTelemetry', JSON.stringify({ +// type: 'pageInfo' +// })); +// // TODO add stream types report here +//#endif callback(); }