Merge m-c to inbound, a=merge
|
@ -75,6 +75,8 @@ browser/extensions/pdfjs/**
|
|||
browser/extensions/pocket/content/panels/js/tmpl.js
|
||||
browser/extensions/pocket/content/panels/js/vendor/**
|
||||
browser/locales/**
|
||||
# imported from chromium
|
||||
browser/extensions/mortar/**
|
||||
|
||||
# devtools/ exclusions
|
||||
devtools/client/canvasdebugger/**
|
||||
|
|
|
@ -85,8 +85,8 @@ tasks:
|
|||
chainOfTrust: true
|
||||
|
||||
# Note: This task is built server side without the context or tooling that
|
||||
# exist in tree so we must hard code the version
|
||||
image: 'taskcluster/decision:0.1.7'
|
||||
# exist in tree so we must hard code the hash
|
||||
image: 'taskcluster/decision@sha256:0f59f922d86c471e208b7ea08ab077fc68c3920ed5e6895d69a23e8f3457dc24'
|
||||
|
||||
maxRunTime: 1800
|
||||
|
||||
|
|
|
@ -684,9 +684,6 @@ pref("dom.ipc.systemMessageCPULockTimeoutSec", 30);
|
|||
// Ignore the "dialog=1" feature in window.open.
|
||||
pref("dom.disable_window_open_dialog_feature", true);
|
||||
|
||||
// Enable before keyboard events and after keyboard events.
|
||||
pref("dom.beforeAfterKeyboardEvent.enabled", true);
|
||||
|
||||
// Screen reader support
|
||||
pref("accessibility.accessfu.activate", 2);
|
||||
pref("accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement,Landmark,ListItem");
|
||||
|
|
|
@ -501,6 +501,8 @@ pref("privacy.cpd.offlineApps", false);
|
|||
pref("privacy.cpd.siteSettings", false);
|
||||
pref("privacy.cpd.openWindows", false);
|
||||
|
||||
pref("privacy.history.custom", false);
|
||||
|
||||
// What default should we use for the time span in the sanitizer:
|
||||
// 0 - Clear everything
|
||||
// 1 - Last Hour
|
||||
|
|
|
@ -11,7 +11,12 @@ var gPermURI;
|
|||
var gPermPrincipal;
|
||||
var gUsageRequest;
|
||||
|
||||
var gPermissions = SitePermissions.listPermissions();
|
||||
// Array of permissionIDs sorted alphabetically by label.
|
||||
var gPermissions = SitePermissions.listPermissions().sort((a, b) => {
|
||||
let firstLabel = SitePermissions.getPermissionLabel(a);
|
||||
let secondLabel = SitePermissions.getPermissionLabel(b);
|
||||
return firstLabel.localeCompare(secondLabel);
|
||||
});
|
||||
gPermissions.push("plugins");
|
||||
|
||||
var permissionObserver = {
|
||||
|
|
|
@ -79,7 +79,11 @@ function openSelectPopup(selectPopup, withMouse, selector = "select", win = win
|
|||
|
||||
function hideSelectPopup(selectPopup, mode = "enter", win = window)
|
||||
{
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
let selectClosedPromise = ContentTask.spawn(browser, null, function*() {
|
||||
Cu.import("resource://gre/modules/SelectContentHelper.jsm");
|
||||
return ContentTaskUtils.waitForCondition(() => !SelectContentHelper.open);
|
||||
});
|
||||
|
||||
if (mode == "escape") {
|
||||
EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" }, win);
|
||||
|
@ -91,7 +95,7 @@ function hideSelectPopup(selectPopup, mode = "enter", win = window)
|
|||
EventUtils.synthesizeMouseAtCenter(selectPopup.lastChild, { }, win);
|
||||
}
|
||||
|
||||
return popupHiddenPromise;
|
||||
return selectClosedPromise;
|
||||
}
|
||||
|
||||
function getInputEvents()
|
||||
|
@ -559,3 +563,4 @@ add_task(function* test_somehidden() {
|
|||
yield hideSelectPopup(selectPopup, "escape");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
|
|
|
@ -287,7 +287,9 @@ var gPrivacyPane = {
|
|||
let mode;
|
||||
let getVal = aPref => document.getElementById(aPref).value;
|
||||
|
||||
if (this._checkHistoryValues(this.prefsForKeepingHistory)) {
|
||||
if (getVal("privacy.history.custom"))
|
||||
mode = "custom";
|
||||
else if (this._checkHistoryValues(this.prefsForKeepingHistory)) {
|
||||
if (getVal("browser.privatebrowsing.autostart"))
|
||||
mode = "dontremember";
|
||||
else
|
||||
|
@ -317,6 +319,7 @@ var gPrivacyPane = {
|
|||
break;
|
||||
}
|
||||
document.getElementById("historyPane").selectedIndex = selectedIndex;
|
||||
document.getElementById("privacy.history.custom").value = selectedIndex == 2;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,6 +52,9 @@
|
|||
<preference id="browser.formfill.enable"
|
||||
name="browser.formfill.enable"
|
||||
type="bool"/>
|
||||
<preference id="privacy.history.custom"
|
||||
name="privacy.history.custom"
|
||||
type="bool"/>
|
||||
<!-- Cookies -->
|
||||
<preference id="network.cookie.cookieBehavior"
|
||||
name="network.cookie.cookieBehavior"
|
||||
|
|
|
@ -331,6 +331,10 @@ var testRunner = {
|
|||
},
|
||||
};
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("privacy.history.custom");
|
||||
});
|
||||
|
||||
openPreferencesViaOpenPreferencesAPI("panePrivacy", null, {leaveOpen: true}).then(function() {
|
||||
let doc = gBrowser.contentDocument;
|
||||
let historyMode = doc.getElementById("historyMode");
|
||||
|
|
|
@ -11,7 +11,7 @@ loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
|
|||
run_test_subset([
|
||||
test_custom_retention("rememberHistory", "remember"),
|
||||
test_custom_retention("rememberHistory", "custom"),
|
||||
test_custom_retention("rememberForms", "remember"),
|
||||
test_custom_retention("rememberForms", "custom"),
|
||||
test_historymode_retention("remember", "remember"),
|
||||
test_custom_retention("rememberForms", "custom"),
|
||||
test_historymode_retention("remember", "custom"),
|
||||
]);
|
||||
|
|
|
@ -14,12 +14,12 @@ let runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
|
|||
run_test_subset([
|
||||
test_custom_retention("acceptCookies", "remember"),
|
||||
test_custom_retention("acceptCookies", "custom"),
|
||||
test_custom_retention("acceptThirdPartyMenu", "remember", "visited"),
|
||||
test_custom_retention("acceptThirdPartyMenu", "custom", "visited"),
|
||||
test_custom_retention("acceptThirdPartyMenu", "custom", "always"),
|
||||
test_custom_retention("keepCookiesUntil", "remember", 1),
|
||||
test_custom_retention("keepCookiesUntil", "custom", 1),
|
||||
test_custom_retention("keepCookiesUntil", "custom", 2),
|
||||
test_custom_retention("keepCookiesUntil", "custom", 0),
|
||||
test_custom_retention("alwaysClear", "remember"),
|
||||
test_custom_retention("alwaysClear", "custom"),
|
||||
test_historymode_retention("remember", "remember"),
|
||||
test_custom_retention("alwaysClear", "custom"),
|
||||
test_historymode_retention("remember", "custom"),
|
||||
]);
|
||||
|
|
|
@ -16,12 +16,14 @@ function testPrefStateMatchesLockedState() {
|
|||
let preference = doc.getElementById("privacy.sanitize.sanitizeOnShutdown");
|
||||
is(checkbox.disabled, preference.locked, "Always Clear checkbox should be enabled when preference is not locked.");
|
||||
|
||||
Services.prefs.clearUserPref("privacy.history.custom");
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
|
||||
add_task(function setup() {
|
||||
registerCleanupFunction(function resetPreferences() {
|
||||
Services.prefs.unlockPref("privacy.sanitize.sanitizeOnShutdown");
|
||||
Services.prefs.clearUserPref("privacy.history.custom");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# We keep the Makefile until the mortar is integrated into gecko build system.
|
||||
ifeq ($(shell uname), Darwin)
|
||||
DLL_SUFFIX = .dylib
|
||||
CXX = g++
|
||||
CXX_FLAGS = -std=c++11 -g -fPIC
|
||||
LD_FLAGS = -dynamiclib
|
||||
ifdef DEBUG
|
||||
CXX_FLAGS += -DDEBUG
|
||||
endif
|
||||
else ifeq ($(shell uname), Linux)
|
||||
DLL_SUFFIX = .so
|
||||
CXX = g++
|
||||
CXX_FLAGS = -std=c++11 -g -fPIC
|
||||
LD_FLAGS = -shared
|
||||
ifdef DEBUG
|
||||
CXX_FLAGS += -DDEBUG
|
||||
endif
|
||||
else
|
||||
DLL_SUFFIX = .dll
|
||||
CXX = cl
|
||||
CXX_FLAGS = -nologo -EHsc -Oy-
|
||||
ifdef DEBUG
|
||||
CXX_FLAGS += -LDd -DDEBUG -Od
|
||||
else
|
||||
CXX_FLAGS += -LD
|
||||
endif
|
||||
LD_FLAGS = -link -dll
|
||||
endif
|
||||
|
||||
all : ppapi/out/rpc$(DLL_SUFFIX) ppapi/out/interpose$(DLL_SUFFIX)
|
||||
|
||||
ppapi/out/rpc$(DLL_SUFFIX): ppapi/out/rpc.cc host/rpc.h host/rpc.cc
|
||||
$(CXX) $(CXX_FLAGS) -I. -o $@ host/rpc.cc $(LD_FLAGS)
|
||||
|
||||
ppapi/out/interpose$(DLL_SUFFIX): ppapi/out/rpc.cc host/rpc.h host/interpose.cc
|
||||
$(CXX) -DINTERPOSE $(CXX_FLAGS) -I. -o $@ host/interpose.cc $(LD_FLAGS)
|
||||
|
||||
ppapi/out/rpc.cc: $(shell find . -name *.idl) $(shell find . -name *.py)
|
||||
cd ppapi/generators; python idl_gen_rpc.py --out ../out/rpc.cc ; cd ../..
|
||||
|
||||
json/test: json/json.cpp json/json.h json/test.cpp
|
||||
$(CXX) -I./json -o $@ json/test.cpp
|
||||
|
||||
test-json: json/test
|
||||
@./json/test && echo "OK"
|
||||
|
||||
clean:
|
||||
rm -rf ppapi/generators/*.pyc ppapi/generators/*~ *~ ppapi/out/* json/*~ json/test *.obj
|
||||
|
||||
.PHONY: all test-json test clean
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
let GLES2Utils = {
|
||||
bytesPerElement: function(context, type) {
|
||||
switch (type) {
|
||||
case context.FLOAT:
|
||||
case context.INT:
|
||||
case context.UNSIGNED_INT:
|
||||
return 4;
|
||||
case context.SHORT:
|
||||
case context.UNSIGNED_SHORT:
|
||||
case context.UNSIGNED_SHORT_5_6_5:
|
||||
case context.UNSIGNED_SHORT_4_4_4_4:
|
||||
case context.UNSIGNED_SHORT_5_5_5_1:
|
||||
return 2;
|
||||
case context.BYTE:
|
||||
case context.UNSIGNED_BYTE:
|
||||
return 1;
|
||||
default:
|
||||
throw new Error("Don't know this type.");
|
||||
}
|
||||
},
|
||||
elementsPerGroup: function(context, format, type) {
|
||||
switch (type) {
|
||||
case context.UNSIGNED_SHORT_5_6_5:
|
||||
case context.UNSIGNED_SHORT_4_4_4_4:
|
||||
case context.UNSIGNED_SHORT_5_5_5_1:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case context.RGB:
|
||||
return 3;
|
||||
case context.LUMINANCE_ALPHA:
|
||||
return 2;
|
||||
case context.RGBA:
|
||||
return 4;
|
||||
case context.ALPHA:
|
||||
case context.LUMINANCE:
|
||||
case context.DEPTH_COMPONENT:
|
||||
case context.DEPTH_COMPONENT16:
|
||||
return 1;
|
||||
default:
|
||||
throw new Error("Don't know this format.");
|
||||
}
|
||||
},
|
||||
computeImageGroupSize: function(context, format, type) {
|
||||
return this.bytesPerElement(context, type) * this.elementsPerGroup(context, format, type);
|
||||
},
|
||||
computeImageDataSize: function(context, width, height, format, type) {
|
||||
const unpackAlignment = 4;
|
||||
|
||||
let bytesPerGroup = this.computeImageGroupSize(context, format, type);
|
||||
let rowSize = width * bytesPerGroup;
|
||||
if (height == 1) {
|
||||
return rowSize;
|
||||
}
|
||||
|
||||
let temp = rowSize + unpackAlignment - 1;
|
||||
let paddedRowSize = Math.floor(temp / unpackAlignment) * unpackAlignment;
|
||||
let sizeOfAllButLastRow = (height - 1) * paddedRowSize;
|
||||
return sizeOfAllButLastRow + rowSize;
|
||||
},
|
||||
};
|
||||
|
||||
var EXPORTED_SYMBOLS = ["GLES2Utils"];
|
|
@ -0,0 +1,33 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let rt;
|
||||
function getRuntime(type) {
|
||||
if (!rt) {
|
||||
let process = Cc["@mozilla.org/plugin/ppapi.js-process;1"].getService(Ci.nsIPPAPIJSProcess);
|
||||
Cu.import("resource://ppapi.js/ppapi-runtime.jsm");
|
||||
rt = new PPAPIRuntime(process);
|
||||
process.launch(rt.callback);
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
addMessageListener("ppapi.js:createInstance", ({ target, data: { type, info }, objects: { pluginWindow } }) => {
|
||||
dump("ppapi.js:createInstance\n");
|
||||
let rt = getRuntime(type);
|
||||
let instance = rt.createInstance(info, content, docShell.chromeEventHandler, pluginWindow, target);
|
||||
addEventListener("unload", () => {
|
||||
rt.destroyInstance(instance);
|
||||
});
|
||||
});
|
||||
|
||||
addEventListener("DOMContentLoaded", () => {
|
||||
// Passing an object here forces the creation of the CPOW manager in the
|
||||
// parent.
|
||||
sendRpcMessage("ppapi.js:frameLoaded", undefined, {});
|
||||
});
|
|
@ -0,0 +1,74 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function sandboxScript(sandbox)
|
||||
{
|
||||
dump("sandboxScript " + sandbox.pluginElement + "\n");
|
||||
Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader).loadSubScript("resource://ppapiflash.js/ppapi-content-sandbox.js", sandbox);
|
||||
}
|
||||
|
||||
let plugins;
|
||||
function startup(data) {
|
||||
dump(">>>STARTED!!!\n");
|
||||
|
||||
let root = data.installPath.parent.parent;
|
||||
let rpclib = root.clone();
|
||||
let pluginlib = root.clone();
|
||||
let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
|
||||
if (os == "Darwin") {
|
||||
rpclib.appendRelativePath("ppapi/out/rpc.dylib");
|
||||
pluginlib.appendRelativePath("plugin/PepperFlashPlayer-debug");
|
||||
} else if (os == "Linux") {
|
||||
rpclib.appendRelativePath("ppapi/out/rpc.so");
|
||||
pluginlib.appendRelativePath("plugin/libpepflashplayer.so");
|
||||
} else if (os == "WINNT") {
|
||||
rpclib.appendRelativePath("ppapi\\out\\rpc.dll");
|
||||
pluginlib.appendRelativePath("plugin\\pepflashplayer.dll");
|
||||
} else {
|
||||
throw("Don't know the path to the libraries for this OS!");
|
||||
}
|
||||
rpclib = rpclib.path;
|
||||
pluginlib = pluginlib.path;
|
||||
|
||||
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
|
||||
let plugin = pluginHost.registerFakePlugin({
|
||||
handlerURI: "chrome://ppapiflash.js/content/viewer.html",
|
||||
mimeEntries: [{ type: "application/x-shockwave-flash", extension: "swf" }],
|
||||
name: "Shockwave Flash",
|
||||
niceName: "PPAPI Flash plugin",
|
||||
description: "10",
|
||||
version: "1.0",
|
||||
sandboxScript : `(${sandboxScript.toSource()})(this);`,
|
||||
ppapiProcessArgs: [ rpclib, pluginlib ],
|
||||
});
|
||||
plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
|
||||
|
||||
let rng = Cc["@mozilla.org/security/random-generator;1"].createInstance(Ci.nsIRandomGenerator);
|
||||
Services.ppmm.addMessageListener("ppapi.js:generateRandomBytes", ({ data }) => {
|
||||
return rng.generateRandomBytes(data);
|
||||
});
|
||||
|
||||
let moduleLocalFiles = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
moduleLocalFiles.append("Flash PPAPI Data");
|
||||
try {
|
||||
moduleLocalFiles.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
|
||||
} catch (e) {
|
||||
if (e.result != Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
Services.ppmm.addMessageListener("ppapiflash.js:getModuleLocalFilesPath", () => {
|
||||
return moduleLocalFiles.path;
|
||||
});
|
||||
|
||||
dump("<<<STARTED!!!\n");
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
dump("SHUTDOWN!!!\n");
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
resource ppapiflash.js .
|
||||
content ppapiflash.js chrome/
|
||||
resource ppapi.js ../common/
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- 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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
line-height: 0;
|
||||
border: 0px none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>ppapiflash.js@mozilla.org</em:id>
|
||||
<em:name>ppapiflash.js</em:name>
|
||||
<em:description>ppapiflash.js</em:description>
|
||||
<em:version>0.1</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--Firefox-->
|
||||
<em:minVersion>37.0</em:minVersion>
|
||||
<em:maxVersion>45.*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
<em:strictCompatibility>false</em:strictCompatibility>
|
||||
</Description>
|
||||
</RDF>
|
|
@ -0,0 +1,226 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This code runs in the sandbox in the content process where the page that
|
||||
* loaded the plugin lives. It communicates with the process where the PPAPI
|
||||
* implementation lives.
|
||||
*/
|
||||
const { utils: Cu } = Components;
|
||||
|
||||
let mm = pluginElement.frameLoader.messageManager;
|
||||
let containerWindow = pluginElement.ownerDocument.defaultView;
|
||||
|
||||
function mapValue(v, instance) {
|
||||
return instance.rt.toPP_Var(v, instance);
|
||||
}
|
||||
|
||||
dump("<>>>>>>>>>>>>>>>>>>>> AHA <<<<<<<<<<<<<<<<<<<<<>\n");
|
||||
dump(`pluginElement: ${pluginElement.toSource()}\n`);
|
||||
dump(`pluginElement.frameLoader: ${pluginElement.frameLoader.toSource()}\n`);
|
||||
dump(`pluginElement.frameLoader.messageManager: ${pluginElement.frameLoader.messageManager.toSource()}\n`);
|
||||
dump("<>>>>>>>>>>>>>>>>>>>> AHA2 <<<<<<<<<<<<<<<<<<<<<>\n");
|
||||
|
||||
mm.addMessageListener("ppapi.js:frameLoaded", ({ target }) => {
|
||||
let tagName = pluginElement.nodeName;
|
||||
|
||||
// Getting absolute URL from the EMBED tag
|
||||
let url = pluginElement.srcURI.spec;
|
||||
let objectParams = new Map();
|
||||
for (let i = 0; i < pluginElement.attributes.length; ++i) {
|
||||
let paramName = pluginElement.attributes[i].localName;
|
||||
objectParams.set(paramName, pluginElement.attributes[i].value);
|
||||
}
|
||||
if (tagName == "OBJECT") {
|
||||
let params = pluginElement.getElementsByTagName("param");
|
||||
Array.prototype.forEach.call(params, (p) => {
|
||||
var paramName = p.getAttribute("name").toLowerCase();
|
||||
objectParams.set(paramName, p.getAttribute("value"));
|
||||
});
|
||||
}
|
||||
|
||||
let documentURL = pluginElement.ownerDocument.location.href;
|
||||
let baseUrl = documentURL;
|
||||
if (objectParams.base) {
|
||||
try {
|
||||
let parsedDocumentUrl = Services.io.newURI(documentURL);
|
||||
baseUrl = Services.io.newURI(objectParams.base, null, parsedDocumentUrl).spec;
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
let info = {
|
||||
url,
|
||||
documentURL,
|
||||
isFullFrame: pluginElement.ownerDocument.mozSyntheticDocument,
|
||||
setupJSInstanceObject: true,
|
||||
arguments: {
|
||||
keys: Array.from(objectParams.keys()),
|
||||
values: Array.from(objectParams.values()),
|
||||
},
|
||||
};
|
||||
|
||||
mm.sendAsyncMessage("ppapi.js:createInstance", { type: "flash", info },
|
||||
{ pluginWindow: containerWindow });
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapiflash.js:executeScript", ({ data, objects: { instance } }) => {
|
||||
return mapValue(containerWindow.eval(data), instance);
|
||||
});
|
||||
|
||||
let jsInterfaceObjects = new WeakMap();
|
||||
|
||||
mm.addMessageListener("ppapiflash.js:createObject", ({ data: { objectClass, objectData }, objects: { instance, call: callRemote } }) => {
|
||||
dump("ppapiflash.js:createObject\n");
|
||||
let call = (name, args) => {
|
||||
/*
|
||||
let metaData = target[Symbol.for("metaData")];
|
||||
let callObj = { __interface: "PPP_Class_Deprecated;1.0", __instance: metaData.objectClass, __member: name, object: metaData.objectData };
|
||||
*/
|
||||
dump("ppapiflash.js:createObject -> call\n");
|
||||
try {
|
||||
let result = callRemote(name, JSON.stringify(args));
|
||||
} catch (e) {
|
||||
dump("FIXME: we rely on CPOWs!\n");
|
||||
return undefined;
|
||||
}
|
||||
dump("RESULT: " + result[0] + "\n");
|
||||
dump("EXCEPTION: " + result[1] + "\n");
|
||||
return result[0];
|
||||
};
|
||||
|
||||
const handler = {
|
||||
// getPrototypeOf -> target (see bug 888969)
|
||||
// setPrototypeOf -> target (see bug 888969)
|
||||
// isExtensible -> target
|
||||
// preventExtensions -> target
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
let value = this.get(target, name);
|
||||
if (this._hasProperty(target, name)) {
|
||||
return {
|
||||
"writable": true,
|
||||
"enumerable": true,
|
||||
"configurable": true,
|
||||
"get": this.get.bind(this, target, name),
|
||||
"set": this.set.bind(this, target, name),
|
||||
};
|
||||
}
|
||||
|
||||
if (this._hasMethod(target, name)) {
|
||||
return {
|
||||
"value": (...args) => {
|
||||
//call("Call", { name, argc, argv, exception });
|
||||
},
|
||||
"writable": true,
|
||||
"enumerable": true,
|
||||
"configurable": true,
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
// defineProperty -> target
|
||||
has: function(target, name) {
|
||||
return this._hasProperty(target, name) || this._hasMethod(target, name);
|
||||
},
|
||||
get: function(target, name, receiver) {
|
||||
dump(`Calling GetProperty for ${name.toSource()}\n`);
|
||||
let prop = call("GetProperty", { name });
|
||||
if (!prop || PP_VarType[prop.type] == PP_VarType.PP_VARTYPE_UNDEFINED) {
|
||||
// FIXME Need to get the exception!
|
||||
return undefined;
|
||||
}
|
||||
return prop;
|
||||
},
|
||||
set: function(target, name, value, receiver) {
|
||||
call("SetProperty", { name, value });
|
||||
// FIXME Need to get the exception!
|
||||
return true;
|
||||
},
|
||||
deleteProperty: function(target, name) {
|
||||
call("RemoveProperty", { name });
|
||||
// FIXME Need to get the exception!
|
||||
return true;
|
||||
},
|
||||
enumerate: function(target) {
|
||||
let keys = this.ownKeys(target);
|
||||
return keys[Symbol.iterator];
|
||||
},
|
||||
ownKeys: function(target) {
|
||||
let result = call("GetAllPropertyNames");
|
||||
dump(result.toSource() + "\n");
|
||||
dump(typeof result.properties + "\n");
|
||||
return result.properties;
|
||||
},
|
||||
apply: function(target, thisArg, args) {
|
||||
},
|
||||
construct: function(target, args) {
|
||||
//call("Construct", { argc, argv, exception });
|
||||
},
|
||||
|
||||
_hasProperty: function(target, name) {
|
||||
return call("HasProperty", { name }) == 1 /* PP_Bool.PP_TRUE */;
|
||||
},
|
||||
_hasMethod: function(target, name) {
|
||||
return call("HasMethod", { name }) == 1 /* PP_Bool.PP_TRUE */;
|
||||
},
|
||||
};
|
||||
|
||||
let metaData = {
|
||||
objectClass,
|
||||
objectData,
|
||||
};
|
||||
|
||||
let clonedHandler = Cu.cloneInto(handler, containerWindow, { cloneFunctions: true });
|
||||
let clonedMetaData = Cu.cloneInto(metaData, containerWindow, { cloneFunctions: true });
|
||||
let target = Cu.createObjectIn(containerWindow);
|
||||
target[Symbol.for("metaData")] = clonedMetaData;
|
||||
|
||||
dump("IN CREATEOBJECT\n");
|
||||
let proxy = new containerWindow.Proxy(target, clonedHandler);
|
||||
jsInterfaceObjects.set(proxy, metaData);
|
||||
let foo = mapValue(proxy.wrappedJSObject, instance);
|
||||
dump("CREATEDOBJECT: " + foo.toSource() + "\n");
|
||||
return foo;
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapiflash.js:isInstanceOf", ({ data: { objectClass }, objects: { object, instance } }) => {
|
||||
let metaData = jsInterfaceObjects.get(object);
|
||||
if (!metaData || metaData.objectClass != objectClass) {
|
||||
return [false];
|
||||
}
|
||||
return [true, metaData.objectData];
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapiflash.js:getProperty", ({ data: { name }, objects: { object, instance } }) => {
|
||||
try {
|
||||
return [mapValue(object[name], instance), { exception: null }];
|
||||
} catch (e) {
|
||||
return [mapValue(null, instance), { exception: mapValue(e, instance) }];
|
||||
}
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapiflash.js:log", ({ data }) => {
|
||||
containerWindow.console.log(data);
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapiflash.js:setInstancePrototype", ({ objects: { proto } }) => {
|
||||
Object.setPrototypeOf(proto.wrappedJSObject, Object.getPrototypeOf(pluginElement.wrappedJSObject));
|
||||
Object.setPrototypeOf(pluginElement.wrappedJSObject, proto.wrappedJSObject);
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapi.js:isFullscreen", () => {
|
||||
return containerWindow.document.fullscreenElement == pluginElement;
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapi.js:setFullscreen", ({ data }) => {
|
||||
if (data) {
|
||||
pluginElement.requestFullscreen();
|
||||
} else {
|
||||
containerWindow.document.exitFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
mm.loadFrameScript("resource://ppapi.js/ppapi-instance.js", true);
|
|
@ -0,0 +1,335 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "../ppapi/out/rpc.cc"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define REAL_PLUGIN_PATH "/Applications/Google Chrome.app/Contents/Versions/42.0.2311.135/Google Chrome Framework.framework/Internet Plug-Ins/PepperFlash/PepperFlashPlayer.plugin/Contents/MacOS/PepperFlashPlayer"
|
||||
//#define REAL_PLUGIN_PATH "/usr/lib/pepperflashplugin-nonfree/libpepflashplayer.so"
|
||||
|
||||
static PPB_GetInterface _real_PPB_GetInterface;
|
||||
|
||||
const void* RealGetInterface(const char* interfaceName) {
|
||||
if (!strcmp(interfaceName, "PPB_Flash_File_FileRef;2.0")) {
|
||||
interfaceName = "PPB_Flash_File_FileRef;2";
|
||||
} else if (!strcmp(interfaceName, "PPB_Flash_File_ModuleLocal;3.0")) {
|
||||
interfaceName = "PPB_Flash_File_ModuleLocal;3";
|
||||
}
|
||||
return _real_PPB_GetInterface(interfaceName);
|
||||
}
|
||||
const void* GetInterfaceForRPC(const char* interfaceName) {
|
||||
const void* interface = gInterfaces[interfaceName];
|
||||
//printf("GetInterfaceForRPC %s\n", interfaceName);
|
||||
if (!interface) {
|
||||
printf("MISSING INTERFACE %s\n", interfaceName);
|
||||
}
|
||||
return interface;
|
||||
}
|
||||
|
||||
void
|
||||
Fail(const char *reason, const char *data)
|
||||
{
|
||||
fprintf(stdout, reason, data);
|
||||
fflush(stdout);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void Logging_PP_CompletionCallback(void* user_data, int32_t result)
|
||||
{
|
||||
PP_CompletionCallback* _real_PP_CompletionCallback = static_cast<PP_CompletionCallback*>(user_data);
|
||||
printf("callFromJSON: > {\"__callback\":\"PP_CompletionCallback\",\"__callbackStruct\":{\"func\":%lu,\"user_data\":%lu,\"flags\":%i},\"result\":%i}\n",
|
||||
uintptr_t(_real_PP_CompletionCallback->func), uintptr_t(_real_PP_CompletionCallback->user_data), _real_PP_CompletionCallback->flags, result);
|
||||
_real_PP_CompletionCallback->func(_real_PP_CompletionCallback->user_data, result);
|
||||
printf("callFromJSON: < {\"__callback\":\"PP_CompletionCallback\",\"__callbackStruct\":{\"func\":%lu,\"user_data\":%lu,\"flags\":%i},\"result\":%i}\n",
|
||||
uintptr_t(_real_PP_CompletionCallback->func), uintptr_t(_real_PP_CompletionCallback->user_data), _real_PP_CompletionCallback->flags, result);
|
||||
delete _real_PP_CompletionCallback;
|
||||
}
|
||||
|
||||
void Logging_PPB_Audio_Callback_1_0(void* sample_buffer,
|
||||
uint32_t buffer_size_in_bytes,
|
||||
void* user_data)
|
||||
{
|
||||
Logging_PPB_Audio_Callback_1_0_holder* holder = static_cast<Logging_PPB_Audio_Callback_1_0_holder*>(user_data);
|
||||
printf("callFromJSON: > {\"__callback\":\"PPB_Audio_Callback_1_0\",\"__callbackStruct\":{\"func\":%lu,\"sample_buffer\":%lu,\"buffer_size_in_bytes\":%i},\"user_data\":%lu}\n",
|
||||
uintptr_t(holder->func), uintptr_t(sample_buffer), buffer_size_in_bytes, uintptr_t(holder->user_data));
|
||||
holder->func(sample_buffer, buffer_size_in_bytes, holder->user_data);
|
||||
printf("callFromJSON: < {\"__callback\":\"PPB_Audio_Callback_1_0\",\"__callbackStruct\":{\"func\":%lu,\"sample_buffer\":%lu,\"buffer_size_in_bytes\":%i},\"user_data\":%lu}\n",
|
||||
uintptr_t(holder->func), uintptr_t(sample_buffer), buffer_size_in_bytes, uintptr_t(holder->user_data));
|
||||
}
|
||||
|
||||
static void *_real_PepperFlash = nullptr;
|
||||
|
||||
typedef int32_t (*PPP_InitializeBroker_Func)(PP_ConnectInstance_Func* connect_instance_func);
|
||||
typedef void (*PPP_ShutdownBroker_Func)(void);
|
||||
|
||||
static PP_InitializeModule_Func _real_PPP_InitializeModule;
|
||||
static PP_GetInterface_Func _real_PPP_GetInterface;
|
||||
static PPP_InitializeBroker_Func _real_PPP_InitializeBroker;
|
||||
static PPP_ShutdownBroker_Func _real_PPP_ShutdownBroker;
|
||||
|
||||
static void
|
||||
LoadRealPepperFlash()
|
||||
{
|
||||
if (!_real_PepperFlash) {
|
||||
_real_PepperFlash = dlopen(REAL_PLUGIN_PATH, RTLD_LAZY);
|
||||
_real_PPP_InitializeModule = (PP_InitializeModule_Func)dlsym(_real_PepperFlash, "PPP_InitializeModule");
|
||||
_real_PPP_GetInterface = (PP_GetInterface_Func)dlsym(_real_PepperFlash, "PPP_GetInterface");
|
||||
_real_PPP_InitializeBroker = (PPP_InitializeBroker_Func)dlsym(_real_PepperFlash, "PPP_InitializeBroker");
|
||||
_real_PPP_ShutdownBroker = (PPP_ShutdownBroker_Func)dlsym(_real_PepperFlash, "PPP_ShutdownBroker");
|
||||
InitializeInterfaceList();
|
||||
}
|
||||
}
|
||||
|
||||
struct Logging_PPP_Class_Deprecated_holder;
|
||||
|
||||
PP_Bool
|
||||
Logging_HasProperty(const void* object,
|
||||
PP_Var name,
|
||||
PP_Var* exception)
|
||||
{
|
||||
uint32_t varNameLength;
|
||||
const char* varName = ((PPB_Var_Deprecated_0_3*)RealGetInterface("PPB_Var(Deprecated);0.3"))->VarToUtf8(name, &varNameLength);
|
||||
printf("Logging_HasProperty for ");
|
||||
for (uint32_t i = 0; i < varNameLength; ++i) {
|
||||
printf("%c", varName[i]);
|
||||
}
|
||||
printf("\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
PP_Bool result = holder->_real_PPP_Class_Deprecated->HasProperty(holder->object, name, exception);
|
||||
printf("Logging_HasProperty for ");
|
||||
for (uint32_t i = 0; i < varNameLength; ++i) {
|
||||
printf("%c", varName[i]);
|
||||
}
|
||||
printf(" returns %s\n", result == PP_TRUE ? "true" : "false");
|
||||
return result;
|
||||
}
|
||||
|
||||
PP_Bool
|
||||
Logging_HasMethod(const void* object,
|
||||
PP_Var name,
|
||||
PP_Var* exception)
|
||||
{
|
||||
uint32_t varNameLength;
|
||||
const char* varName = ((PPB_Var_Deprecated_0_3*)RealGetInterface("PPB_Var(Deprecated);0.3"))->VarToUtf8(name, &varNameLength);
|
||||
printf("Logging_HasMethod for ");
|
||||
for (uint32_t i = 0; i < varNameLength; ++i) {
|
||||
printf("%c", varName[i]);
|
||||
}
|
||||
printf("\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
PP_Bool result = holder->_real_PPP_Class_Deprecated->HasMethod(holder->object, name, exception);
|
||||
printf("Logging_HasMethod for ");
|
||||
for (uint32_t i = 0; i < varNameLength; ++i) {
|
||||
printf("%c", varName[i]);
|
||||
}
|
||||
printf(" returns %s\n", result == PP_TRUE ? "true" : "false");
|
||||
return result;
|
||||
}
|
||||
|
||||
PP_Var
|
||||
Logging_GetProperty(const void* object,
|
||||
PP_Var name,
|
||||
PP_Var* exception)
|
||||
{
|
||||
printf("Logging_GetProperty\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
return holder->_real_PPP_Class_Deprecated->GetProperty(holder->object, name, exception);
|
||||
}
|
||||
|
||||
void
|
||||
Logging_GetAllPropertyNames(const void* object,
|
||||
uint32_t* property_count,
|
||||
PP_Var** properties,
|
||||
PP_Var* exception)
|
||||
{
|
||||
printf("Logging_GetAllPropertyNames\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
holder->_real_PPP_Class_Deprecated->GetAllPropertyNames(holder->object, property_count, properties, exception);
|
||||
}
|
||||
|
||||
void
|
||||
Logging_SetProperty(const void* object,
|
||||
PP_Var name,
|
||||
PP_Var value,
|
||||
PP_Var* exception)
|
||||
{
|
||||
printf("Logging_SetProperty\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
holder->_real_PPP_Class_Deprecated->SetProperty(holder->object, name, value, exception);
|
||||
}
|
||||
|
||||
void
|
||||
Logging_RemoveProperty(const void* object,
|
||||
PP_Var name,
|
||||
PP_Var* exception)
|
||||
{
|
||||
printf("Logging_RemoveProperty\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
holder->_real_PPP_Class_Deprecated->RemoveProperty(holder->object, name, exception);
|
||||
}
|
||||
|
||||
PP_Var
|
||||
Logging_Call(const void* object,
|
||||
PP_Var method_name,
|
||||
uint32_t argc,
|
||||
const PP_Var argv[],
|
||||
PP_Var* exception)
|
||||
{
|
||||
printf("Logging_Call\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
return holder->_real_PPP_Class_Deprecated->Call(holder->object, method_name, argc, argv, exception);
|
||||
}
|
||||
|
||||
PP_Var
|
||||
Logging_Construct(const void* object,
|
||||
uint32_t argc,
|
||||
const PP_Var argv[],
|
||||
PP_Var* exception)
|
||||
{
|
||||
printf("Logging_Construct\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
return holder->_real_PPP_Class_Deprecated->Construct(holder->object, argc, argv, exception);
|
||||
}
|
||||
|
||||
void
|
||||
Logging_Deallocate(const void* object)
|
||||
{
|
||||
printf("Logging_Deallocate\n");
|
||||
const Logging_PPP_Class_Deprecated_holder* holder = static_cast<const Logging_PPP_Class_Deprecated_holder*>(object);
|
||||
holder->_real_PPP_Class_Deprecated->Deallocate(holder->object);
|
||||
delete holder;
|
||||
}
|
||||
|
||||
const PPP_Class_Deprecated _interpose_PPP_Class_Deprecated_1_0 = {
|
||||
Logging_HasProperty,
|
||||
Logging_HasMethod,
|
||||
Logging_GetProperty,
|
||||
Logging_GetAllPropertyNames,
|
||||
Logging_SetProperty,
|
||||
Logging_RemoveProperty,
|
||||
Logging_Call,
|
||||
Logging_Construct,
|
||||
Logging_Deallocate,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static PP_Bool
|
||||
Logging_HandleInputEvent(PP_Instance instance, PP_Resource input_event)
|
||||
{
|
||||
const PPP_InputEvent_0_1* _real_PPP_InputEvent = static_cast<const PPP_InputEvent_0_1*>(_real_PPP_GetInterface("PPP_InputEvent;0.1"));
|
||||
printf("callFromJSON: > {\"__interface\":\"PPP_InputEvent;0.1\",\"__member\":\"HandleInputEvent\",\"instance\":%i,\"input_event\":%i}\n", instance, input_event);
|
||||
PP_Bool result = _real_PPP_InputEvent->HandleInputEvent(instance, input_event);
|
||||
printf("callFromJSON: < \"%s\"\n", ToString_PP_Bool(result).c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static const PPP_InputEvent_0_1 _interpose_PPP_InputEvent_0_1 = {
|
||||
Logging_HandleInputEvent
|
||||
};
|
||||
|
||||
static PP_Bool
|
||||
Logging_DidCreate(PP_Instance instance,
|
||||
uint32_t argc,
|
||||
const char* argn[],
|
||||
const char* argv[])
|
||||
{
|
||||
const PPP_Instance_1_1* _real_PPP_Instance = static_cast<const PPP_Instance_1_1*>(_real_PPP_GetInterface("PPP_Instance;1.1"));
|
||||
printf("callFromJSON: > {\"__interface\":\"PPP_Instance;1.1\",\"__member\":\"DidCreate\",\"instance\":%i,\"argc\":%i,\"argn\":[", instance, argc);
|
||||
for (uint32_t i = 0; i < argc; ++i) {
|
||||
if (i > 0) {
|
||||
printf(",");
|
||||
}
|
||||
printf("\"%s\"", argn[i]);
|
||||
}
|
||||
printf("],\"argv\":[");
|
||||
for (uint32_t i = 0; i < argc; ++i) {
|
||||
if (i > 0) {
|
||||
printf(",");
|
||||
}
|
||||
printf("\"%s\"", argv[i]);
|
||||
}
|
||||
printf("]}\n");
|
||||
PP_Bool result = _real_PPP_Instance->DidCreate(instance, argc, argn, argv);
|
||||
printf("callFromJSON: < \"%s\"\n", ToString_PP_Bool(result).c_str());
|
||||
return result;
|
||||
}
|
||||
static void
|
||||
Logging_DidDestroy(PP_Instance instance)
|
||||
{
|
||||
const PPP_Instance_1_1* _real_PPP_Instance = static_cast<const PPP_Instance_1_1*>(_real_PPP_GetInterface("PPP_Instance;1.1"));
|
||||
printf("callFromJSON: > {\"__interface\":\"PPP_Instance;1.1\",\"__member\":\"DidDestroy\",\"instance\":%i}\n", instance);
|
||||
_real_PPP_Instance->DidDestroy(instance);
|
||||
}
|
||||
static void
|
||||
Logging_DidChangeView(PP_Instance instance, PP_Resource view)
|
||||
{
|
||||
const PPP_Instance_1_1* _real_PPP_Instance = static_cast<const PPP_Instance_1_1*>(_real_PPP_GetInterface("PPP_Instance;1.1"));
|
||||
printf("callFromJSON: > {\"__interface\":\"PPP_Instance;1.1\",\"__member\":\"DidChangeView\",\"instance\":%i,\"view\":%i}\n", instance, view);
|
||||
_real_PPP_Instance->DidChangeView(instance, view);
|
||||
}
|
||||
static void
|
||||
Logging_DidChangeFocus(PP_Instance instance, PP_Bool has_focus)
|
||||
{
|
||||
const PPP_Instance_1_1* _real_PPP_Instance = static_cast<const PPP_Instance_1_1*>(_real_PPP_GetInterface("PPP_Instance;1.1"));
|
||||
printf("callFromJSON: > {\"__interface\":\"PPP_Instance;1.1\",\"__member\":\"DidChangeFocus\",\"instance\":%i,\"has_focus\":%s}\n", instance, has_focus ? "PP_TRUE" : "PP_FALSE");
|
||||
_real_PPP_Instance->DidChangeFocus(instance, has_focus);
|
||||
}
|
||||
static PP_Bool
|
||||
Logging_HandleDocumentLoad(PP_Instance instance, PP_Resource url_loader)
|
||||
{
|
||||
const PPP_Instance_1_1* _real_PPP_Instance = static_cast<const PPP_Instance_1_1*>(_real_PPP_GetInterface("PPP_Instance;1.1"));
|
||||
printf("callFromJSON: > {\"__interface\":\"PPP_Instance;1.1\",\"__member\":\"HandleDocumentLoad\",\"instance\":%i,\"url_loader\":%i}\n", instance, url_loader);
|
||||
PP_Bool result = _real_PPP_Instance->HandleDocumentLoad(instance, url_loader);
|
||||
printf("callFromJSON: < \"%s\"\n", ToString_PP_Bool(result).c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static const PPP_Instance_1_1 _interpose_PPP_Instance_1_1 = {
|
||||
Logging_DidCreate,
|
||||
Logging_DidDestroy,
|
||||
Logging_DidChangeView,
|
||||
Logging_DidChangeFocus,
|
||||
Logging_HandleDocumentLoad
|
||||
};
|
||||
|
||||
const void *
|
||||
PPP_GetInterface(const char *interface_name)
|
||||
{
|
||||
//printf("PPP_GetInterface %s\n", interface_name);
|
||||
LoadRealPepperFlash();
|
||||
if (!strcmp(interface_name, "PPP_InputEvent;0.1")) {
|
||||
return &_interpose_PPP_InputEvent_0_1;
|
||||
}
|
||||
if (!strcmp(interface_name, "PPP_Instance;1.1")) {
|
||||
return &_interpose_PPP_Instance_1_1;
|
||||
}
|
||||
return _real_PPP_GetInterface(interface_name);
|
||||
}
|
||||
|
||||
int32_t
|
||||
PPP_InitializeModule(PP_Module module, PPB_GetInterface get_browser_interface)
|
||||
{
|
||||
LoadRealPepperFlash();
|
||||
_real_PPB_GetInterface = get_browser_interface;
|
||||
return _real_PPP_InitializeModule(module, GetInterfaceForRPC);
|
||||
}
|
||||
|
||||
int32_t
|
||||
PPP_InitializeBroker(PP_ConnectInstance_Func *connect_instance_func)
|
||||
{
|
||||
return _real_PPP_InitializeBroker(connect_instance_func);
|
||||
}
|
||||
|
||||
void
|
||||
PPP_ShutdownBroker()
|
||||
{
|
||||
return _real_PPP_ShutdownBroker();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
|
@ -0,0 +1,69 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function sandboxScript(sandbox)
|
||||
{
|
||||
dump("sandboxScript " + sandbox.pluginElement + "\n");
|
||||
Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader).loadSubScript("resource://ppapipdf.js/ppapi-content-sandbox.js", sandbox);
|
||||
}
|
||||
|
||||
let plugins;
|
||||
function startup(data) {
|
||||
dump(">>>STARTED!!!\n");
|
||||
|
||||
let root = data.installPath.parent.parent;
|
||||
let rpclib = root.clone();
|
||||
let pluginlib = root.clone();
|
||||
let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
|
||||
if (os == "Darwin") {
|
||||
rpclib.appendRelativePath("ppapi/out/rpc.dylib");
|
||||
pluginlib.appendRelativePath("plugin/libpepperpdfium.dylib");
|
||||
} else if (os == "Linux") {
|
||||
rpclib.appendRelativePath("ppapi/out/rpc.so");
|
||||
pluginlib.appendRelativePath("plugin/libpepperpdfium.so");
|
||||
} else if (os == "WINNT") {
|
||||
rpclib.appendRelativePath("ppapi\\out\\rpc.dll");
|
||||
pluginlib.appendRelativePath("plugin\\pepperpdfium.dll");
|
||||
} else {
|
||||
throw("Don't know the path to the libraries for this OS!");
|
||||
}
|
||||
rpclib = rpclib.path;
|
||||
pluginlib = pluginlib.path;
|
||||
|
||||
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
|
||||
let plugin = pluginHost.registerFakePlugin({
|
||||
handlerURI: "chrome://ppapipdf.js/content/viewer.html",
|
||||
mimeEntries: [
|
||||
{ type: "application/pdf", extension: "pdf" },
|
||||
{ type: "application/vnd.adobe.pdf", extension: "pdf" },
|
||||
{ type: "application/vnd.adobe.pdfxml", extension: "pdfxml" },
|
||||
{ type: "application/vnd.adobe.x-mars", extension: "mars" },
|
||||
{ type: "application/vnd.adobe.xdp+xml", extension: "xdp" },
|
||||
{ type: "application/vnd.adobe.xfdf", extension: "xfdf" },
|
||||
{ type: "application/vnd.adobe.xfd+xml", extension: "xfd" },
|
||||
{ type: "application/vnd.fdf", extension: "fdf" },
|
||||
],
|
||||
name: "PPAPI PDF plugin",
|
||||
niceName: "PPAPI PDF plugin",
|
||||
version: "1.0",
|
||||
sandboxScript : `(${sandboxScript.toSource()})(this);`,
|
||||
ppapiProcessArgs: [ rpclib, pluginlib ],
|
||||
});
|
||||
plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
|
||||
|
||||
let rng = Cc["@mozilla.org/security/random-generator;1"].createInstance(Ci.nsIRandomGenerator);
|
||||
Services.ppmm.addMessageListener("ppapi.js:generateRandomBytes", ({ data }) => {
|
||||
return rng.generateRandomBytes(data);
|
||||
});
|
||||
|
||||
dump("<<<STARTED!!!\n");
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
dump("SHUTDOWN!!!\n");
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
resource ppapipdf.js .
|
||||
content ppapipdf.js chrome/
|
||||
resource ppapi.js ../common/
|
|
@ -0,0 +1,239 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
class PolyfillDropdown {
|
||||
constructor(select) {
|
||||
this._select = select;
|
||||
this._currentValue = select.value;
|
||||
|
||||
this._visible = false;
|
||||
this._scrollbarWidth = 0;
|
||||
|
||||
this._options = [];
|
||||
this._onChangedHandler = [];
|
||||
|
||||
this._polyfill = document.createElement('div');
|
||||
this._polyfill.classList.add('polyfillDropdown');
|
||||
this._polyfill.classList.add('hidden');
|
||||
|
||||
for (let elem of this._select.getElementsByTagName('option')) {
|
||||
this._options.push(elem.value);
|
||||
|
||||
if (!elem.hidden) {
|
||||
let option = document.createElement('div');
|
||||
option.classList.add('polyfillOption');
|
||||
option.tabIndex = -1;
|
||||
option.dataset.value = elem.value;
|
||||
document.l10n.setAttributes(option,
|
||||
elem.dataset.l10nId, JSON.parse(elem.dataset.l10nArgs || '{}'));
|
||||
option.addEventListener('click',
|
||||
this._onOptionClicked.bind(this, elem.value));
|
||||
this._polyfill.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
document.body.appendChild(this._polyfill);
|
||||
|
||||
let dropdownStyle = getComputedStyle(this._polyfill);
|
||||
this._defaultDropdownBorderWidth =
|
||||
parseInt(dropdownStyle.getPropertyValue('border-left-width'), 10) +
|
||||
parseInt(dropdownStyle.getPropertyValue('border-right-width'), 10);
|
||||
|
||||
let optionStyle = getComputedStyle(this._polyfill.childNodes[0]);
|
||||
this._defaultOptionPaddingRight =
|
||||
parseInt(optionStyle.getPropertyValue('padding-right'), 10);
|
||||
|
||||
this._select.addEventListener('click', this);
|
||||
this._select.addEventListener('change', this);
|
||||
this._select.addEventListener('blur', this);
|
||||
this._select.addEventListener('keydown', this);
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this._select.value;
|
||||
}
|
||||
|
||||
set value(value) {
|
||||
this._currentValue = value;
|
||||
this._select.value = value;
|
||||
}
|
||||
|
||||
_show() {
|
||||
if (this._visible) {
|
||||
return;
|
||||
}
|
||||
this._visible = true;
|
||||
this._updateOptionStatus();
|
||||
this._polyfill.classList.remove('hidden');
|
||||
this._resize();
|
||||
|
||||
window.addEventListener('resize', this);
|
||||
}
|
||||
|
||||
_hide() {
|
||||
if (!this._visible) {
|
||||
return;
|
||||
}
|
||||
window.removeEventListener('resize', this);
|
||||
this._polyfill.classList.add('hidden');
|
||||
this._visible = false;
|
||||
|
||||
if (this._select.value !== this._currentValue) {
|
||||
this._triggerChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
_updateOptionStatus() {
|
||||
let currentValue = this._select.value;
|
||||
for (let elem of this._polyfill.childNodes) {
|
||||
elem.classList.toggle('toggled', elem.dataset.value === currentValue);
|
||||
};
|
||||
}
|
||||
|
||||
_resize() {
|
||||
let rect = this._select.getBoundingClientRect();
|
||||
this._polyfill.style.left = rect.left + 'px';
|
||||
this._polyfill.style.top = rect.bottom + 'px';
|
||||
|
||||
let scrollbarWidth = this._polyfill.offsetWidth -
|
||||
this._polyfill.clientWidth - this._defaultDropdownBorderWidth;
|
||||
|
||||
if (scrollbarWidth != this._scrollbarWidth) {
|
||||
this._scrollbarWidth = scrollbarWidth;
|
||||
Array.from(this._polyfill.getElementsByClassName('polyfillOption'))
|
||||
.forEach(option => {
|
||||
if (scrollbarWidth > 0) {
|
||||
option.style.paddingRight =
|
||||
(scrollbarWidth + this._defaultOptionPaddingRight) + 'px';
|
||||
} else {
|
||||
option.style.paddingRight = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_onOptionClicked(value) {
|
||||
this._select.value = value;
|
||||
this._hide();
|
||||
}
|
||||
|
||||
_triggerChangeEvent() {
|
||||
this._currentValue = this._select.value;
|
||||
|
||||
let event = {
|
||||
type: 'change',
|
||||
target: this._select
|
||||
};
|
||||
|
||||
this._onChangedHandler.forEach(handler => {
|
||||
if (typeof handler === 'function') {
|
||||
handler.call(this._select, event);
|
||||
} else if (typeof handler === 'object' &&
|
||||
typeof handler.handleEvent === 'function') {
|
||||
handler.handleEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener(type, listener, useCapture) {
|
||||
if (type == 'change') {
|
||||
this._onChangedHandler.push(listener);
|
||||
} else {
|
||||
this._select.addEventListener(type, listener, useCapture);
|
||||
}
|
||||
}
|
||||
|
||||
querySelectorAll(selectors) {
|
||||
return this._select.querySelectorAll(selectors);
|
||||
}
|
||||
|
||||
handleEvent(evt) {
|
||||
let handled = false;
|
||||
|
||||
switch(evt.type) {
|
||||
case 'click':
|
||||
if (this._visible) {
|
||||
this._hide();
|
||||
} else {
|
||||
this._show();
|
||||
}
|
||||
handled = true;
|
||||
break;
|
||||
case 'change':
|
||||
if (this._visible) {
|
||||
this._updateOptionStatus();
|
||||
} else if (this._select.value !== this._currentValue) {
|
||||
this._triggerChangeEvent();
|
||||
}
|
||||
break;
|
||||
case 'blur':
|
||||
setTimeout(() => {
|
||||
if (!this._polyfill.contains(document.activeElement)) {
|
||||
this._hide();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'resize':
|
||||
this._resize();
|
||||
break;
|
||||
case 'keydown':
|
||||
let isMac = navigator.platform.startsWith('Mac');
|
||||
|
||||
if (this._visible) {
|
||||
let index;
|
||||
|
||||
switch(evt.keyCode) {
|
||||
case KeyEvent.DOM_VK_ESCAPE:
|
||||
case KeyEvent.DOM_VK_RETURN:
|
||||
this._hide();
|
||||
handled = true;
|
||||
break;
|
||||
case KeyEvent.DOM_VK_DOWN:
|
||||
if (isMac) {
|
||||
index = this._options.indexOf(this._select.value);
|
||||
if (index < this._options.length - 1) {
|
||||
this._select.value = this._options[index + 1];
|
||||
this._updateOptionStatus();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
case KeyEvent.DOM_VK_UP:
|
||||
if (isMac) {
|
||||
index = this._options.indexOf(this._select.value);
|
||||
if (index > 0) {
|
||||
this._select.value = this._options[index - 1];
|
||||
this._updateOptionStatus();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (document.activeElement == this._select) {
|
||||
switch(evt.keyCode) {
|
||||
case KeyEvent.DOM_VK_SPACE:
|
||||
this._show();
|
||||
handled = true;
|
||||
break;
|
||||
case KeyEvent.DOM_VK_DOWN:
|
||||
case KeyEvent.DOM_VK_UP:
|
||||
if (isMac) {
|
||||
this._show();
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
class ProgressBar {
|
||||
constructor() {
|
||||
this._container = document.getElementById('loadingBar');
|
||||
this._percentage = this._container.querySelector('.progress');
|
||||
}
|
||||
|
||||
show() {
|
||||
this._container.classList.remove('hidden');
|
||||
}
|
||||
|
||||
hide(waitForTransition) {
|
||||
let percentage = this._percentage;
|
||||
let doHide = () => {
|
||||
this._container.classList.add('hidden');
|
||||
percentage.style.width = '';
|
||||
};
|
||||
if (!waitForTransition) {
|
||||
doHide();
|
||||
} else {
|
||||
percentage.addEventListener('transitionend', function handler() {
|
||||
percentage.removeEventListener('transitionend', handler);
|
||||
doHide();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setProgress(progress) {
|
||||
if (isNaN(progress) || progress < 0 || progress > 100) {
|
||||
this._percentage.classList.add('indeterminate');
|
||||
return;
|
||||
}
|
||||
this._percentage.classList.remove('indeterminate');
|
||||
this._percentage.style.width = Math.round(progress) + '%';
|
||||
}
|
||||
}
|
||||
|
||||
class SecondaryToolbar {
|
||||
constructor() {
|
||||
this._container = document.getElementById('secondaryToolbar');
|
||||
this._toggleButton = document.getElementById('secondaryToolbarToggle');
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this._toggleButton.classList.toggle('toggled');
|
||||
this._container.classList.toggle('hidden');
|
||||
if (this._container.classList.contains('hidden')) {
|
||||
window.removeEventListener('click', this);
|
||||
} else {
|
||||
window.addEventListener('click', this);
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(evt) {
|
||||
// Hide the secondary toolbar automatically when a user is not clicking the
|
||||
// toggle button. (The handler of the toggle button will take care of hiding
|
||||
// it instead.)
|
||||
if (evt.target != this._toggleButton) {
|
||||
this.toggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScaleSelect {
|
||||
constructor(viewport) {
|
||||
this._viewport = viewport;
|
||||
|
||||
this._select = new PolyfillDropdown(document.getElementById('scaleSelect'));
|
||||
this._select.addEventListener('change', this);
|
||||
|
||||
this._customScaleOption = document.getElementById('customScaleOption');
|
||||
this._customScale = 0;
|
||||
|
||||
this._predefinedOption =
|
||||
Array.from(this._select.querySelectorAll('option:not([hidden])'))
|
||||
.map(elem => elem.value);
|
||||
}
|
||||
|
||||
setScale(scale) {
|
||||
if (this._predefinedOption.includes(String(scale))) {
|
||||
this._customScale = 0;
|
||||
this._select.value = scale;
|
||||
} else if (!isNaN(scale)) {
|
||||
let customScale = Math.round(scale * 10000) / 100;
|
||||
if (customScale != this._customScale) {
|
||||
this._customScale = customScale;
|
||||
document.l10n.formatValue('page_scale_percent', {scale: customScale})
|
||||
.then(result => {
|
||||
this._select.value = '';
|
||||
this._customScaleOption.textContent = result;
|
||||
this._select.value = 'custom';
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(evt) {
|
||||
switch(evt.type) {
|
||||
case 'change':
|
||||
let scale = this._select.value;
|
||||
if (!isNaN(scale)) {
|
||||
// "viewport.fitting" will be set to "none" automatically once
|
||||
// assigning a new "zoom".
|
||||
this._viewport.zoom = scale;
|
||||
} else if (scale != 'custom') {
|
||||
this._viewport.fitting = scale;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Toolbar {
|
||||
constructor(viewport) {
|
||||
let elements = [
|
||||
'zoomIn', 'zoomOut', 'previous', 'next', 'pageNumber', 'numPages',
|
||||
'firstPage', 'lastPage', 'pageRotateCw', 'pageRotateCcw'
|
||||
];
|
||||
|
||||
this.ZOOM_FACTORS = [
|
||||
0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.3, 1.5, 1.7, 1.9, 2.1,
|
||||
2.4, 2.7, 3, 3.3, 3.7, 4.1, 4.6, 5.1, 5.7, 6.3, 7, 7.7, 8.5, 9.4, 10
|
||||
];
|
||||
|
||||
this._viewport = viewport;
|
||||
this._secondaryToolbar = new SecondaryToolbar();
|
||||
this._loadingBar = new ProgressBar();
|
||||
this._scaleSelect = new ScaleSelect(viewport);
|
||||
|
||||
this._elements = {};
|
||||
elements.forEach(id => {
|
||||
this._elements[id] = document.getElementById(id);
|
||||
});
|
||||
|
||||
let toolbarButtons =
|
||||
document.querySelectorAll('.toolbarButton, .secondaryToolbarButton');
|
||||
for (let button of toolbarButtons) {
|
||||
button.addEventListener('click', this);
|
||||
}
|
||||
|
||||
this._elements.pageNumber.addEventListener('focus', this);
|
||||
this._elements.pageNumber.addEventListener('change', this);
|
||||
|
||||
this._viewport.onDimensionChanged = this._updateDimensions.bind(this);
|
||||
this._viewport.onProgressChanged = this._updateProgress.bind(this);
|
||||
this._viewport.onZoomChanged = this._updateToolbar.bind(this);
|
||||
this._viewport.onPageChanged = this._updateToolbar.bind(this);
|
||||
|
||||
this._updateToolbar();
|
||||
}
|
||||
|
||||
_zoomIn() {
|
||||
let zoom = this._viewport.zoom;
|
||||
let newZoom = this.ZOOM_FACTORS.find(factor => (factor - zoom) > 0.049);
|
||||
if (!newZoom) {
|
||||
newZoom = this.ZOOM_FACTORS[this.ZOOM_FACTORS.length - 1];
|
||||
}
|
||||
this._viewport.zoom = newZoom;
|
||||
}
|
||||
|
||||
_zoomOut() {
|
||||
let reversedFactors = Array.from(this.ZOOM_FACTORS);
|
||||
reversedFactors.reverse();
|
||||
let zoom = this._viewport.zoom;
|
||||
let newZoom = reversedFactors.find(factor => (zoom - factor) > 0.049);
|
||||
if (!newZoom) {
|
||||
newZoom = this.ZOOM_FACTORS[0];
|
||||
}
|
||||
this._viewport.zoom = newZoom;
|
||||
}
|
||||
|
||||
_buttonClicked(id) {
|
||||
switch(id) {
|
||||
case 'firstPage':
|
||||
this._viewport.page = 0;
|
||||
break;
|
||||
case 'lastPage':
|
||||
this._viewport.page = this._viewport.pageCount - 1;
|
||||
break;
|
||||
case 'previous':
|
||||
this._viewport.page--;
|
||||
break;
|
||||
case 'next':
|
||||
this._viewport.page++;
|
||||
break;
|
||||
case 'zoomIn':
|
||||
this._zoomIn();
|
||||
break;
|
||||
case 'zoomOut':
|
||||
this._zoomOut();
|
||||
break;
|
||||
case 'pageRotateCw':
|
||||
this._viewport.rotateClockwise();
|
||||
break;
|
||||
case 'pageRotateCcw':
|
||||
this._viewport.rotateCounterClockwise();
|
||||
break;
|
||||
case 'secondaryToolbarToggle':
|
||||
this._secondaryToolbar.toggle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_pageNumberChanged() {
|
||||
let newPage = parseFloat(this._elements.pageNumber.value);
|
||||
if (!Number.isInteger(newPage) ||
|
||||
newPage < 1 || newPage > this._viewport.pageCount) {
|
||||
this._elements.pageNumber.value = this._viewport.page + 1;
|
||||
return;
|
||||
}
|
||||
this._elements.pageNumber.value = newPage;
|
||||
this._viewport.page = newPage - 1;
|
||||
}
|
||||
|
||||
_updateDimensions() {
|
||||
let pageCount = this._viewport.pageCount;
|
||||
document.l10n.formatValue('page_of', { pageCount })
|
||||
.then(page_of => this._elements.numPages.textContent = page_of);
|
||||
this._elements.pageNumber.max = pageCount;
|
||||
this._updateToolbar();
|
||||
}
|
||||
|
||||
_updateToolbar() {
|
||||
let page = this._viewport.page + 1;
|
||||
let pageCount = this._viewport.pageCount;
|
||||
|
||||
this._elements.pageNumber.disabled =
|
||||
this._elements.pageRotateCw.disabled =
|
||||
this._elements.pageRotateCcw.disabled = (pageCount == 0);
|
||||
|
||||
this._elements.pageNumber.value = pageCount ? page : '';
|
||||
this._elements.previous.disabled =
|
||||
this._elements.firstPage.disabled = (!pageCount || page == 1);
|
||||
this._elements.next.disabled =
|
||||
this._elements.lastPage.disabled = (!pageCount || page == pageCount);
|
||||
|
||||
let zoom = this._viewport.zoom;
|
||||
this._elements.zoomIn.disabled =
|
||||
(zoom >= this.ZOOM_FACTORS[this.ZOOM_FACTORS.length - 1]);
|
||||
this._elements.zoomOut.disabled = (zoom <= this.ZOOM_FACTORS[0]);
|
||||
|
||||
let fitting = this._viewport.fitting;
|
||||
this._scaleSelect.setScale(fitting == 'none' ? zoom : fitting);
|
||||
}
|
||||
|
||||
_updateProgress(progress) {
|
||||
this._loadingBar.show();
|
||||
this._loadingBar.setProgress(progress);
|
||||
|
||||
if (progress == 100) {
|
||||
this._loadingBar.hide(true);
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(evt) {
|
||||
let target = evt.target;
|
||||
|
||||
switch(evt.type) {
|
||||
case 'change':
|
||||
if (target === this._elements.pageNumber) {
|
||||
this._pageNumberChanged();
|
||||
}
|
||||
break;
|
||||
case 'focus':
|
||||
if (target === this._elements.pageNumber) {
|
||||
target.select();
|
||||
}
|
||||
break;
|
||||
case 'click':
|
||||
this._buttonClicked(target.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
let viewport = new Viewport();
|
||||
let toolbar = new Toolbar(viewport);
|
||||
|
||||
// Expose the custom viewport object to runtime
|
||||
window.createCustomViewport = function(actionHandler) {
|
||||
viewport.registerActionHandler(actionHandler);
|
||||
|
||||
return {
|
||||
addView: viewport.addView.bind(viewport),
|
||||
clearView: viewport.clearView.bind(viewport),
|
||||
getBoundingClientRect: viewport.getBoundingClientRect.bind(viewport),
|
||||
is: viewport.is.bind(viewport),
|
||||
bindUIEvent: viewport.bindUIEvent.bind(viewport),
|
||||
unbindUIEvent: viewport.unbindUIEvent.bind(viewport),
|
||||
setCursor: viewport.setCursor.bind(viewport),
|
||||
getScrollOffset: viewport.getScrollOffset.bind(viewport),
|
||||
notify: viewport.notify.bind(viewport)
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,501 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
class Viewport {
|
||||
constructor() {
|
||||
this._canvasContainer = document.getElementById('canvasContainer');
|
||||
this._viewportController = document.getElementById('viewportController');
|
||||
this._sizer = document.getElementById('sizer');
|
||||
|
||||
this._scrollbarWidth = this._getScrollbarWidth();
|
||||
this._hasVisibleScrollbars = {
|
||||
horizontal: false,
|
||||
vertical: false
|
||||
};
|
||||
|
||||
this._page = 0;
|
||||
this._zoom = 1;
|
||||
this._fitting = 'auto';
|
||||
|
||||
// Caches the next position set during a series of actions and will be set
|
||||
// to scrollbar's position until calling "_refresh".
|
||||
this._nextPosition = null;
|
||||
|
||||
// Indicates the last position that runtime knows. Will update runtime only
|
||||
// if "scroll" event gives us a different value.
|
||||
this._runtimePosition = this.getScrollOffset();
|
||||
|
||||
// Similar to above. Will notify runtime only if "_notifyRuntimeOfResized()"
|
||||
// gets a different value.
|
||||
this._runtimeSize = this.getBoundingClientRect();
|
||||
this._runtimeOnResizedListener = [];
|
||||
|
||||
this.onProgressChanged = null;
|
||||
this.onZoomChanged = null;
|
||||
this.onDimensionChanged = null;
|
||||
this.onPageChanged = null;
|
||||
|
||||
this._viewportController.addEventListener('scroll', this);
|
||||
window.addEventListener('resize', this);
|
||||
}
|
||||
|
||||
get zoom() {
|
||||
return this._zoom;
|
||||
}
|
||||
|
||||
set zoom(newZoom) {
|
||||
newZoom = parseFloat(newZoom);
|
||||
if (!isNaN(newZoom) &&
|
||||
(newZoom != this._zoom || this._fitting != 'none')) {
|
||||
this._fitting = 'none';
|
||||
this._setZoom(newZoom);
|
||||
this._refresh();
|
||||
}
|
||||
}
|
||||
|
||||
get fitting() {
|
||||
return this._fitting;
|
||||
}
|
||||
|
||||
set fitting(newFitting) {
|
||||
let VALID_VALUE = ['none', 'auto', 'page-actual', 'page-width', 'page-fit'];
|
||||
if (!VALID_VALUE.includes(newFitting)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newFitting != this._fitting) {
|
||||
this._fitting = newFitting;
|
||||
this._setZoom(this._computeFittingZoom());
|
||||
this._refresh();
|
||||
}
|
||||
}
|
||||
|
||||
get pageCount() {
|
||||
if (!this._pageDimensions) {
|
||||
return 0;
|
||||
}
|
||||
return this._pageDimensions.length;
|
||||
}
|
||||
|
||||
get page() {
|
||||
return this._page;
|
||||
}
|
||||
|
||||
set page(newPage) {
|
||||
newPage = parseFloat(newPage);
|
||||
|
||||
let pageCount = this.pageCount;
|
||||
if (!pageCount || !Number.isInteger(newPage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
newPage = Math.max(0, Math.min(pageCount - 1, newPage));
|
||||
this._setPage(newPage);
|
||||
this._refresh();
|
||||
}
|
||||
|
||||
_getScrollbarWidth() {
|
||||
var div = document.createElement('div');
|
||||
div.style.visibility = 'hidden';
|
||||
div.style.overflow = 'scroll';
|
||||
div.style.width = '50px';
|
||||
div.style.height = '50px';
|
||||
div.style.position = 'absolute';
|
||||
document.body.appendChild(div);
|
||||
var result = div.offsetWidth - div.clientWidth;
|
||||
div.remove();
|
||||
return result;
|
||||
}
|
||||
|
||||
_documentHasVisibleScrollbars(zoom) {
|
||||
let zoomedDimensions = this._getZoomedDocumentDimensions(zoom);
|
||||
if (!zoomedDimensions || !this._scrollbarWidth) {
|
||||
return {
|
||||
horizontal: false,
|
||||
vertical: false
|
||||
};
|
||||
}
|
||||
|
||||
let outerRect = this._viewportController.getBoundingClientRect();
|
||||
if (zoomedDimensions.width > outerRect.width) {
|
||||
zoomedDimensions.height += this._scrollbarWidth;
|
||||
} else if (zoomedDimensions.height > outerRect.height) {
|
||||
zoomedDimensions.width += this._scrollbarWidth;
|
||||
}
|
||||
|
||||
return {
|
||||
horizontal: zoomedDimensions.width > outerRect.width,
|
||||
vertical: zoomedDimensions.height > outerRect.height
|
||||
};
|
||||
}
|
||||
|
||||
_updateProgress(progress) {
|
||||
if (typeof this.onProgressChanged === 'function') {
|
||||
this.onProgressChanged(progress);
|
||||
}
|
||||
}
|
||||
|
||||
_setDocumentDimensions(documentDimensions) {
|
||||
this._documentDimensions = documentDimensions;
|
||||
this._pageDimensions = documentDimensions.pageDimensions;
|
||||
this._setZoom(this._computeFittingZoom());
|
||||
this._setPage(this._page);
|
||||
if (typeof this.onDimensionChanged === 'function') {
|
||||
this.onDimensionChanged();
|
||||
}
|
||||
this._refresh();
|
||||
}
|
||||
|
||||
_computeFittingZoom() {
|
||||
let newZoom = this._zoom;
|
||||
let fitting = this._fitting;
|
||||
|
||||
if (fitting == 'none') {
|
||||
return newZoom;
|
||||
}
|
||||
|
||||
let FITTING_PADDING = 40;
|
||||
let MAX_AUTO_ZOOM = 1.25;
|
||||
|
||||
let page = this._pageDimensions[this._page];
|
||||
let viewportRect = this.getBoundingClientRect();
|
||||
|
||||
let pageWidthZoom = (viewportRect.width - FITTING_PADDING) / page.width;
|
||||
let pageHeightZoom = viewportRect.height / page.height;
|
||||
|
||||
switch (fitting) {
|
||||
case 'auto':
|
||||
let isLandscape = (page.width > page.height);
|
||||
// For pages in landscape mode, fit the page height to the viewer
|
||||
// *unless* the page would thus become too wide to fit horizontally.
|
||||
let horizontalZoom = isLandscape ?
|
||||
Math.min(pageWidthZoom, pageHeightZoom) : pageWidthZoom;
|
||||
newZoom = Math.min(MAX_AUTO_ZOOM, horizontalZoom);
|
||||
break;
|
||||
case 'page-actual':
|
||||
newZoom = 1;
|
||||
break;
|
||||
case 'page-width':
|
||||
newZoom = pageWidthZoom;
|
||||
break;
|
||||
case 'page-fit':
|
||||
newZoom = Math.min(pageWidthZoom, pageHeightZoom);
|
||||
break;
|
||||
}
|
||||
|
||||
return newZoom;
|
||||
}
|
||||
|
||||
_getMostVisiblePage() {
|
||||
let firstVisiblePage = 0;
|
||||
|
||||
let pageCount = this.pageCount;
|
||||
if (!pageCount) {
|
||||
return firstVisiblePage;
|
||||
}
|
||||
|
||||
let position = this.getScrollOffset();
|
||||
|
||||
let min = 0;
|
||||
let max = pageCount - 1;
|
||||
let y = position.y / this._zoom;
|
||||
|
||||
while (max >= min) {
|
||||
let page = Math.floor(min + ((max - min) / 2));
|
||||
// There might be a gap between the pages, in which case use the bottom
|
||||
// of the previous page as the top for finding the page.
|
||||
let top = 0;
|
||||
if (page > 0) {
|
||||
top = this._pageDimensions[page - 1].y +
|
||||
this._pageDimensions[page - 1].height;
|
||||
}
|
||||
let bottom = this._pageDimensions[page].y +
|
||||
this._pageDimensions[page].height;
|
||||
|
||||
if (top <= y && bottom > y) {
|
||||
firstVisiblePage = page;
|
||||
break;
|
||||
} else if (top > y) {
|
||||
max = page - 1;
|
||||
} else {
|
||||
min = page + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstVisiblePage == pageCount - 1) {
|
||||
return firstVisiblePage;
|
||||
}
|
||||
|
||||
let rect = this.getBoundingClientRect();
|
||||
let viewportRect = {
|
||||
x: position.x / this._zoom,
|
||||
y: position.y / this._zoom,
|
||||
width: rect.width / this._zoom,
|
||||
height: rect.height / this._zoom
|
||||
};
|
||||
|
||||
let getVisibleHeightRatio = rect => {
|
||||
let intersectionHeight =
|
||||
Math.min(rect.y + rect.height, viewportRect.y + viewportRect.height) -
|
||||
Math.max(rect.y, viewportRect.y);
|
||||
return Math.max(0, intersectionHeight) / rect.height;
|
||||
};
|
||||
|
||||
let firstVisiblePageVisibility =
|
||||
getVisibleHeightRatio(this._pageDimensions[firstVisiblePage]);
|
||||
let nextPageVisibility =
|
||||
getVisibleHeightRatio(this._pageDimensions[firstVisiblePage + 1]);
|
||||
|
||||
if (nextPageVisibility > firstVisiblePageVisibility) {
|
||||
return firstVisiblePage + 1;
|
||||
}
|
||||
|
||||
return firstVisiblePage;
|
||||
}
|
||||
|
||||
_getZoomedDocumentDimensions(zoom) {
|
||||
if (!this._documentDimensions) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
width: Math.round(this._documentDimensions.width * zoom),
|
||||
height: Math.round(this._documentDimensions.height * zoom)
|
||||
};
|
||||
}
|
||||
|
||||
_setPosition(x, y) {
|
||||
this._nextPosition = {x, y};
|
||||
}
|
||||
|
||||
_setZoom(newZoom) {
|
||||
let currentPos = this._nextPosition || this.getScrollOffset();
|
||||
currentPos.x /= this._zoom;
|
||||
currentPos.y /= this._zoom;
|
||||
this._zoom = newZoom;
|
||||
this._contentSizeChanged();
|
||||
this._setPosition(
|
||||
currentPos.x * newZoom,
|
||||
currentPos.y * newZoom
|
||||
);
|
||||
if (typeof this.onZoomChanged === 'function') {
|
||||
this.onZoomChanged(this._zoom);
|
||||
}
|
||||
}
|
||||
|
||||
_setPage(newPage) {
|
||||
if (newPage < 0 || newPage >= this.pageCount) {
|
||||
return;
|
||||
}
|
||||
this._setPosition(
|
||||
this._pageDimensions[newPage].x * this._zoom,
|
||||
this._pageDimensions[newPage].y * this._zoom
|
||||
);
|
||||
}
|
||||
|
||||
_updateCanvasSize() {
|
||||
let hasScrollbars = this._documentHasVisibleScrollbars(this._zoom);
|
||||
if (hasScrollbars.horizontal == this._hasVisibleScrollbars.horizontal &&
|
||||
hasScrollbars.vertical == this._hasVisibleScrollbars.vertical) {
|
||||
return;
|
||||
}
|
||||
this._hasVisibleScrollbars = hasScrollbars;
|
||||
this._canvasContainer.style.bottom =
|
||||
hasScrollbars.horizontal ? this._scrollbarWidth + 'px' : 0;
|
||||
this._canvasContainer.style.right =
|
||||
hasScrollbars.vertical ? this._scrollbarWidth + 'px' : 0;
|
||||
this._notifyRuntimeOfResized();
|
||||
}
|
||||
|
||||
_contentSizeChanged() {
|
||||
let zoomedDimensions = this._getZoomedDocumentDimensions(this._zoom);
|
||||
if (zoomedDimensions) {
|
||||
this._sizer.style.width = zoomedDimensions.width + 'px';
|
||||
this._sizer.style.height = zoomedDimensions.height + 'px';
|
||||
this._updateCanvasSize();
|
||||
}
|
||||
}
|
||||
|
||||
_resize() {
|
||||
let newZoom = this._computeFittingZoom();
|
||||
if (newZoom != this._zoom) {
|
||||
this._setZoom(newZoom);
|
||||
} else {
|
||||
this._updateCanvasSize();
|
||||
}
|
||||
}
|
||||
|
||||
_notifyRuntimeOfResized() {
|
||||
let rect = this.getBoundingClientRect();
|
||||
|
||||
if (rect.width == this._runtimeSize.width &&
|
||||
rect.height == this._runtimeSize.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._runtimeSize = rect;
|
||||
this._runtimeOnResizedListener.forEach(listener => {
|
||||
let evt = {
|
||||
type: 'resize',
|
||||
target: this._viewportController
|
||||
};
|
||||
if (typeof listener === 'function') {
|
||||
listener(evt);
|
||||
} else if (typeof listener.handleEvent === 'function') {
|
||||
listener.handleEvent(evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_getEventTarget(type) {
|
||||
switch(type) {
|
||||
case 'keydown':
|
||||
case 'keyup':
|
||||
case 'keypress':
|
||||
return window;
|
||||
}
|
||||
return this._viewportController;
|
||||
}
|
||||
|
||||
_doAction(message) {
|
||||
if (this._actionHandler) {
|
||||
this._actionHandler(message);
|
||||
}
|
||||
}
|
||||
|
||||
_refresh() {
|
||||
if (this._nextPosition) {
|
||||
this._viewportController.scrollTo(
|
||||
this._nextPosition.x, this._nextPosition.y);
|
||||
this._nextPosition = null;
|
||||
}
|
||||
|
||||
this._runtimePosition = this.getScrollOffset();
|
||||
this._doAction({
|
||||
type: 'viewport',
|
||||
xOffset: this._runtimePosition.x,
|
||||
yOffset: this._runtimePosition.y,
|
||||
zoom: this._zoom
|
||||
});
|
||||
|
||||
let newPage = this._getMostVisiblePage();
|
||||
if (newPage != this._page) {
|
||||
this._page = newPage;
|
||||
if (typeof this.onPageChanged === 'function') {
|
||||
this.onPageChanged(newPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(evt) {
|
||||
switch(evt.type) {
|
||||
case 'resize':
|
||||
this._resize();
|
||||
this._notifyRuntimeOfResized();
|
||||
this._refresh();
|
||||
break;
|
||||
case 'scroll':
|
||||
this._nextPosition = null;
|
||||
let position = this.getScrollOffset();
|
||||
if (this._runtimePosition.x != position.x ||
|
||||
this._runtimePosition.y != position.y) {
|
||||
this._refresh();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rotateClockwise() {
|
||||
this._doAction({
|
||||
type: 'rotateClockwise'
|
||||
});
|
||||
}
|
||||
|
||||
rotateCounterClockwise() {
|
||||
this._doAction({
|
||||
type: 'rotateCounterclockwise'
|
||||
});
|
||||
}
|
||||
|
||||
// A handler for delivering messages to runtime.
|
||||
registerActionHandler(handler) {
|
||||
if (typeof handler === 'function') {
|
||||
this._actionHandler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************/
|
||||
/* PPAPIViewport Interface */
|
||||
/***************************/
|
||||
|
||||
addView(canvas) {
|
||||
this._canvasContainer.appendChild(canvas);
|
||||
}
|
||||
|
||||
clearView() {
|
||||
this._canvasContainer.innerHTML = '';
|
||||
}
|
||||
|
||||
getBoundingClientRect() {
|
||||
return this._canvasContainer.getBoundingClientRect();
|
||||
}
|
||||
|
||||
is(element) {
|
||||
return element == this._viewportController;
|
||||
}
|
||||
|
||||
bindUIEvent(type, listener) {
|
||||
if (type == 'fullscreenchange' || type == 'MozScrolledAreaChanged') {
|
||||
// These two events won't be bound on a target because they should be
|
||||
// fully controlled by UI layer, and we'll manually trigger the resize
|
||||
// event once needed.
|
||||
return;
|
||||
}
|
||||
switch(type) {
|
||||
case 'resize':
|
||||
this._runtimeOnResizedListener.push(listener);
|
||||
break;
|
||||
default:
|
||||
this._getEventTarget(type).addEventListener(type, listener);
|
||||
}
|
||||
}
|
||||
|
||||
unbindUIEvent(type, listener) {
|
||||
if (type == 'fullscreenchange' || type == 'MozScrolledAreaChanged') {
|
||||
return;
|
||||
}
|
||||
switch(type) {
|
||||
case 'resize':
|
||||
this._runtimeOnResizedListener =
|
||||
this._runtimeOnResizedListener.filter(item => item != listener);
|
||||
break;
|
||||
default:
|
||||
this._getEventTarget(type).removeEventListener(type, listener);
|
||||
}
|
||||
}
|
||||
|
||||
setCursor(cursor) {
|
||||
this._viewportController.style.cursor = cursor;
|
||||
}
|
||||
|
||||
getScrollOffset() {
|
||||
return {
|
||||
x: this._viewportController.scrollLeft,
|
||||
y: this._viewportController.scrollTop
|
||||
};
|
||||
}
|
||||
|
||||
// Notified by runtime.
|
||||
notify(message) {
|
||||
switch (message.type) {
|
||||
case 'loadProgress':
|
||||
this._updateProgress(message.progress);
|
||||
break;
|
||||
case 'documentDimensions':
|
||||
this._setDocumentDimensions(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
# Copyright 2012 Mozilla Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Main toolbar buttons (tooltips and alt text for images)
|
||||
previous.title=Previous Page
|
||||
previous_label=Previous
|
||||
next.title=Next Page
|
||||
next_label=Next
|
||||
|
||||
# LOCALIZATION NOTE (page_label, page_of):
|
||||
# These strings are concatenated to form the "Page: X of Y" string.
|
||||
# Do not translate "{{pageCount}}", it will be substituted with a number
|
||||
# representing the total number of pages.
|
||||
page_label=Page:
|
||||
page_of=of {{pageCount}}
|
||||
|
||||
zoom_out.title=Zoom Out
|
||||
zoom_out_label=Zoom Out
|
||||
zoom_in.title=Zoom In
|
||||
zoom_in_label=Zoom In
|
||||
zoom.title=Zoom
|
||||
presentation_mode.title=Switch to Presentation Mode
|
||||
presentation_mode_label=Presentation Mode
|
||||
open_file.title=Open File
|
||||
open_file_label=Open
|
||||
print.title=Print
|
||||
print_label=Print
|
||||
download.title=Download
|
||||
download_label=Download
|
||||
bookmark.title=Current view (copy or open in new window)
|
||||
bookmark_label=Current View
|
||||
|
||||
# Secondary toolbar and context menu
|
||||
tools.title=Tools
|
||||
tools_label=Tools
|
||||
first_page.title=Go to First Page
|
||||
first_page.label=Go to First Page
|
||||
first_page_label=Go to First Page
|
||||
last_page.title=Go to Last Page
|
||||
last_page.label=Go to Last Page
|
||||
last_page_label=Go to Last Page
|
||||
page_rotate_cw.title=Rotate Clockwise
|
||||
page_rotate_cw.label=Rotate Clockwise
|
||||
page_rotate_cw_label=Rotate Clockwise
|
||||
page_rotate_ccw.title=Rotate Counterclockwise
|
||||
page_rotate_ccw.label=Rotate Counterclockwise
|
||||
page_rotate_ccw_label=Rotate Counterclockwise
|
||||
|
||||
hand_tool_enable.title=Enable hand tool
|
||||
hand_tool_enable_label=Enable hand tool
|
||||
hand_tool_disable.title=Disable hand tool
|
||||
hand_tool_disable_label=Disable hand tool
|
||||
|
||||
# Document properties dialog box
|
||||
document_properties.title=Document Properties…
|
||||
document_properties_label=Document Properties…
|
||||
document_properties_file_name=File name:
|
||||
document_properties_file_size=File size:
|
||||
# LOCALIZATION NOTE (document_properties_kb): "{{size_kb}}" and "{{size_b}}"
|
||||
# will be replaced by the PDF file size in kilobytes, respectively in bytes.
|
||||
document_properties_kb={{size_kb}} KB ({{size_b}} bytes)
|
||||
# LOCALIZATION NOTE (document_properties_mb): "{{size_mb}}" and "{{size_b}}"
|
||||
# will be replaced by the PDF file size in megabytes, respectively in bytes.
|
||||
document_properties_mb={{size_mb}} MB ({{size_b}} bytes)
|
||||
document_properties_title=Title:
|
||||
document_properties_author=Author:
|
||||
document_properties_subject=Subject:
|
||||
document_properties_keywords=Keywords:
|
||||
document_properties_creation_date=Creation Date:
|
||||
document_properties_modification_date=Modification Date:
|
||||
# LOCALIZATION NOTE (document_properties_date_string): "{{date}}" and "{{time}}"
|
||||
# will be replaced by the creation/modification date, and time, of the PDF file.
|
||||
document_properties_date_string={{date}}, {{time}}
|
||||
document_properties_creator=Creator:
|
||||
document_properties_producer=PDF Producer:
|
||||
document_properties_version=PDF Version:
|
||||
document_properties_page_count=Page Count:
|
||||
document_properties_close=Close
|
||||
|
||||
# Tooltips and alt text for side panel toolbar buttons
|
||||
# (the _label strings are alt text for the buttons, the .title strings are
|
||||
# tooltips)
|
||||
toggle_sidebar.title=Toggle Sidebar
|
||||
toggle_sidebar_label=Toggle Sidebar
|
||||
outline.title=Show Document Outline
|
||||
outline_label=Document Outline
|
||||
attachments.title=Show Attachments
|
||||
attachments_label=Attachments
|
||||
thumbs.title=Show Thumbnails
|
||||
thumbs_label=Thumbnails
|
||||
findbar.title=Find in Document
|
||||
findbar_label=Find
|
||||
|
||||
# Thumbnails panel item (tooltip and alt text for images)
|
||||
# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
|
||||
# number.
|
||||
thumb_page_title=Page {{page}}
|
||||
# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
|
||||
# number.
|
||||
thumb_page_canvas=Thumbnail of Page {{page}}
|
||||
|
||||
# Find panel button title and messages
|
||||
find_label=Find:
|
||||
find_previous.title=Find the previous occurrence of the phrase
|
||||
find_previous_label=Previous
|
||||
find_next.title=Find the next occurrence of the phrase
|
||||
find_next_label=Next
|
||||
find_highlight=Highlight all
|
||||
find_match_case_label=Match case
|
||||
find_reached_top=Reached top of document, continued from bottom
|
||||
find_reached_bottom=Reached end of document, continued from top
|
||||
find_not_found=Phrase not found
|
||||
|
||||
# Error panel labels
|
||||
error_more_info=More Information
|
||||
error_less_info=Less Information
|
||||
error_close=Close
|
||||
# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be
|
||||
# replaced by the PDF.JS version and build ID.
|
||||
error_version_info=PDF.js v{{version}} (build: {{build}})
|
||||
# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an
|
||||
# english string describing the error.
|
||||
error_message=Message: {{message}}
|
||||
# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack
|
||||
# trace.
|
||||
error_stack=Stack: {{stack}}
|
||||
# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename
|
||||
error_file=File: {{file}}
|
||||
# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number
|
||||
error_line=Line: {{line}}
|
||||
rendering_error=An error occurred while rendering the page.
|
||||
|
||||
# Predefined zoom values
|
||||
page_scale_width=Page Width
|
||||
page_scale_fit=Page Fit
|
||||
page_scale_auto=Automatic Zoom
|
||||
page_scale_actual=Actual Size
|
||||
# LOCALIZATION NOTE (page_scale_percent): "{{scale}}" will be replaced by a
|
||||
# numerical scale value.
|
||||
page_scale_percent={{scale}}%
|
||||
|
||||
# Loading indicator messages
|
||||
loading_error_indicator=Error
|
||||
loading_error=An error occurred while loading the PDF.
|
||||
invalid_file_error=Invalid or corrupted PDF file.
|
||||
missing_file_error=Missing PDF file.
|
||||
unexpected_response_error=Unexpected server response.
|
||||
|
||||
# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip.
|
||||
# "{{type}}" will be replaced with an annotation type from a list defined in
|
||||
# the PDF spec (32000-1:2008 Table 169 – Annotation types).
|
||||
# Some common types are e.g.: "Check", "Text", "Comment", "Note"
|
||||
text_annotation_type.alt=[{{type}} Annotation]
|
||||
password_label=Enter the password to open this PDF file.
|
||||
password_invalid=Invalid password. Please try again.
|
||||
password_ok=OK
|
||||
password_cancel=Cancel
|
||||
|
||||
printing_not_supported=Warning: Printing is not fully supported by this browser.
|
||||
printing_not_ready=Warning: The PDF is not fully loaded for printing.
|
||||
web_fonts_disabled=Web fonts are disabled: unable to use embedded PDF fonts.
|
||||
document_colors_not_allowed=PDF documents are not allowed to use their own colors: 'Allow pages to choose their own colors' is deactivated in the browser.
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<path
|
||||
d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z"
|
||||
id="path4"
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.25402856;stroke-opacity:1" />
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 415 B |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="40"
|
||||
width="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
width="33.76017"
|
||||
height="33.76017"
|
||||
x="3.119915"
|
||||
y="3.119915" />
|
||||
<path
|
||||
d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.93031836;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 883 B |
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<g
|
||||
transform="translate(0,-60)"
|
||||
id="layer1">
|
||||
<rect
|
||||
width="36.460953"
|
||||
height="34.805603"
|
||||
x="1.7695236"
|
||||
y="62.597198"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.30826771;stroke-opacity:1" />
|
||||
<g
|
||||
transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)">
|
||||
<path
|
||||
d="M 20,64.526342 C 11.454135,64.526342 4.5263421,71.454135 4.5263421,80 4.5263421,88.545865 11.454135,95.473658 20,95.473658 28.545865,95.473658 35.473658,88.545865 35.473658,80 35.473658,71.454135 28.545865,64.526342 20,64.526342 z m -0.408738,9.488564 c 3.527079,0 6.393832,2.84061 6.393832,6.335441 0,3.494831 -2.866753,6.335441 -6.393832,6.335441 -3.527079,0 -6.393832,-2.84061 -6.393832,-6.335441 0,-3.494831 2.866753,-6.335441 6.393832,-6.335441 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.02768445;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
d="m 7.2335209,71.819938 4.9702591,4.161823 c -1.679956,2.581606 -1.443939,6.069592 0.159325,8.677725 l -5.1263071,3.424463 c 0.67516,1.231452 3.0166401,3.547686 4.2331971,4.194757 l 3.907728,-4.567277 c 2.541952,1.45975 5.730694,1.392161 8.438683,-0.12614 l 3.469517,6.108336 c 1.129779,-0.44367 4.742234,-3.449633 5.416358,-5.003859 l -5.46204,-4.415541 c 1.44319,-2.424098 1.651175,-5.267515 0.557303,-7.748623 l 5.903195,-3.833951 C 33.14257,71.704996 30.616217,69.018606 29.02952,67.99296 l -4.118813,4.981678 C 22.411934,71.205099 18.900853,70.937534 16.041319,72.32916 l -3.595408,-5.322091 c -1.345962,0.579488 -4.1293881,2.921233 -5.2123901,4.812869 z m 8.1010311,3.426672 c 2.75284,-2.446266 6.769149,-2.144694 9.048998,0.420874 2.279848,2.56557 2.113919,6.596919 -0.638924,9.043185 -2.752841,2.446267 -6.775754,2.13726 -9.055604,-0.428308 -2.279851,-2.565568 -2.107313,-6.589485 0.64553,-9.035751 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 2.1 KiB |
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 32.003143,1.4044602 57.432701,62.632577 6.5672991,62.627924 z"
|
||||
style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:1.00493038;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 408 B |
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 25.470843,9.4933766 C 25.30219,12.141818 30.139101,14.445969 34.704831,13.529144 40.62635,12.541995 41.398833,7.3856498 35.97505,5.777863 31.400921,4.1549155 25.157674,6.5445892 25.470843,9.4933766 z M 4.5246282,17.652051 C 4.068249,11.832873 9.2742983,5.9270407 18.437379,3.0977088 29.751911,-0.87185184 45.495663,1.4008022 53.603953,7.1104009 c 9.275765,6.1889221 7.158128,16.2079421 -3.171076,21.5939521 -1.784316,1.635815 -6.380222,1.21421 -7.068351,3.186186 -1.04003,0.972427 -1.288046,2.050158 -1.232864,3.168203 1.015111,2.000108 -3.831548,1.633216 -3.270553,3.759574 0.589477,5.264544 -0.179276,10.53738 -0.362842,15.806257 -0.492006,2.184998 1.163456,4.574232 -0.734888,6.610642 -2.482919,2.325184 -7.30604,2.189143 -9.193497,-0.274767 -2.733688,-1.740626 -8.254447,-3.615254 -6.104247,-6.339626 3.468112,-1.708686 -2.116197,-3.449897 0.431242,-5.080274 5.058402,-1.39256 -2.393215,-2.304318 -0.146889,-4.334645 3.069198,-0.977415 2.056986,-2.518352 -0.219121,-3.540397 1.876567,-1.807151 1.484149,-4.868919 -2.565455,-5.942205 0.150866,-1.805474 2.905737,-4.136876 -1.679967,-5.20493 C 10.260902,27.882167 4.6872697,22.95045 4.5245945,17.652051 z"
|
||||
id="path604"
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.72665179;stroke-opacity:1" />
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.4 KiB |
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 32.003143,10.913072 57.432701,53.086929 6.567299,53.083723 z"
|
||||
id="path2985"
|
||||
style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:0.83403099;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 426 B |
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 158 B |
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
width="36.075428"
|
||||
height="31.096582"
|
||||
x="1.962286"
|
||||
y="4.4517088"
|
||||
id="rect4"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.23004246;stroke-opacity:1" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="1.5012145"
|
||||
x="6.0157046"
|
||||
y="10.285"
|
||||
id="rect6"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="6.0157056"
|
||||
y="23.21689"
|
||||
id="rect8"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="5.8130345"
|
||||
y="28.964394"
|
||||
id="rect10"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="6.0157046"
|
||||
y="17.426493"
|
||||
id="rect12"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.0 KiB |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
width="33.76017"
|
||||
height="33.76017"
|
||||
x="3.119915"
|
||||
y="3.119915"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
d="m 17.692678,34.50206 0,-16.182224 c -1.930515,-0.103225 -3.455824,-0.730383 -4.57593,-1.881473 -1.12011,-1.151067 -1.680164,-2.619596 -1.680164,-4.405591 0,-1.992435 0.621995,-3.5796849 1.865988,-4.7617553 1.243989,-1.1820288 3.06352,-1.7730536 5.458598,-1.7730764 l 9.802246,0 0,2.6789711 -2.229895,0 0,26.3251486 -2.632515,0 0,-26.3251486 -3.45324,0 0,26.3251486 z"
|
||||
style="font-size:29.42051125px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.07795751;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial" />
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.1 KiB |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/findbarButton-next-rtl.png
Normal file
После Ширина: | Высота: | Размер: 199 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/findbarButton-next-rtl@2x.png
Normal file
После Ширина: | Высота: | Размер: 304 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/findbarButton-next.png
Normal file
После Ширина: | Высота: | Размер: 193 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/findbarButton-next@2x.png
Normal file
После Ширина: | Высота: | Размер: 296 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/findbarButton-previous-rtl.png
Normal file
После Ширина: | Высота: | Размер: 193 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/findbarButton-previous-rtl@2x.png
Normal file
После Ширина: | Высота: | Размер: 296 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/findbarButton-previous.png
Normal file
После Ширина: | Высота: | Размер: 199 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/findbarButton-previous@2x.png
Normal file
После Ширина: | Высота: | Размер: 304 B |
После Ширина: | Высота: | Размер: 326 B |
После Ширина: | Высота: | Размер: 326 B |
После Ширина: | Высота: | Размер: 2.5 KiB |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/loading-small.png
Normal file
После Ширина: | Высота: | Размер: 7.2 KiB |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/loading-small@2x.png
Normal file
После Ширина: | Высота: | Размер: 16 KiB |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-documentProperties.png
Normal file
После Ширина: | Высота: | Размер: 403 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-documentProperties@2x.png
Normal file
После Ширина: | Высота: | Размер: 933 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-firstPage.png
Normal file
После Ширина: | Высота: | Размер: 179 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-firstPage@2x.png
Normal file
После Ширина: | Высота: | Размер: 266 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-handTool.png
Normal file
После Ширина: | Высота: | Размер: 301 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-handTool@2x.png
Normal file
После Ширина: | Высота: | Размер: 583 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-lastPage.png
Normal file
После Ширина: | Высота: | Размер: 175 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-lastPage@2x.png
Normal file
После Ширина: | Высота: | Размер: 276 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-rotateCcw.png
Normal file
После Ширина: | Высота: | Размер: 360 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-rotateCcw@2x.png
Normal file
После Ширина: | Высота: | Размер: 731 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-rotateCw.png
Normal file
После Ширина: | Высота: | Размер: 359 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/secondaryToolbarButton-rotateCw@2x.png
Normal file
После Ширина: | Высота: | Размер: 714 B |
После Ширина: | Высота: | Размер: 290 B |
После Ширина: | Высота: | Размер: 2.4 KiB |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-bookmark.png
Normal file
После Ширина: | Высота: | Размер: 174 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-bookmark@2x.png
Normal file
После Ширина: | Высота: | Размер: 260 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-download.png
Normal file
После Ширина: | Высота: | Размер: 259 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-download@2x.png
Normal file
После Ширина: | Высота: | Размер: 425 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-menuArrows.png
Normal file
После Ширина: | Высота: | Размер: 107 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-menuArrows@2x.png
Normal file
После Ширина: | Высота: | Размер: 152 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-openFile.png
Normal file
После Ширина: | Высота: | Размер: 295 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-openFile@2x.png
Normal file
После Ширина: | Высота: | Размер: 550 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-pageDown-rtl.png
Normal file
После Ширина: | Высота: | Размер: 242 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-pageDown-rtl@2x.png
Normal file
После Ширина: | Высота: | Размер: 398 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-pageDown.png
Normal file
После Ширина: | Высота: | Размер: 238 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-pageDown@2x.png
Normal file
После Ширина: | Высота: | Размер: 396 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-pageUp-rtl.png
Normal file
После Ширина: | Высота: | Размер: 245 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-pageUp-rtl@2x.png
Normal file
После Ширина: | Высота: | Размер: 405 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-pageUp.png
Normal file
После Ширина: | Высота: | Размер: 246 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-pageUp@2x.png
Normal file
После Ширина: | Высота: | Размер: 403 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-presentationMode.png
Normal file
После Ширина: | Высота: | Размер: 321 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-presentationMode@2x.png
Normal file
После Ширина: | Высота: | Размер: 586 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-print.png
Normal file
После Ширина: | Высота: | Размер: 257 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-print@2x.png
Normal file
После Ширина: | Высота: | Размер: 464 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-search.png
Normal file
После Ширина: | Высота: | Размер: 309 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-search@2x.png
Normal file
После Ширина: | Высота: | Размер: 653 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-secondaryToolbarToggle-rtl.png
Normal file
После Ширина: | Высота: | Размер: 246 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-secondaryToolbarToggle-rtl@2x.png
Normal file
После Ширина: | Высота: | Размер: 456 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-secondaryToolbarToggle.png
Normal file
После Ширина: | Высота: | Размер: 243 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-secondaryToolbarToggle@2x.png
Normal file
После Ширина: | Высота: | Размер: 458 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-sidebarToggle-rtl.png
Normal file
После Ширина: | Высота: | Размер: 225 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-sidebarToggle-rtl@2x.png
Normal file
После Ширина: | Высота: | Размер: 344 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-sidebarToggle.png
Normal file
После Ширина: | Высота: | Размер: 225 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-sidebarToggle@2x.png
Normal file
После Ширина: | Высота: | Размер: 331 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-viewAttachments.png
Normal file
После Ширина: | Высота: | Размер: 384 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-viewAttachments@2x.png
Normal file
После Ширина: | Высота: | Размер: 859 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-viewOutline-rtl.png
Normal file
После Ширина: | Высота: | Размер: 177 B |
Двоичные данные
browser/extensions/mortar/host/pdf/chrome/style/images/toolbarButton-viewOutline-rtl@2x.png
Normal file
После Ширина: | Высота: | Размер: 394 B |