Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-01-28 14:30:12 +01:00
commit 395266b2f6
102 changed files with 2960 additions and 311 deletions

View File

@ -33,14 +33,6 @@ HelperAppLauncherDialog.prototype = {
aLauncher.saveToDisk(null, false);
},
promptForSaveToFile: function(aLauncher,
aContext,
aDefaultFile,
aSuggestedFileExt,
aForcePrompt) {
throw Cr.NS_ERROR_NOT_AVAILABLE;
},
promptForSaveToFileAsync: function(aLauncher,
aContext,
aDefaultFile,

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -136,7 +136,7 @@
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
<project name="kernel/common" path="kernel" revision="6c6f012cea17fb8b3263605737816cf6663432f1"/>
<project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
<project name="u-boot" path="u-boot" revision="5167e5eec5cb6b3147839da158637e6d953a4e4f"/>
<project name="u-boot" path="u-boot" revision="cb37a7434073bb47c4c066f6922006442dcd8531"/>
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="f56ab768cb9f1ad42fb0809ffec1424b1e693369"/>
<project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d6a27295acb0a25926bf6290dd2532a7f9027864"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d6a27295acb0a25926bf6290dd2532a7f9027864"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "1d53fb07984298253aad64bfa4236b7167ee3d4d",
"git_revision": "ba613ae583a706131c45e885f65d428d4a541a81",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "08a288892d8f0b41a960104150fba34f113629e6",
"revision": "4705c493adb5c766382b27e4fbff42f7447900e9",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d6a27295acb0a25926bf6290dd2532a7f9027864"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d53fb07984298253aad64bfa4236b7167ee3d4d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ba613ae583a706131c45e885f65d428d4a541a81"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -217,9 +217,15 @@
}
window.addEventListener("AboutNetErrorOptions", function(evt) {
// Pinning errors are of type nssFailure2 (don't ask me why)
// Pinning errors are of type nssFailure2
if (getErrorCode() == "nssFailure2" && !errTitle.hasAttribute("sslv3")) {
// TODO: and the pref is set...
var learnMoreLink = document.getElementById("learnMoreLink");
// nssFailure2 also gets us other non-overrideable errors. Choose
// a "learn more" link based on description:
if (getDescription().contains("mozilla_pkix_error_key_pinning_failure")) {
learnMoreLink.href = "https://support.mozilla.org/kb/certificate-pinning-reports";
}
var options = JSON.parse(evt.detail);
if (options && options.enabled) {
var checkbox = document.getElementById('automaticallyReportInFuture');
@ -476,8 +482,7 @@
<input type="checkbox" id="automaticallyReportInFuture" />
<label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic;</label>
</p>
<!-- TODO add link to relevant page on sumo -->
<a href="https://support.mozilla.org/kb/certificate-pinning-reports" target="new">&errorReporting.learnMore;</a>
<a href="https://support.mozilla.org/kb/tls-error-reports" id="learnMoreLink" target="new">&errorReporting.learnMore;</a>
<span id="reportingState">
<button id="reportCertificateError">&errorReporting.report;</button>
<button id="reportCertificateErrorRetry">&errorReporting.tryAgain;</button>

View File

@ -1268,6 +1268,9 @@
onSuccess: function(engine) {
event.target.hidePopup();
BrowserSearch.searchBar.openSuggestionsPanel();
},
onError: function(errorCode) {
Components.utils.reportError("Error adding search engine: " + errorCode);
}
}
Services.search.addEngine(target.getAttribute("uri"),

View File

@ -40,7 +40,6 @@
function translateString(key, args, fallback) {
if (args && args.num) {
var num = args && args.num;
delete args.num;
}
var data = getL10nData(key, num);
if (!data && fallback)

View File

@ -73,6 +73,7 @@
<script src="roomViews_test.js"></script>
<script src="conversationViews_test.js"></script>
<script src="contacts_test.js"></script>
<script src="l10n_test.js"></script>
<script>
// Stop the default init functions running to avoid conflicts in tests
document.removeEventListener('DOMContentLoaded', loop.panel.init);

View File

@ -0,0 +1,37 @@
/* 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/. */
var expect = chai.expect;
describe("document.mozL10n", function() {
"use strict";
var fakeMozLoop;
beforeEach(function() {
fakeMozLoop = {
locale: "en-US",
getStrings: function(key) {
if (key === "plural") {
return '{"textContent":"{{num}} plural form;{{num}} plural forms"}';
}
return '{"textContent":"' + key + '"}';
},
getPluralForm: function(num, string) {
return string.split(";")[num === 0 ? 0 : 1];
}
};
document.mozL10n.initialize(fakeMozLoop);
});
it("should get a simple string", function() {
expect(document.mozL10n.get("test")).eql("test");
});
it("should get a plural form", function() {
expect(document.mozL10n.get("plural", {num:10})).eql("10 plural forms");
});
});

View File

@ -23,8 +23,8 @@ CONTENT_SERVER_ENV = os.environ.copy()
CONTENT_SERVER_ENV.update({"PORT": str(CONTENT_SERVER_PORT),
"LOOP_SERVER_PORT": str(LOOP_SERVER_PORT)})
WEB_APP_URL = "http://localhost:" + str(CONTENT_SERVER_PORT) + \
"/content/#call/{token}"
ROOMS_WEB_APP_URL = "http://localhost:" + str(CONTENT_SERVER_PORT) + \
"/content/{token}"
LOOP_SERVER_COMMAND = ["make", "runserver"]
LOOP_SERVER_ENV = os.environ.copy()
@ -33,7 +33,7 @@ LOOP_SERVER_ENV = os.environ.copy()
LOOP_SERVER_ENV.update({"NODE_ENV": "dev",
"PORT": str(LOOP_SERVER_PORT),
"SERVER_ADDRESS": "localhost:" + str(LOOP_SERVER_PORT),
"WEB_APP_URL": WEB_APP_URL})
"ROOMS_WEB_APP_URL": ROOMS_WEB_APP_URL})
class LoopTestServers:

View File

@ -127,15 +127,14 @@ class Test1BrowserCall(MarionetteTestCase):
# Assumes the standlone or the conversation window is selected first.
def check_remote_video(self):
# TODO: This is disabled currently due to bug 1122486
# video_wrapper = self.wait_for_element_displayed(By.CSS_SELECTOR, ".media .OT_subscriber .OT_video-container", 20)
# video = self.wait_for_subelement_displayed(video_wrapper, By.TAG_NAME, "video")
video_wrapper = self.wait_for_element_displayed(
By.CSS_SELECTOR,
".media .OT_subscriber .OT_video-container", 20)
video = self.wait_for_subelement_displayed(
video_wrapper, By.TAG_NAME, "video")
# self.wait_for_element_attribute_to_be_false(video, "paused")
# self.assertEqual(video.get_attribute("ended"), "false")
# Due to the above waits being disabled, we do a sleep.
sleep(15)
self.wait_for_element_attribute_to_be_false(video, "paused")
self.assertEqual(video.get_attribute("ended"), "false")
def standalone_check_remote_video(self):
self.switch_to_standalone()

View File

@ -31,6 +31,12 @@ var gAdvancedPane = {
}
#ifdef MOZ_UPDATER
let onUnload = function () {
window.removeEventListener("unload", onUnload, false);
Services.prefs.removeObserver("app.update.", this);
}.bind(this);
window.addEventListener("unload", onUnload, false);
Services.prefs.addObserver("app.update.", this, false);
this.updateReadPrefs();
#endif
this.updateOfflineApps();
@ -822,5 +828,15 @@ var gAdvancedPane = {
document.documentElement.openWindow("mozilla:devicemanager",
"chrome://pippki/content/device_manager.xul",
"", null);
}
},
#ifdef MOZ_UPDATER
observe: function (aSubject, aTopic, aData) {
switch(aTopic) {
case "nsPref:changed":
this.updateReadPrefs();
break;
}
},
#endif
};

View File

@ -29,6 +29,12 @@ var gAdvancedPane = {
advancedPrefs.selectedIndex = preference.value;
#ifdef MOZ_UPDATER
let onUnload = function () {
window.removeEventListener("unload", onUnload, false);
Services.prefs.removeObserver("app.update.", this);
}.bind(this);
window.addEventListener("unload", onUnload, false);
Services.prefs.addObserver("app.update.", this, false);
this.updateReadPrefs();
#endif
this.updateOfflineApps();
@ -841,5 +847,15 @@ var gAdvancedPane = {
openDialog("chrome://pippki/content/device_manager.xul",
"mozilla:devicemanager",
"modal=yes", null);
}
},
#ifdef MOZ_UPDATER
observe: function (aSubject, aTopic, aData) {
switch(aTopic) {
case "nsPref:changed":
this.updateReadPrefs();
break;
}
},
#endif
};

View File

@ -98,6 +98,15 @@ if (typeof Mozilla == 'undefined') {
});
};
Mozilla.UITour.showHeartbeat = function(message, thankyouMessage, flowId, engagementURL) {
_sendEvent('showHeartbeat', {
message: message,
thankyouMessage: thankyouMessage,
flowId: flowId,
engagementURL: engagementURL
});
};
Mozilla.UITour.showHighlight = function(target, effect) {
_sendEvent('showHighlight', {
target: target,

View File

@ -13,6 +13,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.importGlobalProperties(["URL"]);
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
@ -118,11 +120,15 @@ this.UITour = {
["help", {query: "#PanelUI-help"}],
["home", {query: "#home-button"}],
["forget", {
allowAdd: true,
query: "#panic-button",
widgetName: "panic-button",
allowAdd: true,
}],
["loop", {query: "#loop-button"}],
["loop", {
allowAdd: true,
query: "#loop-button",
widgetName: "loop-button",
}],
["loop-newRoom", {
infoPanelPosition: "leftcenter topright",
query: (aDocument) => {
@ -329,7 +335,6 @@ this.UITour = {
},
onPageEvent: function(aMessage, aEvent) {
let contentDocument = null;
let browser = aMessage.target;
let window = browser.ownerDocument.defaultView;
let tab = window.gBrowser.getTabForBrowser(browser);
@ -364,10 +369,6 @@ this.UITour = {
// Do this before bailing if there's no tab, so later we can pick up the pieces:
window.gBrowser.tabContainer.addEventListener("TabSelect", this);
if (!window.gMultiProcessBrowser) { // Non-e10s. See bug 1089000.
contentDocument = browser.contentWindow.document;
}
switch (action) {
case "registerPageID": {
// This is only relevant if Telemtry is enabled.
@ -391,6 +392,29 @@ this.UITour = {
break;
}
case "showHeartbeat": {
// Validate the input parameters.
if (typeof data.message !== "string" || data.message === "") {
log.error("showHeartbeat: Invalid message specified.");
break;
}
if (typeof data.thankyouMessage !== "string" || data.thankyouMessage === "") {
log.error("showHeartbeat: Invalid thank you message specified.");
break;
}
if (typeof data.flowId !== "string" || data.flowId === "") {
log.error("showHeartbeat: Invalid flowId specified.");
break;
}
// Finally show the Heartbeat UI.
this.showHeartbeat(window, messageManager, data.message, data.thankyouMessage, data.flowId,
data.engagementURL);
break;
}
case "showHighlight": {
let targetPromise = this.getTarget(window, data.target);
targetPromise.then(target => {
@ -507,6 +531,7 @@ this.UITour = {
}
let secman = Services.scriptSecurityManager;
let contentDocument = browser.contentWindow.document;
let principal = contentDocument.nodePrincipal;
let flags = secman.DISALLOW_INHERIT_PRINCIPAL;
try {
@ -549,7 +574,7 @@ this.UITour = {
// 'signup' is the only action that makes sense currently, so we don't
// accept arbitrary actions just to be safe...
// We want to replace the current tab.
contentDocument.location.href = "about:accounts?action=signup&entrypoint=uitour";
browser.loadURI("about:accounts?action=signup&entrypoint=uitour");
break;
}
@ -959,6 +984,137 @@ this.UITour = {
LightweightThemeManager.resetPreview();
},
/**
* Show the Heartbeat UI to request user feedback. This function reports back to the
* caller using |notify|. The notification event name reflects the current status the UI
* is in (either "Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed" or
* "Heartbeat:Voted"). When a "Heartbeat:Voted" event is notified the data payload contains
* a |score| field which holds the rating picked by the user.
* Please note that input parameters are already validated by the caller.
*
* @param aChromeWindow
* The chrome window that the heartbeat notification is displayed in.
* @param aMessageManager
* The message manager to communicate with the API caller.
* @param aMessage
* The message, or question, to display on the notification.
* @param aThankyouMessage
* The thank you message to display after user votes.
* @param aFlowId
* An identifier for this rating flow. Please note that this is only used to
* identify the notification box.
* @param [aEngagementURL]
* The engagement URL to open in a new tab once user has voted. If this is null
* or invalid, no new tab is opened.
*/
showHeartbeat: function(aChromeWindow, aMessageManager, aMessage, aThankyouMessage, aFlowId,
aEngagementURL = null) {
let nb = aChromeWindow.document.getElementById("high-priority-global-notificationbox");
// Create the notification. Prefix its ID to decrease the chances of collisions.
let notice = nb.appendNotification(aMessage, "heartbeat-" + aFlowId,
"chrome://branding/content/icon64.png", nb.PRIORITY_INFO_HIGH, null, function() {
// Let the consumer know the notification bar was closed. This also happens
// after voting.
this.notify("Heartbeat:NotificationClosed", { flowId: aFlowId, timestamp: Date.now() });
}.bind(this));
// Get the elements we need to style.
let messageImage =
aChromeWindow.document.getAnonymousElementByAttribute(notice, "anonid", "messageImage");
let messageText =
aChromeWindow.document.getAnonymousElementByAttribute(notice, "anonid", "messageText");
// Create the fragment holding the rating UI.
let frag = aChromeWindow.document.createDocumentFragment();
// Build the Heartbeat star rating.
const numStars = 5;
let ratingContainer = aChromeWindow.document.createElement("hbox");
ratingContainer.id = "star-rating-container";
for (let i = 0; i < numStars; i++) {
// Create a star rating element.
let ratingElement = aChromeWindow.document.createElement("toolbarbutton");
// Style it.
let starIndex = numStars - i;
ratingElement.className = "plain star-x";
ratingElement.id = "star" + starIndex;
ratingElement.setAttribute("data-score", starIndex);
// Add the click handler.
ratingElement.addEventListener("click", function (evt) {
let rating = Number(evt.target.getAttribute("data-score"), 10);
// Let the consumer know user voted.
this.notify("Heartbeat:Voted", { flowId: aFlowId, score: rating, timestamp: Date.now() });
// Display the Heart and make it pulse twice.
notice.image = "chrome://browser/skin/heartbeat-icon.svg";
notice.label = aThankyouMessage;
messageImage.classList.remove("pulse-onshow");
messageImage.classList.add("pulse-twice");
// Remove all the children of the notice (rating container
// and the flex).
while (notice.firstChild) {
notice.removeChild(notice.firstChild);
}
// Make sure that we have a valid URL. If we haven't, do not open the engagement page.
let engagementURL = null;
try {
engagementURL = new URL(aEngagementURL);
} catch (error) {
log.error("showHeartbeat: Invalid URL specified.");
}
// Just open the engagement tab if we have a valid engagement URL.
if (engagementURL) {
// Append the score data to the engagement URL.
engagementURL.searchParams.append("type", "stars");
engagementURL.searchParams.append("score", rating);
engagementURL.searchParams.append("flowid", aFlowId);
// Open the engagement URL in a new tab.
aChromeWindow.gBrowser.selectedTab =
aChromeWindow.gBrowser.addTab(engagementURL.toString(), {
owner: aChromeWindow.gBrowser.selectedTab,
relatedToCurrent: true
});
}
// Remove the notification bar after 3 seconds.
aChromeWindow.setTimeout(() => {
nb.removeNotification(notice);
}, 3000);
}.bind(this));
// Add it to the container.
ratingContainer.appendChild(ratingElement);
}
frag.appendChild(ratingContainer);
// Make sure the stars are not pushed to the right by the spacer.
let rightSpacer = aChromeWindow.document.createElement("spacer");
rightSpacer.flex = 20;
frag.appendChild(rightSpacer);
let leftSpacer = messageText.nextSibling;
leftSpacer.flex = 0;
// Append the fragment and apply the styling.
notice.appendChild(frag);
notice.classList.add("heartbeat");
messageImage.classList.add("heartbeat", "pulse-onshow");
messageText.classList.add("heartbeat");
// Let the consumer know the notification was shown.
this.notify("Heartbeat:NotificationOffered", { flowId: aFlowId, timestamp: Date.now() });
},
/**
* @param aChromeWindow The chrome window that the highlight is in. Necessary since some targets
* are in a sub-frame so the defaultView is not the same as the chrome

View File

@ -17,6 +17,8 @@ skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
[browser_UITour_annotation_size_attributes.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
[browser_UITour_heartbeat.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
[browser_UITour_loop.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
[browser_UITour_modalDialog.js]

View File

@ -0,0 +1,231 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gTestTab;
let gContentAPI;
let gContentWindow;
let notificationBox = document.getElementById("high-priority-global-notificationbox");
Components.utils.import("resource:///modules/UITour.jsm");
function test() {
UITourTest();
}
/**
* Simulate a click on a rating element in the Heartbeat notification.
*
* @param aId
* The id of the notification box.
* @param aScore
* The score related to the rating element we want to click on.
*/
function simulateVote(aId, aScore) {
// UITour.jsm prefixes the notification box ID with "heartbeat-" to prevent collisions.
let notification = notificationBox.getNotificationWithValue("heartbeat-" + aId);
let ratingContainer = notification.childNodes[0];
ok(ratingContainer, "The notification has a valid rating container.");
let ratingElement = ratingContainer.getElementsByAttribute("data-score", aScore);
ok(ratingElement[0], "The rating container contains the requested rating element.");
ratingElement[0].click();
}
/**
* Remove the notification box.
*
* @param aId
* The id of the notification box to remove.
*/
function cleanUpNotification(aId) {
let notification = notificationBox.getNotificationWithValue("heartbeat-" + aId);
notificationBox.removeNotification(notification);
}
let tests = [
/**
* Check that the "stars" heartbeat UI correctly shows and closes.
*/
function test_heartbeat_stars_show(done) {
let flowId = "ui-ratefirefox-" + Math.random();
let engagementURL = "http://example.com";
gContentAPI.observe(function (aEventName, aData) {
switch (aEventName) {
case "Heartbeat:NotificationOffered": {
info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
cleanUpNotification(flowId);
break;
}
case "Heartbeat:NotificationClosed": {
info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
done();
break;
}
default:
// We are not expecting other states for this test.
ok(false, "Unexpected notification received: " + aEventName);
}
});
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
},
/**
* Test that the heartbeat UI correctly works with null engagement URL.
*/
function test_heartbeat_null_engagementURL(done) {
let flowId = "ui-ratefirefox-" + Math.random();
let originalTabCount = gBrowser.tabs.length;
gContentAPI.observe(function (aEventName, aData) {
switch (aEventName) {
case "Heartbeat:NotificationOffered": {
info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
// The UI was just shown. We can simulate a click on a rating element (i.e., "star").
simulateVote(flowId, 2);
break;
}
case "Heartbeat:Voted": {
info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
break;
}
case "Heartbeat:NotificationClosed": {
info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
done();
break;
}
default:
// We are not expecting other states for this test.
ok(false, "Unexpected notification received: " + aEventName);
}
});
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, null);
},
/**
* Test that the heartbeat UI correctly works with an invalid, but non null, engagement URL.
*/
function test_heartbeat_invalid_engagement_URL(done) {
let flowId = "ui-ratefirefox-" + Math.random();
let originalTabCount = gBrowser.tabs.length;
let invalidEngagementURL = "invalidEngagement";
gContentAPI.observe(function (aEventName, aData) {
switch (aEventName) {
case "Heartbeat:NotificationOffered": {
info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
// The UI was just shown. We can simulate a click on a rating element (i.e., "star").
simulateVote(flowId, 2);
break;
}
case "Heartbeat:Voted": {
info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
break;
}
case "Heartbeat:NotificationClosed": {
info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
done();
break;
}
default:
// We are not expecting other states for this test.
ok(false, "Unexpected notification received: " + aEventName);
}
});
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, invalidEngagementURL);
},
/**
* Test that the score is correctly reported.
*/
function test_heartbeat_stars_vote(done) {
const expectedScore = 4;
let flowId = "ui-ratefirefox-" + Math.random();
gContentAPI.observe(function (aEventName, aData) {
switch (aEventName) {
case "Heartbeat:NotificationOffered": {
info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
// The UI was just shown. We can simulate a click on a rating element (i.e., "star").
simulateVote(flowId, expectedScore);
break;
}
case "Heartbeat:Voted": {
info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
is(aData.score, expectedScore, "Should report a score of " + expectedScore);
done();
break;
}
case "Heartbeat:NotificationClosed": {
info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
break;
}
default:
// We are not expecting other states for this test.
ok(false, "Unexpected notification received: " + aEventName);
}
});
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, null);
},
/**
* Test that the engagement page is correctly opened when voting.
*/
function test_heartbeat_engagement_tab(done) {
let engagementURL = "http://example.com";
let flowId = "ui-ratefirefox-" + Math.random();
let originalTabCount = gBrowser.tabs.length;
const expectedTabCount = originalTabCount + 1;
gContentAPI.observe(function (aEventName, aData) {
switch (aEventName) {
case "Heartbeat:NotificationOffered": {
info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
// The UI was just shown. We can simulate a click on a rating element (i.e., "star").
simulateVote(flowId, 1);
break;
}
case "Heartbeat:Voted": {
info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
break;
}
case "Heartbeat:NotificationClosed": {
info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
is(gBrowser.tabs.length, expectedTabCount, "Engagement URL should open in a new tab.");
gBrowser.removeCurrentTab();
done();
break;
}
default:
// We are not expecting other states for this test.
ok(false, "Unexpected notification received: " + aEventName);
}
});
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
}
];

View File

@ -1294,9 +1294,13 @@ MarkupView.prototype = {
return this._destroyer;
}
this._destroyer = promise.resolve();
// Note that if the toolbox is closed, this will work fine, but will fail
// in case the browser is closed and will trigger a noSuchActor message.
this._destroyer = this._hideBoxModel();
// We ignore the promise that |_hideBoxModel| returns, since we should still
// proceed with the rest of destruction if it fails.
this._hideBoxModel();
this._elt.removeEventListener("click", this._onMouseClick, false);

View File

@ -93,32 +93,24 @@ let WebAudioEditorController = {
PropertiesView.resetUI();
},
// Since node create and connect are probably executed back to back,
// and the controller's `_onCreateNode` needs to look up type,
// the edge creation could be called before the graph node is actually
// created. This way, we can check and listen for the event before
// adding an edge.
_waitForNodeCreation: function (sourceActor, destActor) {
let deferred = defer();
let source = gAudioNodes.get(sourceActor.actorID);
let dest = gAudioNodes.get(destActor.actorID);
// Since node events (create, disconnect, connect) are all async,
// we have to make sure to wait that the node has finished creating
// before performing an operation on it.
getNode: function* (nodeActor) {
let id = nodeActor.actorID;
let node = gAudioNodes.get(id);
if (!source || !dest) {
if (!node) {
let { resolve, promise } = defer();
gAudioNodes.on("add", function createNodeListener (createdNode) {
if (sourceActor.actorID === createdNode.id)
source = createdNode;
if (destActor.actorID === createdNode.id)
dest = createdNode;
if (source && dest) {
if (createdNode.id === id) {
gAudioNodes.off("add", createNodeListener);
deferred.resolve([source, dest]);
resolve(createdNode);
}
});
node = yield promise;
}
else {
deferred.resolve([source, dest]);
}
return deferred.promise;
return node;
},
/**
@ -202,7 +194,8 @@ let WebAudioEditorController = {
* Called when a node is connected to another node.
*/
_onConnectNode: Task.async(function* ({ source: sourceActor, dest: destActor }) {
let [source, dest] = yield WebAudioEditorController._waitForNodeCreation(sourceActor, destActor);
let source = yield WebAudioEditorController.getNode(sourceActor);
let dest = yield WebAudioEditorController.getNode(destActor);
source.connect(dest);
}),
@ -210,22 +203,24 @@ let WebAudioEditorController = {
* Called when a node is conneceted to another node's AudioParam.
*/
_onConnectParam: Task.async(function* ({ source: sourceActor, dest: destActor, param }) {
let [source, dest] = yield WebAudioEditorController._waitForNodeCreation(sourceActor, destActor);
let source = yield WebAudioEditorController.getNode(sourceActor);
let dest = yield WebAudioEditorController.getNode(destActor);
source.connect(dest, param);
}),
/**
* Called when a node is disconnected.
*/
_onDisconnectNode: function(nodeActor) {
let node = gAudioNodes.get(nodeActor.actorID);
_onDisconnectNode: Task.async(function* (nodeActor) {
let node = yield WebAudioEditorController.getNode(nodeActor);
node.disconnect();
},
}),
/**
* Called when a node param is changed.
*/
_onChangeParam: function({ actor, param, value }) {
window.emit(EVENTS.CHANGE_PARAM, gAudioNodes.get(actor.actorID), param, value);
}
_onChangeParam: Task.async(function* ({ actor, param, value }) {
let node = yield WebAudioEditorController.getNode(actor);
window.emit(EVENTS.CHANGE_PARAM, node, param, value);
})
};

View File

@ -11,6 +11,7 @@ support-files =
doc_connect-multi-param.html
doc_iframe-context.html
doc_automation.html
doc_bug_1125817.html
440hz_sine.ogg
head.js
@ -39,6 +40,7 @@ support-files =
[browser_wa_reset-03.js]
[browser_wa_reset-04.js]
[browser_wa_navigate.js]
[browser_wa_controller-01.js]
[browser_wa_graph-click.js]
[browser_wa_graph-markers.js]

View File

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Bug 1125817
* Tests to ensure that disconnecting a node immediately
* after creating it does not fail.
*/
const BUG_1125817_URL = EXAMPLE_URL + "doc_bug_1125817.html";
add_task(function*() {
let { target, panel } = yield initWebAudioEditor(BUG_1125817_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
reload(target);
let [actors] = yield Promise.all([
once(gAudioNodes, "add", 2),
once(gAudioNodes, "disconnect")
]);
ok(true, "Successfully disconnected a just-created node.");
yield teardown(target);
});

View File

@ -0,0 +1,23 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Web Audio Editor test page</title>
</head>
<body>
<script type="text/javascript;version=1.8">
"use strict";
let ctx = new AudioContext();
let osc = ctx.createOscillator();
osc.frequency.value = 200;
osc.disconnect();
</script>
</body>
</html>

View File

@ -138,10 +138,6 @@ HelperAppLauncherDialog.prototype = {
messageContainer.appendChild(fragment);
},
promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
throw new Components.Exception("Async version must be used", Cr.NS_ERROR_NOT_AVAILABLE);
},
promptForSaveToFileAsync: function hald_promptForSaveToFileAsync(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
let file = null;
let prefs = Services.prefs;

View File

@ -34,6 +34,9 @@ browser.jar:
skin/classic/browser/fullscreen-darknoise.png
skin/classic/browser/Geolocation-16.png
skin/classic/browser/Geolocation-64.png
skin/classic/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg)
skin/classic/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg)
skin/classic/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg)
skin/classic/browser/identity.png
skin/classic/browser/identity-icons-generic.png
skin/classic/browser/identity-icons-https.png

View File

@ -36,6 +36,9 @@ browser.jar:
skin/classic/browser/Geolocation-16@2x.png
skin/classic/browser/Geolocation-64.png
skin/classic/browser/Geolocation-64@2x.png
skin/classic/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg)
skin/classic/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg)
skin/classic/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg)
skin/classic/browser/identity.png
skin/classic/browser/identity@2x.png
skin/classic/browser/identity-icons-generic.png

View File

@ -195,3 +195,110 @@
background-image: -moz-image-rect(url("chrome://browser/skin/dots@2x.png"), 0, 14, 100%, 0);
}
}
/* Notification overrides for Heartbeat UI */
notification.heartbeat {
background-color: #F1F1F1;
%ifdef XP_MACOSX
background-image: linear-gradient(-179deg, #FBFBFB 0%, #EBEBEB 100%);
%endif
box-shadow: 0px 1px 0px 0px rgba(0,0,0,0.35);
}
@keyframes pulse-onshow {
0% {
opacity: 0;
transform: scale(1.0);
}
25% {
opacity: 1;
transform: scale(1.1);
}
50% {
transform: scale(1.0);
}
75% {
transform: scale(1.1);
}
100% {
transform: scale(1.0);
}
}
@keyframes pulse-twice {
0% {
transform: scale(1.1);
}
50% {
transform: scale(0.8);
}
100% {
transform: scale(1);
}
}
.messageText.heartbeat {
color: #333333;
font-weight: normal;
font-family: "Lucida Grande", Segoe, Ubuntu;
font-size: 14px;
line-height: 16px;
text-shadow: none;
}
.messageImage.heartbeat {
width: 36px;
height: 36px;
-moz-margin-end: 10px;
}
.messageImage.heartbeat.pulse-onshow {
animation-name: pulse-onshow;
animation-duration: 1.5s;
animation-iteration-count: 1;
animation-timing-function: cubic-bezier(.7,1.8,.9,1.1);
}
.messageImage.heartbeat.pulse-twice {
animation-name: pulse-twice;
animation-duration: 1s;
animation-iteration-count: 2;
animation-timing-function: linear;
}
/* Heartbeat UI Rating Star Classes */
.heartbeat > #star-rating-container {
display: -moz-box;
}
.heartbeat > #star-rating-container > #star5 {
-moz-box-ordinal-group: 5;
}
.heartbeat > #star-rating-container > #star4 {
-moz-box-ordinal-group: 4;
}
.heartbeat > #star-rating-container > #star3 {
-moz-box-ordinal-group: 3;
}
.heartbeat > #star-rating-container > #star2 {
-moz-box-ordinal-group: 2;
}
.heartbeat > #star-rating-container > .star-x {
background: url("chrome://browser/skin/heartbeat-star-off.svg");
cursor: pointer;
width: 24px;
height: 24px;
}
.heartbeat > #star-rating-container > .star-x:hover,
.heartbeat > #star-rating-container > .star-x:hover ~ .star-x {
background: url("chrome://browser/skin/heartbeat-star-lit.svg");
cursor: pointer;
width: 24px;
height: 24px;
}

View File

@ -28,6 +28,7 @@ ul {
}
#errorPageContainer {
position: relative;
min-width: 320px;
max-width: 512px;
}
@ -74,15 +75,10 @@ button:disabled {
div#certificateErrorReporting {
display: none;
float:right;
float: right;
/* Align with the "Try Again" button */
margin-top:24px;
margin-right:24px;
}
div#certificateErrorReporting a,
div#certificateErrorReportingPanel a {
color: #0095DD;
margin-top: 24px;
-moz-margin-end: 24px;
}
div#certificateErrorReporting a {
@ -94,7 +90,11 @@ div#certificateErrorReporting a:hover {
}
span.downArrow {
font-size: 0.9em;
display: inline-block;
vertical-align: middle;
font-size: 0.6em;
-moz-margin-start: 0.5em;
transform: scaleY(0.7);
}
div#certificateErrorReportingPanel {
@ -106,11 +106,18 @@ div#certificateErrorReportingPanel {
* makes the overall div look uneven */
padding: 0 12px 12px 12px;
box-shadow: 0 0 4px #ddd;
position: relative;
width: 75%;
left: 34%;
font-size: 0.9em;
top: 8px;
position: absolute;
width: 75%;
margin-top: 10px;
}
div#certificateErrorReportingPanel:-moz-dir(ltr) {
left: 34%;
}
div#certificateErrorReportingPanel:-moz-dir(rtl) {
right: 0;
}
span#hostname {

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="288px" height="248px" viewBox="0 0 288 248" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title> + Line 14</title>
<desc>Created with Sketch.</desc>
<defs>
<path id="path-1" d="M144,246.857143 C141.214272,246.857143 138.857152,245.892867 136.928571,243.964286 L36.6428571,147.214286 C35.5714232,146.357139 34.0982237,144.964295 32.2232143,143.035714 C30.3482049,141.107133 27.3750204,137.59824 23.3035714,132.508929 C19.2321225,127.419617 15.5893018,122.196455 12.375,116.839286 C9.16069821,111.482116 6.29465545,105.000038 3.77678571,97.3928571 C1.25891598,89.7856763 0,82.392893 0,75.2142857 C0,51.6427393 6.80350339,33.2143521 20.4107143,19.9285714 C34.0179252,6.64279071 52.8213086,0 76.8214286,0 C83.4643189,0 90.2410369,1.1517742 97.1517857,3.45535714 C104.062535,5.75894009 110.491042,8.86605187 116.4375,12.7767857 C122.383958,16.6875196 127.499979,20.3571257 131.785714,23.7857143 C136.07145,27.2143029 140.142838,30.8571236 144,34.7142857 C147.857162,30.8571236 151.92855,27.2143029 156.214286,23.7857143 C160.500021,20.3571257 165.616042,16.6875196 171.5625,12.7767857 C177.508958,8.86605187 183.937465,5.75894009 190.848214,3.45535714 C197.758963,1.1517742 204.535681,0 211.178571,0 C235.178691,0 253.982075,6.64279071 267.589286,19.9285714 C281.196497,33.2143521 288,51.6427393 288,75.2142857 C288,98.8929755 275.732266,122.999877 251.196429,147.535714 L151.071429,243.964286 C149.142847,245.892867 146.785728,246.857143 144,246.857143 L144,246.857143 Z"/>
</defs>
<g id="Prompt---Spec" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="--+-Line-14" sketch:type="MSLayerGroup" transform="translate(0.000000, -1.000000)">
<path d="M144,248.571429 C141.214272,248.571429 138.857152,247.607152 136.928571,245.678571 L36.6428571,148.928571 C35.5714232,148.071424 34.0982237,146.678581 32.2232143,144.75 C30.3482049,142.821419 27.3750204,139.312525 23.3035714,134.223214 C19.2321225,129.133903 15.5893018,123.910741 12.375,118.553571 C9.16069821,113.196402 6.29465545,106.714324 3.77678571,99.1071429 C1.25891598,91.499962 0,84.1071788 0,76.9285714 C0,53.357025 6.80350339,34.9286379 20.4107143,21.6428571 C34.0179252,8.35707643 52.8213086,1.71428571 76.8214286,1.71428571 C83.4643189,1.71428571 90.2410369,2.86605991 97.1517857,5.16964286 C104.062535,7.4732258 110.491042,10.5803376 116.4375,14.4910714 C122.383958,18.4018053 127.499979,22.0714114 131.785714,25.5 C136.07145,28.9285886 140.142838,32.5714093 144,36.4285714 C147.857162,32.5714093 151.92855,28.9285886 156.214286,25.5 C160.500021,22.0714114 165.616042,18.4018053 171.5625,14.4910714 C177.508958,10.5803376 183.937465,7.4732258 190.848214,5.16964286 C197.758963,2.86605991 204.535681,1.71428571 211.178571,1.71428571 C235.178691,1.71428571 253.982075,8.35707643 267.589286,21.6428571 C281.196497,34.9286379 288,53.357025 288,76.9285714 C288,100.607261 275.732266,124.714163 251.196429,149.25 L151.071429,245.678571 C149.142847,247.607152 146.785728,248.571429 144,248.571429 L144,248.571429 Z" id="-" fill="#D74345" sketch:type="MSShapeGroup"/>
<g id="Line-14" transform="translate(0.000000, 0.714286)">
<mask id="mask-2" sketch:name="Mask" fill="white">
<use xlink:href="#path-1"/>
</mask>
<use id="Mask" sketch:type="MSShapeGroup" xlink:href="#path-1"/>
<path d="M-166,115.135254 C-166,115.135254 0.595052083,115.135254 2.9765625,115.135254 L91.9101562,115.135254 L97.9638977,100.101562 L105.430695,115.135254 L114.893585,115.135254 L131.129913,189.53125 L148.161163,57 L165.348663,131.027344 L172.272491,115.135254 L250.84967,115.135254 L428.259813,115.135254" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" sketch:type="MSShapeGroup" mask="url(#mask-2)">
<g transform="translate(131.129906, 123.265625) scale(1, -1) translate(-131.129906, -123.265625) "/>
</path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,428 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Toolbar"
x="0px"
y="0px"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve"
inkscape:version="0.48.5 r10040"
width="100%"
height="100%"
sodipodi:docname="star-lit.svg"><metadata
id="metadata4111"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs4109" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1440"
inkscape:window-height="838"
id="namedview4107"
showgrid="false"
inkscape:zoom="41.7193"
inkscape:cx="6.7712219"
inkscape:cy="7.3971752"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Toolbar" />
<g
id="g3926">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-238.2,6h-6.1l2.7-2.7c0.4-0.4,0.5-1.1,0.2-1.4l-1.2-1.2 c-0.3-0.3-1-0.3-1.4,0.2l-6.4,6.4c-0.1,0.1-0.1,0.2-0.2,0.2l0,0c0,0,0,0,0,0c0,0.1-0.1,0.1-0.1,0.1c0,0.1-0.1,0.2-0.1,0.3 c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0.1,0,0.2,0.1,0.3c0,0.1,0.1,0.1,0.1,0.1c0,0,0,0,0,0l0,0c0.1,0.1,0.1,0.2,0.2,0.2l6.4,6.4 c0.4,0.4,1.1,0.5,1.4,0.2l1.2-1.2c0.3-0.3,0.3-1-0.2-1.4l-2.8-2.8h6.2c0.6,0,1-0.4,1-1V7C-237.2,6.4-237.6,6-238.2,6z"
id="path3928" />
</g>
<g
id="g3930">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-212.7,6.3l-1,1c-0.2,0.2-0.2,0.4-0.2,0.7c0,0.3,0,0.5,0.2,0.7 l0.8,0.9l4.5,4.5c0.5,0.5,1.2,0.6,1.6,0.2l0.8-0.9c0.4-0.4,0.3-1.1-0.2-1.6l-1.8-1.8h4.7c0.6,0,1-0.5,1-1V7c0-0.6-0.5-1-1-1h-4.8 l1.9-1.9c0.5-0.5,0.6-1.3,0.2-1.6l-0.8-0.9c-0.4-0.4-1.1-0.3-1.6,0.2L-212.7,6.3z"
id="path3932" />
</g>
<g
id="g3934">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-167.3,9.7l1-1c0.2-0.2,0.2-0.4,0.2-0.7c0-0.3,0-0.5-0.2-0.7 l-0.8-0.9l-4.5-4.5c-0.5-0.5-1.2-0.6-1.6-0.2l-0.8,0.9c-0.4,0.4-0.3,1.1,0.2,1.6l1.8,1.8h-4.7c-0.6,0-1,0.5-1,1v2c0,0.6,0.5,1,1,1 h4.8l-1.9,1.9c-0.5,0.5-0.6,1.3-0.2,1.6l0.8,0.9c0.4,0.4,1.1,0.3,1.6-0.2L-167.3,9.7z"
id="path3936" />
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-129.5,8h-7l2.8-2.8c-0.7-0.5-1.4-0.7-2.3-0.7c-2.2,0-4,1.8-4,4 c0,2.2,1.8,4,4,4c1.4,0,2.7-0.7,3.4-1.9l2.3,1c-1.1,2-3.2,3.4-5.7,3.4c-3.6,0-6.5-2.9-6.5-6.5c0-3.6,2.9-6.5,6.5-6.5 c1.5,0,2.9,0.5,4.1,1.4l2.4-2.4V8z"
id="path3938" />
<g
id="g3940">
<g
id="g3942">
<polygon
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
points="-93.6,3.8 -95.8,1.7 -100,5.9 -104.3,1.6 -106.4,3.8 -102.1,8 -106.3,12.2 -104.1,14.3 -99.9,10.2 -95.7,14.4 -93.6,12.2 -97.8,8.1 "
id="polygon3944" />
</g>
</g>
<g
id="g3946">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-64,1l-8,7h2l6-5l6,5h2L-64,1z M-69,8v7h4v-5h2v5h4V8l-5-4L-69,8 z"
id="path3948" />
</g>
<path
fill="#00A3F2"
d="M-28,3.6l1,2l0.5,0.9l1,0.2l2.3,0.4l-1.7,1.8l-0.7,0.7l0.1,1l0.4,2.4l-2-1l-0.9-0.5l-0.9,0.5l-2,1l0.4-2.4 l0.1-1l-0.7-0.7l-1.7-1.8l2.4-0.4l1-0.2l0.5-0.9L-28,3.6 M-28,0c-0.3,0-0.6,0.2-0.8,0.7l-2,4.1l-4.3,0.7c-1,0.2-1.2,0.9-0.5,1.6 l3.1,3.3l-0.7,4.6c-0.1,0.7,0.2,1.1,0.7,1.1c0.2,0,0.4-0.1,0.6-0.2l3.9-2.1l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1 l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6l-4.3-0.7l-2-4.1C-27.4,0.2-27.7,0-28,0L-28,0z"
id="path3950" />
<path
fill="#00A3F2"
d="M8,0C7.7,0,7.4,0.2,7.2,0.7l-2,4.1L0.9,5.5c-1,0.2-1.2,0.9-0.5,1.6l3.1,3.3l-0.7,4.6C2.7,15.6,3,16,3.4,16 c0.2,0,0.4-0.1,0.6-0.2L8,13.7l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6 l-4.3-0.7l-2-4.1C8.6,0.2,8.3,0,8,0L8,0z"
id="path3952"
style="fill:#0095dd;fill-opacity:1" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M44,15.2c-4,0-7.2-3.2-7.2-7.2c0-4,3.2-7.2,7.2-7.2 c4,0,7.2,3.2,7.2,7.2C51.2,12,48,15.2,44,15.2z M44,3c-2.8,0-5,2.2-5,5c0,2.7,2.2,5,5,5c2.8,0,5-2.2,5-5C49,5.3,46.8,3,44,3z M43.7,8.9C43.3,8.8,43,8.4,43,8V5c0-0.6,0.4-1,1-1c0.6,0,1,0.4,1,1v2.8c1.1,1.1,2,3.2,2,3.2S44.8,10,43.7,8.9z"
id="path3954" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M86.7,9.1l-5.6,5.5C80.8,14.9,80.4,15,80,15 c-0.4,0-0.8-0.1-1.1-0.4l-5.6-5.5C72.7,8.5,72.9,8,73.8,8H77l0-6c0-0.6,0.4-1,1-1h4c0.6,0,1,0.4,1,1v6h3.2C87.1,8,87.3,8.5,86.7,9.1 z"
id="path3956" />
<path
fill="#00A3F2"
d="M-241,52c0.5,0,1-0.4,1-1v-4c0,0,0.1-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2 c0-1.8-0.9-2-1.5-2c-1.1,0-1.1,0.8-1.8,0.8c-0.6,0-0.8-0.8-0.8-0.8v-2c0-0.6-0.4-1-1-1h-3c0,0-0.8-0.1-0.8-0.8 c0-0.6,0.8-0.6,0.8-1.8c0-0.6-0.2-1.5-2-1.5c-1.8,0-2,0.9-2,1.5c0,1.1,0.8,1.1,0.8,1.8c0,0.6-0.8,0.8-0.8,0.8h-3c-0.5,0-1,0.4-1,1 l0,2.5c0,0-0.1,1.5,1.1,1.5c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1c-1.2,0-1.1,1.5-1.1,1.5l0,3.5 c0,0.6,0.4,1,1,1h3.8c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2c1,0,1.8,0.7,1.8,1.2c0,1-1,1.1-1,1.9 c0,1.2,1.5,1.1,1.5,1.1H-241z"
id="path3958" />
<g
id="g3960">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-200.4,41.9h-0.3v0h-14.4v0h-0.3c-0.2,0-0.4,0.2-0.4,0.4c0,0,0.3,2.2,0.5,4.4 c0.2,2.5,0.2,4.2,0.2,4.2c0,0.2,0.2,0.4,0.4,0.4h13.7c0.2,0,0.4-0.2,0.4-0.4c0,0,0.1-2.1,0.2-4.2c0.1-2.2,0.5-4.4,0.5-4.4 C-200.1,42.1-200.2,41.9-200.4,41.9z"
id="path3962" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-200.8,41.3v-2c0-0.3-0.2-0.5-0.5-0.5l-6.7,0l-0.8-1.1c0,0-0.6-0.9-1.2-0.9h-4.2 c-0.6,0-1,0.5-1,1v1l0,2.5H-200.8z"
id="path3964" />
</g>
<g
id="g3966">
<g
id="g3968">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-169,37h-7.5c-0.8,0-1.5,0.7-1.5,1.5v11c0,0.8,0.7,1.5,1.5,1.5 h9c0.8,0,1.5-0.7,1.5-1.5V40L-169,37z M-170,41v-3.3l3.3,3.3H-170z"
id="path3970" />
</g>
</g>
<g
id="g3972">
<g
id="g3974">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-132.5,38c-0.4,0.2-0.9,0.6-1.3,1.2c-0.3,0.4-0.5,0.9-0.8,1.3 c0.7,0.3,1.3,0.8,1.7,1.4c0.4,0.7,0.7,1.5,0.6,2.3c-0.1,1.3-1,2.4-2.1,3c-0.5-0.5-1.4-2.1-1.4-2.1c0,0,0,1.8-0.6,3 c-0.4,0.8-0.9,1.5-1.6,2.1c1,0.4,2,0.6,3,0.6l0-0.1c0,0,0.1,0.1,0.1,0.1c2.1,0,4.1-0.3,4.1-0.3s-0.9-0.6-1.3-0.9 c1.3-0.7,2.2-2,2.5-3.3c0.2-0.5,0.3-1.1,0.4-1.7C-128.7,41.9-130.2,39.3-132.5,38z M-139.8,44.2c0-1.4,0.8-2.8,2.2-3.5 c0.5,0.5,1.6,2,1.6,2s0-3.6,1.2-5.6c-4.3-0.5-6.8,0.3-6.8,0.3s1.2,0.4,1.6,0.8c-0.1,0.1-0.2,0.1-0.3,0.2c-1,0.7-1.7,1.6-2.1,2.7 c-0.3,0.6-0.5,1.3-0.5,2c-0.3,2.8,1.1,5.4,3.4,6.7c0.4-0.3,0.8-0.6,1.2-1.1c0.4-0.4,0.7-0.9,1-1.4 C-138.7,46.9-139.7,45.7-139.8,44.2z"
id="path3976" />
</g>
</g>
<g
id="g3978">
<g
id="g3980">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-96.6,41.5c-3.3-4-9.6-4.5-9.6-4.5c-0.4,0-0.8,0.3-0.8,0.7v1.4 c0,0.4,0.3,0.7,0.8,0.7c0,0,4.5,0.1,7.1,3.2c3.5,3.6,3.2,7.2,3.2,7.2c0,0.4,0.3,0.8,0.8,0.8h1.5c0.4,0,0.7-0.3,0.7-0.8 C-93,50.2-93.3,44.5-96.6,41.5z M-106.2,42c-0.4,0-0.8,0.3-0.8,0.7V44c0,0.4,0.3,0.7,0.8,0.7c0,0,2.4,0.1,3.8,1.4 c1.9,1.7,1.8,4.2,1.8,4.2c0,0.4,0.2,0.8,0.6,0.8h1.5c0.4,0,0.5-0.3,0.5-0.8c0,0-0.4-3.9-2.5-5.9C-102.5,42.4-106.2,42-106.2,42z M-105,47c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C-103,47.9-103.9,47-105,47z"
id="path3982" />
</g>
</g>
<path
fill="none"
d="M-102.6,34.4c0.5,0,1-0.4,1-1v-4c0,0,0.1-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2 c0-1.8-0.9-2-1.5-2c-1.1,0-1.1,0.8-1.8,0.8c-0.6,0-0.8-0.8-0.8-0.8v-2c0-0.6-0.4-1-1-1h-3c0,0-0.8-0.1-0.8-0.8 c0-0.6,0.8-0.6,0.8-1.8c0-0.6-0.2-1.5-2-1.5c-1.8,0-2,0.9-2,1.5c0,1.1,0.8,1.1,0.8,1.8c0,0.6-0.8,0.8-0.8,0.8h-3c-0.5,0-1,0.4-1,1 l0,2.5c0,0-0.1,1.5,1.1,1.5c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1c-1.2,0-1.1,1.5-1.1,1.5l0,3.5 c0,0.6,0.4,1,1,1h3.8c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2c1,0,1.8,0.7,1.8,1.2c0,1-1,1.1-1,1.9 c0,1.2,1.5,1.1,1.5,1.1H-102.6z"
id="path3984" />
<g
id="g3986">
<g
id="g3988">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-63,46.7l-0.3-0.1L-63,52l2.6-3.8L-63,46.7z M-63.3,46l0.3,0 l6,2.9V36l-14,11l4.6-0.6L-63,52l-1.9-5.7l-0.3-0.1l0.3,0L-65,46l7-8L-63.3,46L-63.3,46z"
id="path3990" />
</g>
</g>
<g
id="g3992">
<g
id="g3994">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-23,37h-10c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h10 c1.1,0,2-0.9,2-2V39C-21,37.9-21.9,37-23,37z M-23,46c0,1.7-1.3,2-3,2h-4c-1.7,0-3-1.3-3-3v-4c0-1.7,1.3-3,3-3h4c1.7,0,3,1.3,3,3 V46z"
id="path3996" />
</g>
</g>
<path
fill="#00A3F2"
d="M-23.7,42.6c0-0.8-0.2-1.5-0.6-2c-0.4-0.5-1-0.7-1.7-0.7c-0.4,0-0.8,0.1-1.1,0.3s-0.6,0.4-0.8,0.8 c-0.2-0.4-0.4-0.6-0.7-0.8c-0.3-0.2-0.7-0.2-1.2-0.2c-0.4,0-0.7,0-1.1,0.1s-0.7,0.2-1,0.4l0.3,0.7c0.6-0.3,1.2-0.5,1.7-0.5 c0.4,0,0.8,0.1,1,0.3c0.2,0.2,0.3,0.6,0.3,1.1v0.4l-1,0c-0.9,0-1.6,0.2-2.1,0.5c-0.5,0.3-0.7,0.8-0.7,1.4c0,0.6,0.2,1,0.5,1.3 c0.3,0.3,0.8,0.5,1.4,0.5c0.5,0,0.9-0.1,1.3-0.3c0.4-0.2,0.7-0.5,1-1c0.5,0.9,1.2,1.3,2.3,1.3c0.4,0,0.7,0,1-0.1 c0.3-0.1,0.6-0.2,0.9-0.3v-0.8c-0.3,0.1-0.6,0.3-0.9,0.3c-0.3,0.1-0.6,0.1-0.9,0.1c-1.2,0-1.8-0.7-1.8-2.2h3.9V42.6z M-28.5,43.6 c0,0.6-0.2,1-0.5,1.3c-0.3,0.3-0.7,0.5-1.3,0.5c-0.3,0-0.6-0.1-0.8-0.2c-0.2-0.2-0.3-0.4-0.3-0.8c0-0.4,0.2-0.7,0.5-0.9 c0.3-0.2,0.8-0.3,1.5-0.3l0.9,0V43.6z M-27.5,42.4c0-0.6,0.2-1,0.4-1.3c0.3-0.3,0.6-0.5,1.1-0.5c0.4,0,0.8,0.2,1,0.5 c0.2,0.3,0.4,0.8,0.4,1.3H-27.5z"
id="path3998" />
<g
id="g4000">
<g
id="g4002">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M14.5,37h-13C0.7,37,0,37.7,0,38.5v11C0,50.3,0.7,51,1.5,51h13 c0.8,0,1.5-0.7,1.5-1.5v-11C16,37.7,15.3,37,14.5,37z M6.5,38C6.8,38,7,38.2,7,38.5C7,38.8,6.8,39,6.5,39C6.2,39,6,38.8,6,38.5 C6,38.2,6.2,38,6.5,38z M4.4,38c0.3,0,0.5,0.2,0.5,0.5c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C3.9,38.2,4.2,38,4.4,38z M2.5,38C2.8,38,3,38.2,3,38.5C3,38.8,2.8,39,2.5,39C2.2,39,2,38.8,2,38.5C2,38.2,2.2,38,2.5,38z M14,48c0,0.6-0.4,1-1,1H3 c-0.6,0-1-0.4-1-1v-6c0-0.6,0.4-1,1-1h10c0.6,0,1,0.4,1,1V48z"
id="path4004" />
</g>
</g>
<path
fill="#00A3F2"
d="M51.2,45.9L51.2,45.9c-0.4-0.1-0.7-0.3-1-0.6c-0.1-0.1-0.1-0.1-0.2-0.2c-0.1-0.1-0.1-0.1-0.2-0.2 c0-0.1-0.1-0.1-0.1-0.2c0-0.1-0.1-0.1-0.1-0.2c-0.3-0.6-0.4-1.5-0.5-2.4c-0.3-2.2-0.1-3.8-3-3.8h-4.4c-2.9,0-2.6,1.6-2.9,3.8 c-0.1,0.9-0.3,1.8-0.6,2.4c0,0,0,0,0,0c-0.1,0.1-0.1,0.2-0.2,0.3c0,0,0,0-0.1,0.1c-0.1,0.2-0.2,0.3-0.4,0.4 c-0.3,0.2-0.5,0.4-0.9,0.5l0,0C36.4,46,36,46.3,36,46.8v1.9c0,0.5,0.4,0.9,0.9,0.9h14.1c0.5,0,0.9-0.4,0.9-0.9v-1.9 C52,46.3,51.6,46,51.2,45.9z M46.4,43.8c0,0.1-0.1,0.2-0.2,0.2h-1.6v1.6c0,0.1-0.1,0.2-0.2,0.2h-0.5c-0.1,0-0.2-0.1-0.2-0.2V44h-1.6 c-0.1,0-0.2-0.1-0.2-0.2v-0.5c0-0.1,0.1-0.2,0.2-0.2h1.6v-1.6c0-0.1,0.1-0.2,0.2-0.2h0.5c0.1,0,0.2,0.1,0.2,0.2v1.6h1.6 c0.1,0,0.2,0.1,0.2,0.2V43.8z"
id="path4006" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M87.7,42c-0.1,1.1,0.2,2.5-1.3,4.4c-1.6,2.1-3.1,2.1-3.4,2.1 c-1.8-0.1-2-1.5-3-1.5c-0.9,0-1.6,1.4-3,1.5c-0.3,0-1.9,0-3.4-2c-1.5-1.9-1.2-3.3-1.3-4.4S72,39.7,72,39.7s0.7,0.7,1.6,0.8 c0.9,0.1,1.1-0.3,3-0.9c2.1-0.6,3.4,1.9,3.4,1.9s1.4-2.4,3.4-1.9c1.9,0.6,2,0.9,2.9,0.9c0.9-0.1,1.7-0.8,1.7-0.8S87.8,40.9,87.7,42z M76.9,42.5c-1.1-0.3-1.6,0.2-2.1,0.4C74.4,43,74,43.1,74,43.1s0.1,0.7,1.2,1.2c1.1,0.6,3.5,0.3,3.5,0.3S78.9,42.9,76.9,42.5z M85.2,42.9c-0.5-0.2-1-0.6-2.1-0.4c-2,0.4-1.8,2.1-1.8,2.1s2.4,0.3,3.5-0.3c1.1-0.6,1.2-1.2,1.2-1.2S85.6,43,85.2,42.9z"
id="path4008" />
<g
id="g4010">
<g
id="g4012">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-237.5,84.9l-3.3-3.3c0.5-0.9,0.9-1.9,0.9-2.9c0-3-2.4-5.5-5.5-5.5 c-3,0-5.5,2.4-5.5,5.5c0,3,2.4,5.5,5.5,5.5c1.1,0,2.1-0.3,3-0.9l3.3,3.3c0.4,0.4,1,0.4,1.4,0l0.2-0.2 C-237.1,85.9-237.1,85.3-237.5,84.9z M-245.4,82c-1.8,0-3.3-1.5-3.3-3.4c0-1.9,1.5-3.4,3.3-3.4c1.8,0,3.3,1.5,3.3,3.4 C-242,80.5-243.5,82-245.4,82z"
id="path4014" />
</g>
</g>
<g
id="g4016">
<g
id="g4018">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-214.5,86.3l0.2,0.2c0.4,0.4,1,0.4,1.4,0l3.3-3.3c0.9,0.6,1.9,0.9,3,0.9 c3,0,5.5-2.4,5.5-5.5c0-3-2.4-5.5-5.5-5.5c-3,0-5.5,2.4-5.5,5.5c0,1.1,0.3,2.1,0.9,2.9l-3.3,3.3 C-214.9,85.3-214.9,85.9-214.5,86.3z M-210,78.6c0-1.9,1.5-3.4,3.3-3.4c1.8,0,3.3,1.5,3.3,3.4c0,1.9-1.5,3.4-3.3,3.4 C-208.5,82-210,80.5-210,78.6z"
id="path4020" />
</g>
</g>
<g
id="g4022">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-165,77.5h-1v-2c0-0.6-0.4-1-1-1v-1c0-0.6-0.4-1-1-1h-8 c-0.6,0-1,0.4-1,1v1c-0.6,0-1,0.4-1,1v2h-1c-0.6,0-1,0.4-1,1v5c0,0.6,0.4,1,1,1h2v-1h0.5l-1.5,3h12l-1.5-3h0.5v1h2c0.6,0,1-0.4,1-1 v-5C-164,77.9-164.4,77.5-165,77.5z M-176.5,80.5h-1c-0.3,0-0.5-0.2-0.5-0.5c0-0.3,0.2-0.5,0.5-0.5h1c0.3,0,0.5,0.2,0.5,0.5 C-176,80.2-176.2,80.5-176.5,80.5z M-176,85.5l0.9-2h6.2l0.9,2H-176z M-168,77.5c0,0.6-0.4,1-1,1h-6c-0.6,0-1-0.4-1-1v-3 c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V77.5z"
id="path4024" />
</g>
<g
id="g4026">
<path
fill="#00A3F2"
d="M-135,73l1.9,1.9l-2.6,2.6c-0.5,0.5-0.5,1.4,0.1,2c0.6,0.6,1.5,0.6,2,0.1l2.6-2.6l1.9,1.9v-6H-135z M-138.5,80.4l-2.6,2.6L-143,81v6h6l-1.9-1.9l2.6-2.6c0.5-0.5,0.5-1.4-0.1-2C-137.1,79.9-138,79.9-138.5,80.4z"
id="path4028" />
</g>
<g
id="g4030">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-93.5,73.5c-0.6-0.6-1.5-0.6-2-0.1l-2.6,2.6L-100,74v6h6 l-1.9-1.9l2.6-2.6C-92.9,75-92.9,74.1-93.5,73.5z M-104.1,81.9l-2.6,2.6c-0.5,0.5-0.5,1.4,0.1,2c0.6,0.6,1.5,0.6,2,0.1l2.6-2.6 l1.9,1.9v-6h-6L-104.1,81.9z"
id="path4032" />
</g>
<g
id="g4034">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M14.6,75.1l-2.3,2.3c-0.5,0.5-1.3,0.5-1.7,0 c-0.5-0.5-0.5-1.2,0-1.7l2.4-2.3c-0.6-0.3-1.2-0.5-1.9-0.5C8.8,72.9,7,74.7,7,77c0,0.5,0.1,1.1,0.3,1.5l-5.9,5.8 c-0.6,0.6-0.6,1.7,0,2.3c0.6,0.6,1.7,0.6,2.3,0l6-5.9c0.4,0.2,0.9,0.3,1.4,0.3c2.2,0,4.1-1.8,4.1-4.1 C15.1,76.3,14.9,75.7,14.6,75.1z M2.5,86.4c-0.5,0-0.9-0.4-0.9-0.9c0-0.5,0.4-0.9,0.9-0.9c0.5,0,0.9,0.4,0.9,0.9 C3.4,86,3,86.4,2.5,86.4z"
id="path4036" />
</g>
<path
fill="#00A3F2"
d="M-22.7,83.9l-1.1-1.1c0.2-0.3,0.3-0.6,0.5-0.9c0.1-0.3,0.2-0.6,0.3-0.9l1.6,0c0.3,0,0.5-0.2,0.5-0.5l0-1 c0-0.3-0.2-0.5-0.5-0.5l-1.6,0c-0.1-0.6-0.4-1.2-0.7-1.8l1.1-1.1c0.2-0.2,0.2-0.5,0-0.7l-0.7-0.7c-0.2-0.2-0.5-0.2-0.7,0l-1.1,1.1 c-0.3-0.2-0.6-0.3-0.9-0.5c-0.3-0.1-0.6-0.2-0.9-0.3l0-1.6c0-0.3-0.2-0.5-0.5-0.5l-1,0c-0.3,0-0.5,0.2-0.5,0.5l0,1.6 c-0.6,0.1-1.2,0.4-1.8,0.7l-1.1-1.1c-0.2-0.2-0.5-0.2-0.7,0l-0.7,0.7c-0.2,0.2-0.2,0.5,0,0.7l1.1,1.1c-0.2,0.3-0.3,0.6-0.5,0.9 c-0.1,0.3-0.2,0.6-0.3,0.9l-1.6,0c-0.3,0-0.5,0.2-0.5,0.5l0,1c0,0.3,0.2,0.5,0.5,0.5l1.6,0c0.1,0.6,0.4,1.2,0.7,1.8l-1.1,1.1 c-0.2,0.2-0.2,0.5,0,0.7l0.7,0.7c0.2,0.2,0.5,0.2,0.7,0l1.1-1.1c0.3,0.2,0.6,0.3,0.9,0.5c0.3,0.1,0.6,0.2,0.9,0.3l0,1.6 c0,0.3,0.2,0.5,0.5,0.5l1,0c0.3,0,0.5-0.2,0.5-0.5l0-1.6c0.6-0.1,1.2-0.4,1.8-0.7l1.1,1.1c0.2,0.2,0.5,0.2,0.7,0l0.7-0.7 C-22.5,84.4-22.5,84.1-22.7,83.9z M-28.8,82.1c-1.2-0.5-1.7-1.8-1.3-3c0.5-1.2,1.8-1.7,3-1.3c1.2,0.5,1.7,1.8,1.3,3 C-26.4,82-27.7,82.6-28.8,82.1z"
id="path4038" />
<g
id="g4040">
<g
id="g4042">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-62,77h-4c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h4 c0.6,0,1-0.4,1-1v-4C-61,77.4-61.4,77-62,77z M-62,81c0,0.6-0.4,1-1,1h-2c-0.6,0-1-0.4-1-1v-1c0-0.6,0.4-1,1-1h2c0.6,0,1,0.4,1,1 V81z M-64,87l3-3h-6L-64,87z M-67,76h6l-3-3L-67,76z M-60,76.9v6l3-3L-60,76.9z M-68,82.9v-6l-3,3L-68,82.9z"
id="path4044" />
</g>
</g>
<g
id="g4046">
<g
id="g4048">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M37.5,76.5h13c0.8,0,1.5-0.7,1.5-1.5c0-0.8-0.7-1.5-1.5-1.5h-13 c-0.8,0-1.5,0.7-1.5,1.5C36,75.8,36.7,76.5,37.5,76.5z M50.5,78.5h-13c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h13 c0.8,0,1.5-0.7,1.5-1.5C52,79.2,51.3,78.5,50.5,78.5z M50.5,83.5h-13c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h13 c0.8,0,1.5-0.7,1.5-1.5C52,84.2,51.3,83.5,50.5,83.5z"
id="path4050" />
</g>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M85.6,87c-0.8,0-1.5-0.2-2.3-1.3c-0.8-1.1-1.7-2.5-1.7-2.5 s-0.7-0.9-1.1-1.6c-0.4-0.7-1.1-0.5-1.1-0.5s-2.8-4.6-3.3-5.4c-0.7-1,0.6-2.7,0.6-2.7l4.4,7c0,0,1.3,1.9,1.9,2.3 c0.5,0.4,1.4-0.4,2.8,0.9C87.7,85,87.1,87,85.6,87z M85.4,84.1c-0.9-1-1.7-0.9-1.9-0.6c-0.2,0.3,0,1.2,0.4,1.7 c0.4,0.5,0.8,0.7,1.4,0.7C85.9,86,86.4,85.2,85.4,84.1z M81.6,79.4l-1.2-1.8l2.9-4.6c0,0,1.2,1.7,0.6,2.7 C83.6,76.1,82.5,78,81.6,79.4z M77,82.3c0.3-0.3,1-1.1,1.4-1.7l0.8,1.2c-0.4,0.6-0.9,1.4-0.9,1.4s-0.9,1.4-1.7,2.5 c-0.8,1.1-1.5,1.3-2.3,1.3c-1.4,0-2.1-2-0.1-3.8C75.6,82,76.5,82.7,77,82.3z M74.6,84.1c-0.9,1-0.4,1.8,0.2,1.8c0.6,0,1-0.2,1.4-0.7 c0.4-0.5,0.6-1.5,0.4-1.7C76.3,83.2,75.5,83.1,74.6,84.1z"
id="path4052" />
<g
id="g4054">
<g
id="g4056">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-243,111c0-0.1-2-2-2-2c-1.7,0-5,0-5,0c-0.6,0-1,0.4-1,1v8 c0,0.6,0.4,1,1,1h4v-6.1c0-0.3,0.2-0.5,0.5-0.5h2.5V111z M-246,112v-2l2,2H-246z M-239,113c-1.7,0-5,0-5,0c-0.6,0-1,0.4-1,1v8 c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1c0,0,0-4.8,0-7C-237,115-239,113-239,113z M-240,116v-2l2,2H-240z"
id="path4058" />
</g>
</g>
<g
id="g4060">
<g
id="g4062">
<path
fill="#00A3F2"
d="M-204.5,111h-1.3l0,0c0,0-0.2-2-2.2-2c-2,0-2.2,2-2.2,2l0,0h-1.3c-0.8,0-1.5,0.7-1.5,1.5v9 c0,0.8,0.7,1.5,1.5,1.5h7c0.8,0,1.5-0.7,1.5-1.5v-9C-203,111.7-203.7,111-204.5,111z M-210.7,112.1l0.8-0.4l0.4-0.2l0-0.4 c0-0.2,0.2-1.3,1.5-1.3c1.2,0,1.4,1.1,1.5,1.3l0,0.4l0.4,0.2l0.8,0.4l0.3,0.7h-6.1L-210.7,112.1z M-210.1,120.4l-2.8-4.9l3.3-1.9 h4.3l1.8,3.1L-210.1,120.4z"
id="path4064" />
</g>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-177,115h10v2h-10V115z"
id="path4066" />
<g
id="g4068">
<g
id="g4070">
<polygon
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
points="-131,115 -135,115 -135,111 -137,111 -137,115 -141,115 -141,117 -137,117 -137,121 -135,121 -135,117 -131,117 "
id="polygon4072" />
</g>
</g>
<g
id="g4074">
<path
fill="#00A3F2"
d="M-94,111.7l-3,2.7v-2c0-0.8-0.6-1.4-1.3-1.4h-7.4c-0.7,0-1.3,0.6-1.3,1.4v7.2c0,0.8,0.6,1.4,1.3,1.4h7.4 c0.7,0,1.3-0.6,1.3-1.4v-2.1l3,2.7c0.3,0.3,0.6,0.4,1,0.3v-9.1C-93.3,111.4-93.7,111.5-94,111.7z"
id="path4076" />
</g>
<g
id="g4078">
<g
id="g4080">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-61.9,112h-3l3.8,4l-3.8,4h3.1l3.8-4L-61.9,112z M-66.9,112h-3 l3.8,4l-3.8,4h3.1l3.8-4L-66.9,112z"
id="path4082" />
</g>
</g>
<g
id="g4084">
<g
id="g4086">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-23,111.5h-2l-3-3l-3,3h-2c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2 h10c1.1,0,2-0.9,2-2v-8C-21,112.4-21.9,111.5-23,111.5z M-32,121.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1S-31.4,121.5-32,121.5z M-32,118.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1S-31.4,118.5-32,118.5z M-32,115.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1 S-31.4,115.5-32,115.5z M-23,121.5h-7v-2h7V121.5z M-23,118.5h-7v-2h7V118.5z M-23,115.5h-7v-2h7V115.5z"
id="path4088" />
</g>
</g>
<g
id="g4090">
<g
id="g4092">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M2,115h7c0.6,0,1-0.4,1-1v-4c0-0.6-0.4-1-1-1H2c-0.6,0-1,0.4-1,1 v4C1,114.6,1.4,115,2,115z M14,109h-2c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h2c0.6,0,1-0.4,1-1v-4C15,109.4,14.6,109,14,109z M14,116H8c-0.6,0-1,0.4-1,1v5c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1v-5C15,116.4,14.6,116,14,116z M5,116H2c-0.6,0-1,0.4-1,1v5 c0,0.6,0.4,1,1,1h3c0.6,0,1-0.4,1-1v-5C6,116.4,5.6,116,5,116z"
id="path4094" />
</g>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M45.8,116c0,0-0.6,0.8-1.8,0.8c-1.2,0-1.8-0.8-1.8-0.8l-6-5.1 c0.3-0.5,0.9-0.9,1.6-0.9h12.4c0.7,0,1.3,0.4,1.6,0.9L45.8,116z M42.2,117.7c0,0,0.6,0.8,1.8,0.8c1.2,0,1.8-0.8,1.8-0.8l6.2-5.4v8 c0,0.9-0.8,1.7-1.8,1.7H37.8c-1,0-1.8-0.8-1.8-1.7v-8L42.2,117.7z"
id="path4096" />
<g
id="g4098">
<g
id="g4100">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-237.5,145h-13c-0.8,0-1.5,0.7-1.5,1.5v11 c0,0.8,0.7,1.5,1.5,1.5h13c0.8,0,1.5-0.7,1.5-1.5v-11C-236,145.7-236.7,145-237.5,145z M-245.5,146c0.3,0,0.5,0.2,0.5,0.5 c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C-246,146.2-245.8,146-245.5,146z M-247.6,146c0.3,0,0.5,0.2,0.5,0.5 c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5C-248.1,146.2-247.8,146-247.6,146z M-249.5,146c0.3,0,0.5,0.2,0.5,0.5 c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5C-250,146.2-249.8,146-249.5,146z M-250,156v-6c0-0.6,0.4-1,1-1h7v8h-7 C-249.6,157-250,156.6-250,156z M-238,156c0,0.6-0.4,1-1,1h-1v-8h1c0.6,0,1,0.4,1,1V156z"
id="path4102" />
</g>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#00A3F2"
d="M-213.2,156c-1.3-1.2-2.2-2.8-2.2-4.6c0-3.6,3.3-6.5,7.4-6.5 c4.1,0,7.4,2.9,7.4,6.5c0,3.6-3.3,6.5-7.4,6.5c-0.8,0-1.6-0.1-2.4-0.3c-1.8,0.7-4.3,1.7-4.5,1.4C-213.9,157.9-213.5,156.8-213.2,156 z"
id="path4104" />
<rect
id="_x3C_Slice_x3E_"
fill="none"
width="16"
height="16" />
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,428 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Toolbar"
x="0px"
y="0px"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve"
inkscape:version="0.48.5 r10040"
width="100%"
height="100%"
sodipodi:docname="star-off.svg"><metadata
id="metadata5255"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs5253" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="710"
inkscape:window-height="480"
id="namedview5251"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="7.6064963"
inkscape:cy="8"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="Toolbar" />
<g
id="g5070">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-238.2,6h-6.1l2.7-2.7c0.4-0.4,0.5-1.1,0.2-1.4l-1.2-1.2 c-0.3-0.3-1-0.3-1.4,0.2l-6.4,6.4c-0.1,0.1-0.1,0.2-0.2,0.2l0,0c0,0,0,0,0,0c0,0.1-0.1,0.1-0.1,0.1c0,0.1-0.1,0.2-0.1,0.3 c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0.1,0,0.2,0.1,0.3c0,0.1,0.1,0.1,0.1,0.1c0,0,0,0,0,0l0,0c0.1,0.1,0.1,0.2,0.2,0.2l6.4,6.4 c0.4,0.4,1.1,0.5,1.4,0.2l1.2-1.2c0.3-0.3,0.3-1-0.2-1.4l-2.8-2.8h6.2c0.6,0,1-0.4,1-1V7C-237.2,6.4-237.6,6-238.2,6z"
id="path5072" />
</g>
<g
id="g5074">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-212.7,6.3l-1,1c-0.2,0.2-0.2,0.4-0.2,0.7c0,0.3,0,0.5,0.2,0.7 l0.8,0.9l4.5,4.5c0.5,0.5,1.2,0.6,1.6,0.2l0.8-0.9c0.4-0.4,0.3-1.1-0.2-1.6l-1.8-1.8h4.7c0.6,0,1-0.5,1-1V7c0-0.6-0.5-1-1-1h-4.8 l1.9-1.9c0.5-0.5,0.6-1.3,0.2-1.6l-0.8-0.9c-0.4-0.4-1.1-0.3-1.6,0.2L-212.7,6.3z"
id="path5076" />
</g>
<g
id="g5078">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-167.3,9.7l1-1c0.2-0.2,0.2-0.4,0.2-0.7c0-0.3,0-0.5-0.2-0.7 l-0.8-0.9l-4.5-4.5c-0.5-0.5-1.2-0.6-1.6-0.2l-0.8,0.9c-0.4,0.4-0.3,1.1,0.2,1.6l1.8,1.8h-4.7c-0.6,0-1,0.5-1,1v2c0,0.6,0.5,1,1,1 h4.8l-1.9,1.9c-0.5,0.5-0.6,1.3-0.2,1.6l0.8,0.9c0.4,0.4,1.1,0.3,1.6-0.2L-167.3,9.7z"
id="path5080" />
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-129.5,8h-7l2.8-2.8c-0.7-0.5-1.4-0.7-2.3-0.7c-2.2,0-4,1.8-4,4 c0,2.2,1.8,4,4,4c1.4,0,2.7-0.7,3.4-1.9l2.3,1c-1.1,2-3.2,3.4-5.7,3.4c-3.6,0-6.5-2.9-6.5-6.5c0-3.6,2.9-6.5,6.5-6.5 c1.5,0,2.9,0.5,4.1,1.4l2.4-2.4V8z"
id="path5082" />
<g
id="g5084">
<g
id="g5086">
<polygon
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
points="-93.6,3.8 -95.8,1.7 -100,5.9 -104.3,1.6 -106.4,3.8 -102.1,8 -106.3,12.2 -104.1,14.3 -99.9,10.2 -95.7,14.4 -93.6,12.2 -97.8,8.1 "
id="polygon5088" />
</g>
</g>
<g
id="g5090">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-64,1l-8,7h2l6-5l6,5h2L-64,1z M-69,8v7h4v-5h2v5h4V8l-5-4L-69,8 z"
id="path5092" />
</g>
<path
fill="#231F20"
d="M-28,3.6l1,2l0.5,0.9l1,0.2l2.3,0.4l-1.7,1.8l-0.7,0.7l0.1,1l0.4,2.4l-2-1l-0.9-0.5l-0.9,0.5l-2,1l0.4-2.4 l0.1-1l-0.7-0.7l-1.7-1.8l2.4-0.4l1-0.2l0.5-0.9L-28,3.6 M-28,0c-0.3,0-0.6,0.2-0.8,0.7l-2,4.1l-4.3,0.7c-1,0.2-1.2,0.9-0.5,1.6 l3.1,3.3l-0.7,4.6c-0.1,0.7,0.2,1.1,0.7,1.1c0.2,0,0.4-0.1,0.6-0.2l3.9-2.1l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1 l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6l-4.3-0.7l-2-4.1C-27.4,0.2-27.7,0-28,0L-28,0z"
id="path5094" />
<path
fill="#231F20"
d="M8,0C7.7,0,7.4,0.2,7.2,0.7l-2,4.1L0.9,5.5c-1,0.2-1.2,0.9-0.5,1.6l3.1,3.3l-0.7,4.6C2.7,15.6,3,16,3.4,16 c0.2,0,0.4-0.1,0.6-0.2L8,13.7l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6 l-4.3-0.7l-2-4.1C8.6,0.2,8.3,0,8,0L8,0z"
id="path5096"
style="fill:#c0c0c0;fill-opacity:1" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M44,15.2c-4,0-7.2-3.2-7.2-7.2c0-4,3.2-7.2,7.2-7.2 c4,0,7.2,3.2,7.2,7.2C51.2,12,48,15.2,44,15.2z M44,3c-2.8,0-5,2.2-5,5c0,2.7,2.2,5,5,5c2.8,0,5-2.2,5-5C49,5.3,46.8,3,44,3z M43.7,8.9C43.3,8.8,43,8.4,43,8V5c0-0.6,0.4-1,1-1c0.6,0,1,0.4,1,1v2.8c1.1,1.1,2,3.2,2,3.2S44.8,10,43.7,8.9z"
id="path5098" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M86.7,9.1l-5.6,5.5C80.8,14.9,80.4,15,80,15 c-0.4,0-0.8-0.1-1.1-0.4l-5.6-5.5C72.7,8.5,72.9,8,73.8,8H77l0-6c0-0.6,0.4-1,1-1h4c0.6,0,1,0.4,1,1v6h3.2C87.1,8,87.3,8.5,86.7,9.1 z"
id="path5100" />
<path
fill="#231F20"
d="M-241,52c0.5,0,1-0.4,1-1v-4c0,0,0.1-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2 c0-1.8-0.9-2-1.5-2c-1.1,0-1.1,0.8-1.8,0.8c-0.6,0-0.8-0.8-0.8-0.8v-2c0-0.6-0.4-1-1-1h-3c0,0-0.8-0.1-0.8-0.8 c0-0.6,0.8-0.6,0.8-1.8c0-0.6-0.2-1.5-2-1.5c-1.8,0-2,0.9-2,1.5c0,1.1,0.8,1.1,0.8,1.8c0,0.6-0.8,0.8-0.8,0.8h-3c-0.5,0-1,0.4-1,1 l0,2.5c0,0-0.1,1.5,1.1,1.5c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1c-1.2,0-1.1,1.5-1.1,1.5l0,3.5 c0,0.6,0.4,1,1,1h3.8c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2c1,0,1.8,0.7,1.8,1.2c0,1-1,1.1-1,1.9 c0,1.2,1.5,1.1,1.5,1.1H-241z"
id="path5102" />
<g
id="g5104">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-200.4,41.9h-0.3v0h-14.4v0h-0.3c-0.2,0-0.4,0.2-0.4,0.4c0,0,0.3,2.2,0.5,4.4 c0.2,2.5,0.2,4.2,0.2,4.2c0,0.2,0.2,0.4,0.4,0.4h13.7c0.2,0,0.4-0.2,0.4-0.4c0,0,0.1-2.1,0.2-4.2c0.1-2.2,0.5-4.4,0.5-4.4 C-200.1,42.1-200.2,41.9-200.4,41.9z"
id="path5106" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-200.8,41.3v-2c0-0.3-0.2-0.5-0.5-0.5l-6.7,0l-0.8-1.1c0,0-0.6-0.9-1.2-0.9h-4.2 c-0.6,0-1,0.5-1,1v1l0,2.5H-200.8z"
id="path5108" />
</g>
<g
id="g5110">
<g
id="g5112">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-169,37h-7.5c-0.8,0-1.5,0.7-1.5,1.5v11c0,0.8,0.7,1.5,1.5,1.5 h9c0.8,0,1.5-0.7,1.5-1.5V40L-169,37z M-170,41v-3.3l3.3,3.3H-170z"
id="path5114" />
</g>
</g>
<g
id="g5116">
<g
id="g5118">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-132.5,38c-0.4,0.2-0.9,0.6-1.3,1.2c-0.3,0.4-0.5,0.9-0.8,1.3 c0.7,0.3,1.3,0.8,1.7,1.4c0.4,0.7,0.7,1.5,0.6,2.3c-0.1,1.3-1,2.4-2.1,3c-0.5-0.5-1.4-2.1-1.4-2.1c0,0,0,1.8-0.6,3 c-0.4,0.8-0.9,1.5-1.6,2.1c1,0.4,2,0.6,3,0.6l0-0.1c0,0,0.1,0.1,0.1,0.1c2.1,0,4.1-0.3,4.1-0.3s-0.9-0.6-1.3-0.9 c1.3-0.7,2.2-2,2.5-3.3c0.2-0.5,0.3-1.1,0.4-1.7C-128.7,41.9-130.2,39.3-132.5,38z M-139.8,44.2c0-1.4,0.8-2.8,2.2-3.5 c0.5,0.5,1.6,2,1.6,2s0-3.6,1.2-5.6c-4.3-0.5-6.8,0.3-6.8,0.3s1.2,0.4,1.6,0.8c-0.1,0.1-0.2,0.1-0.3,0.2c-1,0.7-1.7,1.6-2.1,2.7 c-0.3,0.6-0.5,1.3-0.5,2c-0.3,2.8,1.1,5.4,3.4,6.7c0.4-0.3,0.8-0.6,1.2-1.1c0.4-0.4,0.7-0.9,1-1.4 C-138.7,46.9-139.7,45.7-139.8,44.2z"
id="path5120" />
</g>
</g>
<g
id="g5122">
<g
id="g5124">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-96.6,41.5c-3.3-4-9.6-4.5-9.6-4.5c-0.4,0-0.8,0.3-0.8,0.7v1.4 c0,0.4,0.3,0.7,0.8,0.7c0,0,4.5,0.1,7.1,3.2c3.5,3.6,3.2,7.2,3.2,7.2c0,0.4,0.3,0.8,0.8,0.8h1.5c0.4,0,0.7-0.3,0.7-0.8 C-93,50.2-93.3,44.5-96.6,41.5z M-106.2,42c-0.4,0-0.8,0.3-0.8,0.7V44c0,0.4,0.3,0.7,0.8,0.7c0,0,2.4,0.1,3.8,1.4 c1.9,1.7,1.8,4.2,1.8,4.2c0,0.4,0.2,0.8,0.6,0.8h1.5c0.4,0,0.5-0.3,0.5-0.8c0,0-0.4-3.9-2.5-5.9C-102.5,42.4-106.2,42-106.2,42z M-105,47c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C-103,47.9-103.9,47-105,47z"
id="path5126" />
</g>
</g>
<path
fill="none"
d="M-102.6,34.4c0.5,0,1-0.4,1-1v-4c0,0,0.1-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2 c0-1.8-0.9-2-1.5-2c-1.1,0-1.1,0.8-1.8,0.8c-0.6,0-0.8-0.8-0.8-0.8v-2c0-0.6-0.4-1-1-1h-3c0,0-0.8-0.1-0.8-0.8 c0-0.6,0.8-0.6,0.8-1.8c0-0.6-0.2-1.5-2-1.5c-1.8,0-2,0.9-2,1.5c0,1.1,0.8,1.1,0.8,1.8c0,0.6-0.8,0.8-0.8,0.8h-3c-0.5,0-1,0.4-1,1 l0,2.5c0,0-0.1,1.5,1.1,1.5c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1c-1.2,0-1.1,1.5-1.1,1.5l0,3.5 c0,0.6,0.4,1,1,1h3.8c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2c1,0,1.8,0.7,1.8,1.2c0,1-1,1.1-1,1.9 c0,1.2,1.5,1.1,1.5,1.1H-102.6z"
id="path5128" />
<g
id="g5130">
<g
id="g5132">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-63,46.7l-0.3-0.1L-63,52l2.6-3.8L-63,46.7z M-63.3,46l0.3,0 l6,2.9V36l-14,11l4.6-0.6L-63,52l-1.9-5.7l-0.3-0.1l0.3,0L-65,46l7-8L-63.3,46L-63.3,46z"
id="path5134" />
</g>
</g>
<g
id="g5136">
<g
id="g5138">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-23,37h-10c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h10 c1.1,0,2-0.9,2-2V39C-21,37.9-21.9,37-23,37z M-23,46c0,1.7-1.3,2-3,2h-4c-1.7,0-3-1.3-3-3v-4c0-1.7,1.3-3,3-3h4c1.7,0,3,1.3,3,3 V46z"
id="path5140" />
</g>
</g>
<path
fill="#231F20"
d="M-23.7,42.6c0-0.8-0.2-1.5-0.6-2c-0.4-0.5-1-0.7-1.7-0.7c-0.4,0-0.8,0.1-1.1,0.3s-0.6,0.4-0.8,0.8 c-0.2-0.4-0.4-0.6-0.7-0.8c-0.3-0.2-0.7-0.2-1.2-0.2c-0.4,0-0.7,0-1.1,0.1s-0.7,0.2-1,0.4l0.3,0.7c0.6-0.3,1.2-0.5,1.7-0.5 c0.4,0,0.8,0.1,1,0.3c0.2,0.2,0.3,0.6,0.3,1.1v0.4l-1,0c-0.9,0-1.6,0.2-2.1,0.5c-0.5,0.3-0.7,0.8-0.7,1.4c0,0.6,0.2,1,0.5,1.3 c0.3,0.3,0.8,0.5,1.4,0.5c0.5,0,0.9-0.1,1.3-0.3c0.4-0.2,0.7-0.5,1-1c0.5,0.9,1.2,1.3,2.3,1.3c0.4,0,0.7,0,1-0.1 c0.3-0.1,0.6-0.2,0.9-0.3v-0.8c-0.3,0.1-0.6,0.3-0.9,0.3c-0.3,0.1-0.6,0.1-0.9,0.1c-1.2,0-1.8-0.7-1.8-2.2h3.9V42.6z M-28.5,43.6 c0,0.6-0.2,1-0.5,1.3c-0.3,0.3-0.7,0.5-1.3,0.5c-0.3,0-0.6-0.1-0.8-0.2c-0.2-0.2-0.3-0.4-0.3-0.8c0-0.4,0.2-0.7,0.5-0.9 c0.3-0.2,0.8-0.3,1.5-0.3l0.9,0V43.6z M-27.5,42.4c0-0.6,0.2-1,0.4-1.3c0.3-0.3,0.6-0.5,1.1-0.5c0.4,0,0.8,0.2,1,0.5 c0.2,0.3,0.4,0.8,0.4,1.3H-27.5z"
id="path5142" />
<g
id="g5144">
<g
id="g5146">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M14.5,37h-13C0.7,37,0,37.7,0,38.5v11C0,50.3,0.7,51,1.5,51h13 c0.8,0,1.5-0.7,1.5-1.5v-11C16,37.7,15.3,37,14.5,37z M6.5,38C6.8,38,7,38.2,7,38.5C7,38.8,6.8,39,6.5,39C6.2,39,6,38.8,6,38.5 C6,38.2,6.2,38,6.5,38z M4.4,38c0.3,0,0.5,0.2,0.5,0.5c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C3.9,38.2,4.2,38,4.4,38z M2.5,38C2.8,38,3,38.2,3,38.5C3,38.8,2.8,39,2.5,39C2.2,39,2,38.8,2,38.5C2,38.2,2.2,38,2.5,38z M14,48c0,0.6-0.4,1-1,1H3 c-0.6,0-1-0.4-1-1v-6c0-0.6,0.4-1,1-1h10c0.6,0,1,0.4,1,1V48z"
id="path5148" />
</g>
</g>
<path
fill="#231F20"
d="M51.2,45.9L51.2,45.9c-0.4-0.1-0.7-0.3-1-0.6c-0.1-0.1-0.1-0.1-0.2-0.2c-0.1-0.1-0.1-0.1-0.2-0.2 c0-0.1-0.1-0.1-0.1-0.2c0-0.1-0.1-0.1-0.1-0.2c-0.3-0.6-0.4-1.5-0.5-2.4c-0.3-2.2-0.1-3.8-3-3.8h-4.4c-2.9,0-2.6,1.6-2.9,3.8 c-0.1,0.9-0.3,1.8-0.6,2.4c0,0,0,0,0,0c-0.1,0.1-0.1,0.2-0.2,0.3c0,0,0,0-0.1,0.1c-0.1,0.2-0.2,0.3-0.4,0.4 c-0.3,0.2-0.5,0.4-0.9,0.5l0,0C36.4,46,36,46.3,36,46.8v1.9c0,0.5,0.4,0.9,0.9,0.9h14.1c0.5,0,0.9-0.4,0.9-0.9v-1.9 C52,46.3,51.6,46,51.2,45.9z M46.4,43.8c0,0.1-0.1,0.2-0.2,0.2h-1.6v1.6c0,0.1-0.1,0.2-0.2,0.2h-0.5c-0.1,0-0.2-0.1-0.2-0.2V44h-1.6 c-0.1,0-0.2-0.1-0.2-0.2v-0.5c0-0.1,0.1-0.2,0.2-0.2h1.6v-1.6c0-0.1,0.1-0.2,0.2-0.2h0.5c0.1,0,0.2,0.1,0.2,0.2v1.6h1.6 c0.1,0,0.2,0.1,0.2,0.2V43.8z"
id="path5150" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M87.7,42c-0.1,1.1,0.2,2.5-1.3,4.4c-1.6,2.1-3.1,2.1-3.4,2.1 c-1.8-0.1-2-1.5-3-1.5c-0.9,0-1.6,1.4-3,1.5c-0.3,0-1.9,0-3.4-2c-1.5-1.9-1.2-3.3-1.3-4.4S72,39.7,72,39.7s0.7,0.7,1.6,0.8 c0.9,0.1,1.1-0.3,3-0.9c2.1-0.6,3.4,1.9,3.4,1.9s1.4-2.4,3.4-1.9c1.9,0.6,2,0.9,2.9,0.9c0.9-0.1,1.7-0.8,1.7-0.8S87.8,40.9,87.7,42z M76.9,42.5c-1.1-0.3-1.6,0.2-2.1,0.4C74.4,43,74,43.1,74,43.1s0.1,0.7,1.2,1.2c1.1,0.6,3.5,0.3,3.5,0.3S78.9,42.9,76.9,42.5z M85.2,42.9c-0.5-0.2-1-0.6-2.1-0.4c-2,0.4-1.8,2.1-1.8,2.1s2.4,0.3,3.5-0.3c1.1-0.6,1.2-1.2,1.2-1.2S85.6,43,85.2,42.9z"
id="path5152" />
<g
id="g5154">
<g
id="g5156">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-237.5,84.9l-3.3-3.3c0.5-0.9,0.9-1.9,0.9-2.9c0-3-2.4-5.5-5.5-5.5 c-3,0-5.5,2.4-5.5,5.5c0,3,2.4,5.5,5.5,5.5c1.1,0,2.1-0.3,3-0.9l3.3,3.3c0.4,0.4,1,0.4,1.4,0l0.2-0.2 C-237.1,85.9-237.1,85.3-237.5,84.9z M-245.4,82c-1.8,0-3.3-1.5-3.3-3.4c0-1.9,1.5-3.4,3.3-3.4c1.8,0,3.3,1.5,3.3,3.4 C-242,80.5-243.5,82-245.4,82z"
id="path5158" />
</g>
</g>
<g
id="g5160">
<g
id="g5162">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M-214.5,86.3l0.2,0.2c0.4,0.4,1,0.4,1.4,0l3.3-3.3c0.9,0.6,1.9,0.9,3,0.9 c3,0,5.5-2.4,5.5-5.5c0-3-2.4-5.5-5.5-5.5c-3,0-5.5,2.4-5.5,5.5c0,1.1,0.3,2.1,0.9,2.9l-3.3,3.3 C-214.9,85.3-214.9,85.9-214.5,86.3z M-210,78.6c0-1.9,1.5-3.4,3.3-3.4c1.8,0,3.3,1.5,3.3,3.4c0,1.9-1.5,3.4-3.3,3.4 C-208.5,82-210,80.5-210,78.6z"
id="path5164" />
</g>
</g>
<g
id="g5166">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-165,77.5h-1v-2c0-0.6-0.4-1-1-1v-1c0-0.6-0.4-1-1-1h-8 c-0.6,0-1,0.4-1,1v1c-0.6,0-1,0.4-1,1v2h-1c-0.6,0-1,0.4-1,1v5c0,0.6,0.4,1,1,1h2v-1h0.5l-1.5,3h12l-1.5-3h0.5v1h2c0.6,0,1-0.4,1-1 v-5C-164,77.9-164.4,77.5-165,77.5z M-176.5,80.5h-1c-0.3,0-0.5-0.2-0.5-0.5c0-0.3,0.2-0.5,0.5-0.5h1c0.3,0,0.5,0.2,0.5,0.5 C-176,80.2-176.2,80.5-176.5,80.5z M-176,85.5l0.9-2h6.2l0.9,2H-176z M-168,77.5c0,0.6-0.4,1-1,1h-6c-0.6,0-1-0.4-1-1v-3 c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V77.5z"
id="path5168" />
</g>
<g
id="g5170">
<path
fill="#231F20"
d="M-135,73l1.9,1.9l-2.6,2.6c-0.5,0.5-0.5,1.4,0.1,2c0.6,0.6,1.5,0.6,2,0.1l2.6-2.6l1.9,1.9v-6H-135z M-138.5,80.4l-2.6,2.6L-143,81v6h6l-1.9-1.9l2.6-2.6c0.5-0.5,0.5-1.4-0.1-2C-137.1,79.9-138,79.9-138.5,80.4z"
id="path5172" />
</g>
<g
id="g5174">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-93.5,73.5c-0.6-0.6-1.5-0.6-2-0.1l-2.6,2.6L-100,74v6h6 l-1.9-1.9l2.6-2.6C-92.9,75-92.9,74.1-93.5,73.5z M-104.1,81.9l-2.6,2.6c-0.5,0.5-0.5,1.4,0.1,2c0.6,0.6,1.5,0.6,2,0.1l2.6-2.6 l1.9,1.9v-6h-6L-104.1,81.9z"
id="path5176" />
</g>
<g
id="g5178">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M14.6,75.1l-2.3,2.3c-0.5,0.5-1.3,0.5-1.7,0 c-0.5-0.5-0.5-1.2,0-1.7l2.4-2.3c-0.6-0.3-1.2-0.5-1.9-0.5C8.8,72.9,7,74.7,7,77c0,0.5,0.1,1.1,0.3,1.5l-5.9,5.8 c-0.6,0.6-0.6,1.7,0,2.3c0.6,0.6,1.7,0.6,2.3,0l6-5.9c0.4,0.2,0.9,0.3,1.4,0.3c2.2,0,4.1-1.8,4.1-4.1 C15.1,76.3,14.9,75.7,14.6,75.1z M2.5,86.4c-0.5,0-0.9-0.4-0.9-0.9c0-0.5,0.4-0.9,0.9-0.9c0.5,0,0.9,0.4,0.9,0.9 C3.4,86,3,86.4,2.5,86.4z"
id="path5180" />
</g>
<path
fill="#231F20"
d="M-22.7,83.9l-1.1-1.1c0.2-0.3,0.3-0.6,0.5-0.9c0.1-0.3,0.2-0.6,0.3-0.9l1.6,0c0.3,0,0.5-0.2,0.5-0.5l0-1 c0-0.3-0.2-0.5-0.5-0.5l-1.6,0c-0.1-0.6-0.4-1.2-0.7-1.8l1.1-1.1c0.2-0.2,0.2-0.5,0-0.7l-0.7-0.7c-0.2-0.2-0.5-0.2-0.7,0l-1.1,1.1 c-0.3-0.2-0.6-0.3-0.9-0.5c-0.3-0.1-0.6-0.2-0.9-0.3l0-1.6c0-0.3-0.2-0.5-0.5-0.5l-1,0c-0.3,0-0.5,0.2-0.5,0.5l0,1.6 c-0.6,0.1-1.2,0.4-1.8,0.7l-1.1-1.1c-0.2-0.2-0.5-0.2-0.7,0l-0.7,0.7c-0.2,0.2-0.2,0.5,0,0.7l1.1,1.1c-0.2,0.3-0.3,0.6-0.5,0.9 c-0.1,0.3-0.2,0.6-0.3,0.9l-1.6,0c-0.3,0-0.5,0.2-0.5,0.5l0,1c0,0.3,0.2,0.5,0.5,0.5l1.6,0c0.1,0.6,0.4,1.2,0.7,1.8l-1.1,1.1 c-0.2,0.2-0.2,0.5,0,0.7l0.7,0.7c0.2,0.2,0.5,0.2,0.7,0l1.1-1.1c0.3,0.2,0.6,0.3,0.9,0.5c0.3,0.1,0.6,0.2,0.9,0.3l0,1.6 c0,0.3,0.2,0.5,0.5,0.5l1,0c0.3,0,0.5-0.2,0.5-0.5l0-1.6c0.6-0.1,1.2-0.4,1.8-0.7l1.1,1.1c0.2,0.2,0.5,0.2,0.7,0l0.7-0.7 C-22.5,84.4-22.5,84.1-22.7,83.9z M-28.8,82.1c-1.2-0.5-1.7-1.8-1.3-3c0.5-1.2,1.8-1.7,3-1.3c1.2,0.5,1.7,1.8,1.3,3 C-26.4,82-27.7,82.6-28.8,82.1z"
id="path5182" />
<g
id="g5184">
<g
id="g5186">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-62,77h-4c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h4 c0.6,0,1-0.4,1-1v-4C-61,77.4-61.4,77-62,77z M-62,81c0,0.6-0.4,1-1,1h-2c-0.6,0-1-0.4-1-1v-1c0-0.6,0.4-1,1-1h2c0.6,0,1,0.4,1,1 V81z M-64,87l3-3h-6L-64,87z M-67,76h6l-3-3L-67,76z M-60,76.9v6l3-3L-60,76.9z M-68,82.9v-6l-3,3L-68,82.9z"
id="path5188" />
</g>
</g>
<g
id="g5190">
<g
id="g5192">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M37.5,76.5h13c0.8,0,1.5-0.7,1.5-1.5c0-0.8-0.7-1.5-1.5-1.5h-13 c-0.8,0-1.5,0.7-1.5,1.5C36,75.8,36.7,76.5,37.5,76.5z M50.5,78.5h-13c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h13 c0.8,0,1.5-0.7,1.5-1.5C52,79.2,51.3,78.5,50.5,78.5z M50.5,83.5h-13c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h13 c0.8,0,1.5-0.7,1.5-1.5C52,84.2,51.3,83.5,50.5,83.5z"
id="path5194" />
</g>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M85.6,87c-0.8,0-1.5-0.2-2.3-1.3c-0.8-1.1-1.7-2.5-1.7-2.5 s-0.7-0.9-1.1-1.6c-0.4-0.7-1.1-0.5-1.1-0.5s-2.8-4.6-3.3-5.4c-0.7-1,0.6-2.7,0.6-2.7l4.4,7c0,0,1.3,1.9,1.9,2.3 c0.5,0.4,1.4-0.4,2.8,0.9C87.7,85,87.1,87,85.6,87z M85.4,84.1c-0.9-1-1.7-0.9-1.9-0.6c-0.2,0.3,0,1.2,0.4,1.7 c0.4,0.5,0.8,0.7,1.4,0.7C85.9,86,86.4,85.2,85.4,84.1z M81.6,79.4l-1.2-1.8l2.9-4.6c0,0,1.2,1.7,0.6,2.7 C83.6,76.1,82.5,78,81.6,79.4z M77,82.3c0.3-0.3,1-1.1,1.4-1.7l0.8,1.2c-0.4,0.6-0.9,1.4-0.9,1.4s-0.9,1.4-1.7,2.5 c-0.8,1.1-1.5,1.3-2.3,1.3c-1.4,0-2.1-2-0.1-3.8C75.6,82,76.5,82.7,77,82.3z M74.6,84.1c-0.9,1-0.4,1.8,0.2,1.8c0.6,0,1-0.2,1.4-0.7 c0.4-0.5,0.6-1.5,0.4-1.7C76.3,83.2,75.5,83.1,74.6,84.1z"
id="path5196" />
<g
id="g5198">
<g
id="g5200">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-243,111c0-0.1-2-2-2-2c-1.7,0-5,0-5,0c-0.6,0-1,0.4-1,1v8 c0,0.6,0.4,1,1,1h4v-6.1c0-0.3,0.2-0.5,0.5-0.5h2.5V111z M-246,112v-2l2,2H-246z M-239,113c-1.7,0-5,0-5,0c-0.6,0-1,0.4-1,1v8 c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1c0,0,0-4.8,0-7C-237,115-239,113-239,113z M-240,116v-2l2,2H-240z"
id="path5202" />
</g>
</g>
<g
id="g5204">
<g
id="g5206">
<path
fill="#231F20"
d="M-204.5,111h-1.3l0,0c0,0-0.2-2-2.2-2c-2,0-2.2,2-2.2,2l0,0h-1.3c-0.8,0-1.5,0.7-1.5,1.5v9 c0,0.8,0.7,1.5,1.5,1.5h7c0.8,0,1.5-0.7,1.5-1.5v-9C-203,111.7-203.7,111-204.5,111z M-210.7,112.1l0.8-0.4l0.4-0.2l0-0.4 c0-0.2,0.2-1.3,1.5-1.3c1.2,0,1.4,1.1,1.5,1.3l0,0.4l0.4,0.2l0.8,0.4l0.3,0.7h-6.1L-210.7,112.1z M-210.1,120.4l-2.8-4.9l3.3-1.9 h4.3l1.8,3.1L-210.1,120.4z"
id="path5208" />
</g>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-177,115h10v2h-10V115z"
id="path5210" />
<g
id="g5212">
<g
id="g5214">
<polygon
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
points="-131,115 -135,115 -135,111 -137,111 -137,115 -141,115 -141,117 -137,117 -137,121 -135,121 -135,117 -131,117 "
id="polygon5216" />
</g>
</g>
<g
id="g5218">
<path
fill="#231F20"
d="M-94,111.7l-3,2.7v-2c0-0.8-0.6-1.4-1.3-1.4h-7.4c-0.7,0-1.3,0.6-1.3,1.4v7.2c0,0.8,0.6,1.4,1.3,1.4h7.4 c0.7,0,1.3-0.6,1.3-1.4v-2.1l3,2.7c0.3,0.3,0.6,0.4,1,0.3v-9.1C-93.3,111.4-93.7,111.5-94,111.7z"
id="path5220" />
</g>
<g
id="g5222">
<g
id="g5224">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-61.9,112h-3l3.8,4l-3.8,4h3.1l3.8-4L-61.9,112z M-66.9,112h-3 l3.8,4l-3.8,4h3.1l3.8-4L-66.9,112z"
id="path5226" />
</g>
</g>
<g
id="g5228">
<g
id="g5230">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-23,111.5h-2l-3-3l-3,3h-2c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2 h10c1.1,0,2-0.9,2-2v-8C-21,112.4-21.9,111.5-23,111.5z M-32,121.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1S-31.4,121.5-32,121.5z M-32,118.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1S-31.4,118.5-32,118.5z M-32,115.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1 S-31.4,115.5-32,115.5z M-23,121.5h-7v-2h7V121.5z M-23,118.5h-7v-2h7V118.5z M-23,115.5h-7v-2h7V115.5z"
id="path5232" />
</g>
</g>
<g
id="g5234">
<g
id="g5236">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M2,115h7c0.6,0,1-0.4,1-1v-4c0-0.6-0.4-1-1-1H2c-0.6,0-1,0.4-1,1 v4C1,114.6,1.4,115,2,115z M14,109h-2c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h2c0.6,0,1-0.4,1-1v-4C15,109.4,14.6,109,14,109z M14,116H8c-0.6,0-1,0.4-1,1v5c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1v-5C15,116.4,14.6,116,14,116z M5,116H2c-0.6,0-1,0.4-1,1v5 c0,0.6,0.4,1,1,1h3c0.6,0,1-0.4,1-1v-5C6,116.4,5.6,116,5,116z"
id="path5238" />
</g>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M45.8,116c0,0-0.6,0.8-1.8,0.8c-1.2,0-1.8-0.8-1.8-0.8l-6-5.1 c0.3-0.5,0.9-0.9,1.6-0.9h12.4c0.7,0,1.3,0.4,1.6,0.9L45.8,116z M42.2,117.7c0,0,0.6,0.8,1.8,0.8c1.2,0,1.8-0.8,1.8-0.8l6.2-5.4v8 c0,0.9-0.8,1.7-1.8,1.7H37.8c-1,0-1.8-0.8-1.8-1.7v-8L42.2,117.7z"
id="path5240" />
<g
id="g5242">
<g
id="g5244">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-237.5,145h-13c-0.8,0-1.5,0.7-1.5,1.5v11 c0,0.8,0.7,1.5,1.5,1.5h13c0.8,0,1.5-0.7,1.5-1.5v-11C-236,145.7-236.7,145-237.5,145z M-245.5,146c0.3,0,0.5,0.2,0.5,0.5 c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C-246,146.2-245.8,146-245.5,146z M-247.6,146c0.3,0,0.5,0.2,0.5,0.5 c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5C-248.1,146.2-247.8,146-247.6,146z M-249.5,146c0.3,0,0.5,0.2,0.5,0.5 c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5C-250,146.2-249.8,146-249.5,146z M-250,156v-6c0-0.6,0.4-1,1-1h7v8h-7 C-249.6,157-250,156.6-250,156z M-238,156c0,0.6-0.4,1-1,1h-1v-8h1c0.6,0,1,0.4,1,1V156z"
id="path5246" />
</g>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#231F20"
d="M-213.2,156c-1.3-1.2-2.2-2.8-2.2-4.6c0-3.6,3.3-6.5,7.4-6.5 c4.1,0,7.4,2.9,7.4,6.5c0,3.6-3.3,6.5-7.4,6.5c-0.8,0-1.6-0.1-2.4-0.3c-1.8,0.7-4.3,1.7-4.5,1.4C-213.9,157.9-213.5,156.8-213.2,156 z"
id="path5248" />
<rect
id="_x3C_Slice_x3E_"
fill="none"
width="16"
height="16" />
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -140,3 +140,95 @@ menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
}
}
%endif
@media not all and (-moz-windows-default-theme) {
#edit-controls@inAnyPanel@ > #copy-button,
#zoom-controls@inAnyPanel@ > #zoom-reset-button,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
border: 1px solid transparent;
}
panelview .toolbarbutton-1@buttonStateHover@,
toolbarbutton.subviewbutton@buttonStateHover@,
menu.subviewbutton@menuStateHover@,
menuitem.subviewbutton@menuStateHover@,
.widget-overflow-list .toolbarbutton-1@buttonStateHover@,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
border-color: ThreeDLightShadow !important;
}
panelview:not(#PanelUI-mainView) .toolbarbutton-1@buttonStateHover@,
toolbarbutton.subviewbutton@buttonStateHover@,
menu.subviewbutton@menuStateHover@,
menuitem.subviewbutton@menuStateHover@,
.widget-overflow-list .toolbarbutton-1@buttonStateHover@ {
background-color: Highlight;
color: highlighttext;
}
panelview .toolbarbutton-1:-moz-any(@buttonStateActive@,[checked=true]),
toolbarbutton.subviewbutton@buttonStateActive@,
menu.subviewbutton@menuStateActive@,
menuitem.subviewbutton@menuStateActive@,
.widget-overflow-list .toolbarbutton-1@buttonStateActive@,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
background-color: Highlight;
border-color: ThreeDLightShadow;
color: highlighttext;
box-shadow: none;
}
panelview .toolbarbutton-1[disabled],
toolbarbutton.subviewbutton[disabled],
menu.subviewbutton[disabled],
menuitem.subviewbutton[disabled],
.widget-overflow-list .toolbarbutton-1[disabled],
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton[disabled] {
text-shadow: none;
}
#PanelUI-fxa-status,
#PanelUI-help,
#PanelUI-customize {
border: 1px solid transparent;
}
#PanelUI-fxa-status:not([disabled]):hover,
#PanelUI-help:not([disabled]):hover,
#PanelUI-customize:hover,
#PanelUI-fxa-status:not([disabled]):hover:active,
#PanelUI-help:not([disabled]):hover:active,
#PanelUI-customize:hover:active {
border-color: ThreeDLightShadow;
box-shadow: none;
}
#BMB_bookmarksPopup .menu-text,
#BMB_bookmarksPopup menupopup {
color: -moz-FieldText;
}
#BMB_bookmarksPopup .subviewbutton[disabled=true] > .menu-text {
color: GrayText;
}
#BMB_bookmarksPopup menupopup[placespopup=true] > hbox {
box-shadow: none;
background: -moz-field;
border: 1px solid ThreeDShadow;
}
.subviewbutton.panel-subview-footer,
#BMB_bookmarksPopup .subviewbutton.panel-subview-footer {
color: ButtonText;
}
.subviewbutton@menuStateHover@,
menuitem.panel-subview-footer@menuStateHover@,
.subviewbutton.panel-subview-footer@buttonStateHover@,
.subviewbutton.panel-subview-footer@buttonStateActive@,
#BMB_bookmarksPopup .panel-subview-footer@menuStateHover@ > .menu-text {
background-color: Highlight;
color: highlighttext !important;
}
}

View File

@ -36,6 +36,9 @@ browser.jar:
skin/classic/browser/fullscreen-darknoise.png
skin/classic/browser/Geolocation-16.png
skin/classic/browser/Geolocation-64.png
skin/classic/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg)
skin/classic/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg)
skin/classic/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg)
skin/classic/browser/Info.png
skin/classic/browser/identity.png
skin/classic/browser/identity-icons-generic.png
@ -494,6 +497,9 @@ browser.jar:
skin/classic/aero/browser/fullscreen-darknoise.png
skin/classic/aero/browser/Geolocation-16.png
skin/classic/aero/browser/Geolocation-64.png
skin/classic/aero/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg)
skin/classic/aero/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg)
skin/classic/aero/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg)
skin/classic/aero/browser/Info.png (Info-aero.png)
skin/classic/aero/browser/identity.png (identity-aero.png)
skin/classic/aero/browser/identity-icons-generic.png

View File

@ -466,6 +466,12 @@ MouseEvent::GetMozPressure(float* aPressure)
return NS_OK;
}
bool
MouseEvent::HitCluster() const
{
return mEvent->AsMouseEventBase()->hitCluster;
}
uint16_t
MouseEvent::MozInputSource() const
{

View File

@ -83,6 +83,7 @@ public:
return GetMovementPoint().y;
}
float MozPressure() const;
bool HitCluster() const;
uint16_t MozInputSource() const;
void InitNSMouseEvent(const nsAString& aType,
bool aCanBubble, bool aCancelable,

View File

@ -147,6 +147,10 @@ NfcContentHelper.prototype = {
},
encodeNDEFRecords: function encodeNDEFRecords(records) {
if (!Array.isArray(records)) {
return null;
}
let encodedRecords = [];
for (let i = 0; i < records.length; i++) {
let record = records[i];
@ -278,6 +282,16 @@ NfcContentHelper.prototype = {
rfState: rfState});
},
callDefaultFoundHandler: function callDefaultFoundHandler(sessionToken,
isP2P,
records) {
let encodedRecords = this.encodeNDEFRecords(records);
cpmm.sendAsyncMessage("NFC:CallDefaultFoundHandler",
{sessionToken: sessionToken,
isP2P: isP2P,
records: encodedRecords});
},
// nsIObserver
observe: function observe(subject, topic, data) {
if (topic == "xpcom-shutdown") {

View File

@ -57,7 +57,8 @@ const NFC_CID =
const NFC_IPC_MSG_ENTRIES = [
{ permission: null,
messages: ["NFC:AddEventListener",
"NFC:QueryInfo"] },
"NFC:QueryInfo",
"NFC:CallDefaultFoundHandler"] },
{ permission: "nfc",
messages: ["NFC:ReadNDEF",
@ -227,6 +228,13 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
sessionToken: sessionToken});
},
callDefaultFoundHandler: function callDefaultFoundHandler(message) {
let sysMsg = new NfcTechDiscoveredSysMsg(message.sessionToken,
message.isP2P,
message.records || null);
gSystemMessenger.broadcastMessage("nfc-manager-tech-discovered", sysMsg);
},
onTagFound: function onTagFound(message) {
let target = this.eventListeners[this.focusApp] ||
this.eventListeners[NFC.SYSTEM_APP_ID];
@ -317,6 +325,9 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
}
this.nfc.sendNfcResponse(message.data);
return null;
case "NFC:CallDefaultFoundHandler":
this.callDefaultFoundHandler(message.data);
return null;
default:
return this.nfc.receiveMessage(message);
}
@ -502,11 +513,6 @@ Nfc.prototype = {
} else {
gMessageManager.onTagFound(message);
}
let sysMsg = new NfcTechDiscoveredSysMsg(message.sessionToken,
message.isP2P,
message.records || null);
gSystemMessenger.broadcastMessage("nfc-manager-tech-discovered", sysMsg);
break;
case "TechLostNotification":
message.type = "techLost";

View File

@ -111,7 +111,7 @@ interface nsINfcBrowserAPI : nsISupports
in boolean isFocus);
};
[scriptable, uuid(b5194ae8-d5d5-482f-a73f-dd0d755a1972)]
[scriptable, uuid(b35f4bf5-e1b8-45f4-b5d3-2ae9b6d5871e)]
interface nsINfcContentHelper : nsISupports
{
void init(in nsIDOMWindow window);
@ -278,4 +278,19 @@ interface nsINfcContentHelper : nsISupports
*/
void changeRFState(in DOMString rfState,
in nsINfcRequestCallback callback);
/**
* Notify parent process to call the default tagfound or peerfound event
* handler.
*
* @param sessionToken
* Session token of this event.
* @param isP2P
* Is this a P2P Session.
* @param records
* NDEF Records.
*/
void callDefaultFoundHandler(in DOMString sessionToken,
in boolean isP2P,
in nsIVariant records);
};

View File

@ -431,18 +431,29 @@ MozNFCImpl.prototype = {
},
notifyTagFound: function notifyTagFound(sessionToken, tagInfo, ndefInfo, records) {
if (!this.handleTagFound(sessionToken, tagInfo, ndefInfo, records)) {
this._nfcContentHelper.callDefaultFoundHandler(sessionToken, false, records);
};
},
/**
* Handles Tag Found event.
*
* returns true if the app could process this event, false otherwise.
*/
handleTagFound: function handleTagFound(sessionToken, tagInfo, ndefInfo, records) {
if (this.hasDeadWrapper()) {
dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
return;
return false;
}
if (!this.eventService.hasListenersFor(this.__DOM_IMPL__, "tagfound")) {
debug("ontagfound is not registered.");
return;
return false;
}
if (!this.checkPermissions(["nfc"])) {
return;
return false;
}
this.eventService.addSystemEventListener(this._window, "visibilitychange",
@ -465,6 +476,7 @@ MozNFCImpl.prototype = {
}
let eventData = {
"cancelable": true,
"tag": tag,
"ndefRecords": ndefRecords
};
@ -472,6 +484,15 @@ MozNFCImpl.prototype = {
debug("fire ontagfound " + sessionToken);
let tagEvent = new this._window.MozNFCTagEvent("tagfound", eventData);
this.__DOM_IMPL__.dispatchEvent(tagEvent);
// If defaultPrevented is false, means we need to take the default action
// for this event - redirect this event to System app. Before redirecting to
// System app, we need revoke the tag object first.
if (!tagEvent.defaultPrevented) {
this.notifyTagLost(sessionToken);
}
return tagEvent.defaultPrevented;
},
notifyTagLost: function notifyTagLost(sessionToken) {
@ -504,20 +525,31 @@ MozNFCImpl.prototype = {
},
notifyPeerFound: function notifyPeerFound(sessionToken, isPeerReady) {
if (!this.handlePeerFound(sessionToken, isPeerReady)) {
this._nfcContentHelper.callDefaultFoundHandler(sessionToken, true, null);
}
},
/**
* Handles Peer Found/Peer Ready event.
*
* returns true if the app could process this event, false otherwise.
*/
handlePeerFound: function handlePeerFound(sessionToken, isPeerReady) {
if (this.hasDeadWrapper()) {
dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
return;
return false;
}
if (!isPeerReady &&
!this.eventService.hasListenersFor(this.__DOM_IMPL__, "peerfound")) {
debug("onpeerfound is not registered.");
return;
return false;
}
let perm = isPeerReady ? ["nfc-share"] : ["nfc"];
if (!this.checkPermissions(perm)) {
return;
return false;
}
this.eventService.addSystemEventListener(this._window, "visibilitychange",
@ -525,12 +557,36 @@ MozNFCImpl.prototype = {
let peerImpl = new MozNFCPeerImpl(this._window, sessionToken);
this.nfcPeer = this._window.MozNFCPeer._create(this._window, peerImpl);
let eventData = { "peer": this.nfcPeer };
let type = (isPeerReady) ? "peerready" : "peerfound";
debug("fire on" + type + " " + sessionToken);
let event = new this._window.MozNFCPeerEvent(type, eventData);
let eventType;
let eventData = {
"peer": this.nfcPeer
};
if (isPeerReady) {
eventType = "peerready";
} else {
eventData.cancelable = true;
eventType = "peerfound";
}
debug("fire on" + eventType + " " + sessionToken);
let event = new this._window.MozNFCPeerEvent(eventType, eventData);
this.__DOM_IMPL__.dispatchEvent(event);
// For peerready we don't take the default action.
if (isPeerReady) {
return true;
}
// If defaultPrevented is false, means we need to take the default action
// for this event - redirect this event to System app. Before redirecting to
// System app, we need revoke the peer object first.
if (!event.defaultPrevented) {
this.notifyPeerLost(sessionToken);
}
return event.defaultPrevented;
},
notifyPeerLost: function notifyPeerLost(sessionToken) {

View File

@ -41,7 +41,7 @@ DOMStorageDBBridge::DOMStorageDBBridge()
DOMStorageDBThread::DOMStorageDBThread()
: mThread(nullptr)
, mMonitor("DOMStorageThreadMonitor")
, mThreadObserver(new ThreadObserver())
, mStopIOThread(false)
, mWALModeEnabled(false)
, mDBReady(false)
@ -76,7 +76,7 @@ DOMStorageDBThread::Init()
// Need to keep the lock to avoid setting mThread later then
// the thread body executes.
MonitorAutoLock monitor(mMonitor);
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
mThread = PR_CreateThread(PR_USER_THREAD, &DOMStorageDBThread::ThreadFunc, this,
PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
@ -98,7 +98,7 @@ DOMStorageDBThread::Shutdown()
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS> timer;
{
MonitorAutoLock monitor(mMonitor);
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
// After we stop, no other operations can be accepted
mFlushImmediately = true;
@ -130,7 +130,7 @@ DOMStorageDBThread::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
if (mDBReady && mWALModeEnabled) {
bool pendingTasks;
{
MonitorAutoLock monitor(mMonitor);
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
pendingTasks = mPendingTasks.IsScopeUpdatePending(aCache->Scope()) ||
mPendingTasks.IsScopeClearPending(aCache->Scope());
}
@ -157,7 +157,7 @@ DOMStorageDBThread::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
void
DOMStorageDBThread::AsyncFlush()
{
MonitorAutoLock monitor(mMonitor);
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
mFlushImmediately = true;
monitor.Notify();
}
@ -165,7 +165,7 @@ DOMStorageDBThread::AsyncFlush()
bool
DOMStorageDBThread::ShouldPreloadScope(const nsACString& aScope)
{
MonitorAutoLock monitor(mMonitor);
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
return mScopesHavingData.Contains(aScope);
}
@ -185,14 +185,14 @@ GetScopesHavingDataEnum(nsCStringHashKey* aKey, void* aArg)
void
DOMStorageDBThread::GetScopesHavingData(InfallibleTArray<nsCString>* aScopes)
{
MonitorAutoLock monitor(mMonitor);
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
mScopesHavingData.EnumerateEntries(GetScopesHavingDataEnum, aScopes);
}
nsresult
DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation)
{
MonitorAutoLock monitor(mMonitor);
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
// Sentinel to don't forget to delete the operation when we exit early.
nsAutoPtr<DOMStorageDBThread::DBOperation> opScope(aOperation);
@ -204,7 +204,7 @@ DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation)
}
if (NS_FAILED(mStatus)) {
MonitorAutoUnlock unlock(mMonitor);
MonitorAutoUnlock unlock(mThreadObserver->GetMonitor());
aOperation->Finalize(mStatus);
return mStatus;
}
@ -225,7 +225,7 @@ DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation)
// actually been cleared from the database. Preloads are processed
// immediately before update and clear operations on the database that
// are flushed periodically in batches.
MonitorAutoUnlock unlock(mMonitor);
MonitorAutoUnlock unlock(mThreadObserver->GetMonitor());
aOperation->Finalize(NS_OK);
return NS_OK;
}
@ -292,7 +292,7 @@ DOMStorageDBThread::ThreadFunc()
{
nsresult rv = InitDatabase();
MonitorAutoLock lockMonitor(mMonitor);
MonitorAutoLock lockMonitor(mThreadObserver->GetMonitor());
if (NS_FAILED(rv)) {
mStatus = rv;
@ -300,13 +300,32 @@ DOMStorageDBThread::ThreadFunc()
return;
}
while (MOZ_LIKELY(!mStopIOThread || mPreloads.Length() || mPendingTasks.HasTasks())) {
// Create an nsIThread for the current PRThread, so we can observe runnables
// dispatched to it.
nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
nsCOMPtr<nsIThreadInternal> threadInternal = do_QueryInterface(thread);
MOZ_ASSERT(threadInternal); // Should always succeed.
threadInternal->SetObserver(mThreadObserver);
while (MOZ_LIKELY(!mStopIOThread || mPreloads.Length() ||
mPendingTasks.HasTasks() ||
mThreadObserver->HasPendingEvents())) {
// Process xpcom events first.
while (MOZ_UNLIKELY(mThreadObserver->HasPendingEvents())) {
mThreadObserver->ClearPendingEvents();
MonitorAutoUnlock unlock(mThreadObserver->GetMonitor());
bool processedEvent;
do {
rv = thread->ProcessNextEvent(false, &processedEvent);
} while (NS_SUCCEEDED(rv) && processedEvent);
}
if (MOZ_UNLIKELY(TimeUntilFlush() == 0)) {
// Flush time is up or flush has been forced, do it now.
UnscheduleFlush();
if (mPendingTasks.Prepare()) {
{
MonitorAutoUnlock unlockMonitor(mMonitor);
MonitorAutoUnlock unlockMonitor(mThreadObserver->GetMonitor());
rv = mPendingTasks.Execute(this);
}
@ -320,7 +339,7 @@ DOMStorageDBThread::ThreadFunc()
nsAutoPtr<DBOperation> op(mPreloads[0]);
mPreloads.RemoveElementAt(0);
{
MonitorAutoUnlock unlockMonitor(mMonitor);
MonitorAutoUnlock unlockMonitor(mThreadObserver->GetMonitor());
op->PerformAndFinalize(this);
}
@ -333,8 +352,41 @@ DOMStorageDBThread::ThreadFunc()
} // thread loop
mStatus = ShutdownDatabase();
if (threadInternal) {
threadInternal->SetObserver(nullptr);
}
}
NS_IMPL_ISUPPORTS(DOMStorageDBThread::ThreadObserver, nsIThreadObserver)
NS_IMETHODIMP
DOMStorageDBThread::ThreadObserver::OnDispatchedEvent(nsIThreadInternal *thread)
{
MonitorAutoLock lock(mMonitor);
mHasPendingEvents = true;
lock.Notify();
return NS_OK;
}
NS_IMETHODIMP
DOMStorageDBThread::ThreadObserver::OnProcessNextEvent(nsIThreadInternal *thread,
bool mayWait,
uint32_t recursionDepth)
{
return NS_OK;
}
NS_IMETHODIMP
DOMStorageDBThread::ThreadObserver::AfterProcessNextEvent(nsIThreadInternal *thread,
uint32_t recursionDepth,
bool eventWasProcessed)
{
return NS_OK;
}
extern void
ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult);
@ -508,7 +560,7 @@ DOMStorageDBThread::InitDatabase()
rv = stmt->GetUTF8String(0, foundScope);
NS_ENSURE_SUCCESS(rv, rv);
MonitorAutoLock monitor(mMonitor);
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
mScopesHavingData.PutEntry(foundScope);
}
@ -648,7 +700,7 @@ DOMStorageDBThread::ScheduleFlush()
mDirtyEpoch = PR_IntervalNow() | 1; // Must be non-zero to indicate we are scheduled
// Wake the monitor from indefinite sleep...
mMonitor.Notify();
(mThreadObserver->GetMonitor()).Notify();
}
void

View File

@ -15,6 +15,7 @@
#include "nsCOMPtr.h"
#include "nsClassHashtable.h"
#include "nsIFile.h"
#include "nsIThreadInternal.h"
class mozIStorageConnection;
@ -208,6 +209,34 @@ public:
uint32_t mFlushFailureCount;
};
class ThreadObserver MOZ_FINAL : public nsIThreadObserver
{
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITHREADOBSERVER
ThreadObserver()
: mHasPendingEvents(false)
, mMonitor("DOMStorageThreadMonitor")
{
}
bool HasPendingEvents() {
mMonitor.AssertCurrentThreadOwns();
return mHasPendingEvents;
}
void ClearPendingEvents() {
mMonitor.AssertCurrentThreadOwns();
mHasPendingEvents = false;
}
Monitor& GetMonitor() { return mMonitor; }
private:
virtual ~ThreadObserver() {}
bool mHasPendingEvents;
// The monitor we drive the thread with
Monitor mMonitor;
};
public:
DOMStorageDBThread();
virtual ~DOMStorageDBThread() {}
@ -250,10 +279,11 @@ private:
nsCOMPtr<nsIFile> mDatabaseFile;
PRThread* mThread;
// The monitor we drive the thread with
Monitor mMonitor;
// Used to observe runnables dispatched to our thread and to monitor it.
nsRefPtr<ThreadObserver> mThreadObserver;
// Flag to stop, protected by the monitor
// Flag to stop, protected by the monitor returned by
// mThreadObserver->GetMonitor().
bool mStopIOThread;
// Whether WAL is enabled

View File

@ -106,6 +106,8 @@ partial interface MouseEvent
EventTarget? relatedTargetArg,
float pressure,
unsigned short inputSourceArg);
[ChromeOnly]
readonly attribute boolean hitCluster; // True when touch occurs in a cluster of links
};

View File

@ -90,7 +90,13 @@ interface MozNFC : EventTarget {
attribute EventHandler onpeerready;
/**
* This event will be fired when a NFCPeer is detected.
* This event will be fired when a NFCPeer is detected. The application has to
* be running on the foreground (decided by System app) to receive this event.
*
* The default action of this event is to dispatch the event in System app
* again, and System app will run the default UX behavior (like vibration).
* So if the application would like to cancel the event, the application
* should call event.preventDefault() or return false in this event handler.
*/
attribute EventHandler onpeerfound;
@ -101,7 +107,16 @@ interface MozNFC : EventTarget {
attribute EventHandler onpeerlost;
/**
* Ths event will be fired when a NFCTag is detected.
* This event will be fired when a NFCTag is detected. The application has to
* be running on the foreground (decided by System app) to receive this event.
*
* The default action of this event is to dispatch the event in System app
* again, and System app will run the default UX behavior (like vibration) and
* launch MozActivity to handle the content of the tag. (For example, System
* app will launch Browser if the tag contains URL). So if the application
* would like to cancel the event, i.e. in the above example, the application
* would process the URL by itself without launching Browser, the application
* should call event.preventDefault() or return false in this event handler.
*/
attribute EventHandler ontagfound;

View File

@ -75,6 +75,7 @@ struct EventRadiusPrefs
bool mRegistered;
bool mTouchOnly;
bool mRepositionEventCoords;
bool mTouchClusterDetection;
};
static EventRadiusPrefs sMouseEventRadiusPrefs;
@ -121,6 +122,9 @@ GetPrefsFor(EventClassID aEventClassID)
nsPrintfCString repositionPref("ui.%s.radius.reposition", prefBranch);
Preferences::AddBoolVarCache(&prefs->mRepositionEventCoords, repositionPref.get(), false);
nsPrintfCString touchClusterPref("ui.zoomedview.enabled", prefBranch);
Preferences::AddBoolVarCache(&prefs->mTouchClusterDetection, touchClusterPref.get(), false);
}
return prefs;
@ -316,7 +320,8 @@ SubtractFromExposedRegion(nsRegion* aExposedRegion, const nsRegion& aRegion)
static nsIFrame*
GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
const nsRect& aTargetRect, const EventRadiusPrefs* aPrefs,
nsIFrame* aRestrictToDescendants, nsTArray<nsIFrame*>& aCandidates)
nsIFrame* aRestrictToDescendants, nsTArray<nsIFrame*>& aCandidates,
int32_t* aElementsInCluster)
{
nsIFrame* bestTarget = nullptr;
// Lower is better; distance is in appunits
@ -358,6 +363,8 @@ GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
continue;
}
(*aElementsInCluster)++;
// distance is in appunits
float distance = ComputeDistanceFromRegion(aPointRelativeToRootFrame, region);
nsIContent* content = f->GetContent();
@ -424,10 +431,18 @@ FindFrameTargetedByInputEvent(WidgetGUIEvent* aEvent,
return target;
}
int32_t elementsInCluster = 0;
nsIFrame* closestClickable =
GetClosest(aRootFrame, aPointRelativeToRootFrame, targetRect, prefs,
restrictToDescendants, candidates);
restrictToDescendants, candidates, &elementsInCluster);
if (closestClickable) {
if (prefs->mTouchClusterDetection && elementsInCluster > 1) {
if (aEvent->mClass == eMouseEventClass) {
WidgetMouseEventBase* mouseEventBase = aEvent->AsMouseEventBase();
mouseEventBase->hitCluster = true;
}
}
target = closestClickable;
}
PET_LOG("Final target is %p\n", target);

View File

@ -395,6 +395,8 @@ pref("font.size.inflation.minTwips", 120);
// When true, zooming will be enabled on all sites, even ones that declare user-scalable=no.
pref("browser.ui.zoom.force-user-scalable", false);
pref("ui.zoomedview.enabled", false);
pref("ui.touch.radius.enabled", false);
pref("ui.touch.radius.leftmm", 3);
pref("ui.touch.radius.topmm", 5);

View File

@ -955,7 +955,7 @@ public class BrowserApp extends GeckoApp
if (enabled) {
if (mLayerView != null) {
mLayerView.setOnMetricsChangedListener(this);
mLayerView.setOnMetricsChangedDynamicToolbarViewportListener(this);
}
setToolbarMargin(0);
mHomePagerContainer.setPadding(0, mBrowserChrome.getHeight(), 0, 0);
@ -963,7 +963,7 @@ public class BrowserApp extends GeckoApp
// Immediately show the toolbar when disabling the dynamic
// toolbar.
if (mLayerView != null) {
mLayerView.setOnMetricsChangedListener(null);
mLayerView.setOnMetricsChangedDynamicToolbarViewportListener(null);
}
mHomePagerContainer.setPadding(0, 0, 0, 0);
if (mBrowserChrome != null) {

View File

@ -130,6 +130,8 @@ public abstract class GeckoApp
private static final String LOGTAG = "GeckoApp";
private static final int ONE_DAY_MS = 1000*60*60*24;
private static final boolean ZOOMED_VIEW_ENABLED = AppConstants.NIGHTLY_BUILD;
private static enum StartupAction {
NORMAL, /* normal application start */
URL, /* launched with a passed URL */
@ -173,6 +175,7 @@ public abstract class GeckoApp
private ContactService mContactService;
private PromptService mPromptService;
private TextSelection mTextSelection;
private ZoomedView mZoomedView;
protected DoorHangerPopup mDoorHangerPopup;
protected FormAssistPopup mFormAssistPopup;
@ -1578,6 +1581,11 @@ public abstract class GeckoApp
(TextSelectionHandle) findViewById(R.id.caret_handle),
(TextSelectionHandle) findViewById(R.id.focus_handle));
if (ZOOMED_VIEW_ENABLED) {
ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub);
mZoomedView = (ZoomedView) stub.inflate();
}
PrefsHelper.getPref("app.update.autodownload", new PrefsHelper.PrefHandlerBase() {
@Override public void prefValue(String pref, String value) {
UpdateServiceHelper.registerForUpdates(GeckoApp.this, value);
@ -2048,6 +2056,9 @@ public abstract class GeckoApp
mPromptService.destroy();
if (mTextSelection != null)
mTextSelection.destroy();
if (mZoomedView != null) {
mZoomedView.destroy();
}
NotificationHelper.destroy();
IntentHelper.destroy();
GeckoNetworkManager.destroy();

View File

@ -105,7 +105,8 @@ public class GeckoEvent {
TELEMETRY_UI_EVENT(44),
GAMEPAD_ADDREMOVE(45),
GAMEPAD_DATA(46),
LONG_PRESS(47);
LONG_PRESS(47),
ZOOMEDVIEW(48);
public final int value;
@ -749,6 +750,17 @@ public class GeckoEvent {
return event;
}
public static GeckoEvent createZoomedViewEvent(int tabId, int x, int y, int bufw, int bufh, float scaleFactor, ByteBuffer buffer) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.ZOOMEDVIEW);
event.mPoints = new Point[2];
event.mPoints[0] = new Point(x, y);
event.mPoints[1] = new Point(bufw, bufh);
event.mX = (double) scaleFactor;
event.mMetaState = tabId;
event.mBuffer = buffer;
return event;
}
public static GeckoEvent createScreenOrientationEvent(short aScreenOrientation) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.SCREENORIENTATION_CHANGED);
event.mScreenOrientation = aScreenOrientation;

View File

@ -0,0 +1,488 @@
package org.mozilla.gecko;
import java.text.DecimalFormat;
import java.nio.ByteBuffer;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.mozglue.DirectBufferAllocator;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChangedListener,
LayerView.OnZoomedViewListener, GeckoEventListener {
private static final String LOGTAG = "Gecko" + ZoomedView.class.getSimpleName();
private static final int ZOOM_FACTOR = 2;
private static final int W_CAPTURED_VIEW_IN_PERCENT = 80;
private static final int H_CAPTURED_VIEW_IN_PERCENT = 50;
private static final int MINIMUM_DELAY_BETWEEN_TWO_RENDER_CALLS_NS = 1000000;
private static final int DELAY_BEFORE_NEXT_RENDER_REQUEST_MS = 2000;
private ImageView zoomedImageView;
private LayerView layerView;
private MotionEvent actionDownEvent;
private int viewWidth;
private int viewHeight;
private int xLastPosition;
private int yLastPosition;
private boolean shouldSetVisibleOnUpdate;
private PointF returnValue;
private boolean stopUpdateView;
private int lastOrientation = 0;
private ByteBuffer buffer;
private Runnable requestRenderRunnable;
private long startTimeReRender = 0;
private long lastStartTimeReRender = 0;
private class ZoomedViewTouchListener implements View.OnTouchListener {
private float originRawX;
private float originRawY;
private int touchState;
@Override
public boolean onTouch(View view, MotionEvent event) {
if (layerView == null) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (moveZoomedView(event)) {
touchState = MotionEvent.ACTION_MOVE;
}
break;
case MotionEvent.ACTION_UP:
if (touchState == MotionEvent.ACTION_MOVE) {
touchState = -1;
} else {
layerView.dispatchTouchEvent(actionDownEvent);
actionDownEvent.recycle();
PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY());
MotionEvent e = MotionEvent.obtain(event.getDownTime(), event.getEventTime(),
MotionEvent.ACTION_UP, convertedPosition.x, convertedPosition.y,
event.getMetaState());
layerView.dispatchTouchEvent(e);
e.recycle();
}
break;
case MotionEvent.ACTION_DOWN:
touchState = -1;
originRawX = event.getRawX();
originRawY = event.getRawY();
PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY());
actionDownEvent = MotionEvent.obtain(event.getDownTime(), event.getEventTime(),
MotionEvent.ACTION_DOWN, convertedPosition.x, convertedPosition.y,
event.getMetaState());
break;
}
return true;
}
private boolean moveZoomedView(MotionEvent event) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ZoomedView.this.getLayoutParams();
if ((touchState != MotionEvent.ACTION_MOVE) && (Math.abs((int) (event.getRawX() - originRawX)) < 1)
&& (Math.abs((int) (event.getRawY() - originRawY)) < 1)) {
// When the user just touches the screen ACTION_MOVE can be detected for a very small delta on position.
// In this case, the move is ignored if the delta is lower than 1 unit.
return false;
}
float newLeftMargin = params.leftMargin + event.getRawX() - originRawX;
float newTopMargin = params.topMargin + event.getRawY() - originRawY;
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
ZoomedView.this.moveZoomedView(metrics, newLeftMargin, newTopMargin);
originRawX = event.getRawX();
originRawY = event.getRawY();
return true;
}
}
public ZoomedView(Context context) {
this(context, null, 0);
}
public ZoomedView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZoomedView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
returnValue = new PointF();
requestRenderRunnable = new Runnable() {
@Override
public void run() {
requestZoomedViewRender();
}
};
EventDispatcher.getInstance().registerGeckoThreadListener(this, "Gesture:nothingDoneOnLongPress",
"Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange");
}
void destroy() {
ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Gesture:nothingDoneOnLongPress",
"Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange");
}
// This method (onFinishInflate) is called only when the zoomed view class is used inside
// an xml structure <org.mozilla.gecko.ZoomedView ...
// It won't be called if the class is used from java code like "new ZoomedView(context);"
@Override
protected void onFinishInflate() {
super.onFinishInflate();
ImageView closeButton = (ImageView) findViewById(R.id.dialog_close);
closeButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
stopZoomDisplay();
}
});
zoomedImageView = (ImageView) findViewById(R.id.zoomed_image_view);
zoomedImageView.setOnTouchListener(new ZoomedViewTouchListener());
}
/*
* Convert a click from ZoomedView. Return the position of the click in the
* LayerView
*/
private PointF getUnzoomedPositionFromPointInZoomedView(float x, float y) {
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
PointF offset = metrics.getMarginOffset();
final float parentWidth = metrics.getWidth();
final float parentHeight = metrics.getHeight();
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams();
returnValue.x = (int) ((x / ZOOM_FACTOR) + // Conversion of the x offset inside the zoomed view (using the scale factor)
offset.x + // The offset of the layerView
/* Conversion of the left side position of the zoomed view
* Minimum value for the left side of the zoomed view is 0
* and we return 0 after conversion
* Maximum value for the left side of the zoomed view is (parentWidth - offset.x - viewWidth)
* and we return (parentWidth - offset.x - (viewWidth / ZOOM_FACTOR)) after conversion.
*/
(((float) params.leftMargin) - offset.x) *
((parentWidth - offset.x - (viewWidth / ZOOM_FACTOR)) /
(parentWidth - offset.x - viewWidth)));
// Same comments here vertically
returnValue.y = (int) ((y / ZOOM_FACTOR) +
offset.y +
(((float) params.topMargin) - offset.y) *
((parentHeight - offset.y - (viewHeight / ZOOM_FACTOR)) /
(parentHeight - offset.y - viewHeight)));
return returnValue;
}
/*
* A touch point (x,y) occurs in LayerView, this point should be displayed
* in the center of the zoomed view. The returned point is the position of
* the Top-Left zoomed view point on the screen device
*/
private PointF getZoomedViewTopLeftPositionFromTouchPosition(float x, float y) {
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
PointF offset = metrics.getMarginOffset();
final float parentWidth = metrics.getWidth();
final float parentHeight = metrics.getHeight();
returnValue.x = (int) ((((x - (viewWidth / (2 * ZOOM_FACTOR)))) / // Translation to get the left side position of the zoomed view
// centered on x (the value 2 to get the middle).
/* Conversion of the left side position of the zoomed view.
* See the comment in getUnzoomedPositionFromPointInZoomedView.
* The proportional factor is the same. It is used in a division
* and not in a multiplication to convert the position from
* the LayerView to the ZoomedView.
*/
((parentWidth - offset.x - (viewWidth / ZOOM_FACTOR)) /
(parentWidth - offset.x - viewWidth)))
+ offset.x); // The offset of the layerView
// Same comments here vertically
returnValue.y = (int) ((((y - (viewHeight / (2 * ZOOM_FACTOR)))) /
((parentHeight - offset.y - (viewHeight / ZOOM_FACTOR)) /
(parentHeight - offset.y - viewHeight)))
+ offset.y);
return returnValue;
}
private void moveZoomedView(ImmutableViewportMetrics metrics, float newLeftMargin, float newTopMargin) {
if (layerView == null) {
return;
}
final float parentWidth = metrics.getWidth();
final float parentHeight = metrics.getHeight();
RelativeLayout.LayoutParams newLayoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
newLayoutParams.leftMargin = (int) newLeftMargin;
newLayoutParams.topMargin = (int) newTopMargin;
int topMarginMin;
int leftMarginMin;
PointF offset = metrics.getMarginOffset();
topMarginMin = (int) offset.y;
leftMarginMin = (int) offset.x;
if (newTopMargin < topMarginMin) {
newLayoutParams.topMargin = topMarginMin;
} else if (newTopMargin + viewHeight >= parentHeight) {
newLayoutParams.topMargin = (int) (parentHeight - viewHeight);
}
if (newLeftMargin < leftMarginMin) {
newLayoutParams.leftMargin = leftMarginMin;
} else if (newLeftMargin + viewWidth > parentWidth) {
newLayoutParams.leftMargin = (int) (parentWidth - viewWidth);
}
setLayoutParams(newLayoutParams);
PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(0, 0);
xLastPosition = Math.round(convertedPosition.x);
yLastPosition = Math.round(convertedPosition.y);
requestZoomedViewRender();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// In case of orientation change, the zoomed view update is stopped until the orientation change
// is completed. At this time, the function onMetricsChanged is called and the
// zoomed view update is restarted again.
if (lastOrientation != newConfig.orientation) {
shouldBlockUpdate(true);
lastOrientation = newConfig.orientation;
}
}
public void refreshZoomedViewSize(ImmutableViewportMetrics viewport) {
if (layerView == null) {
return;
}
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams();
setCapturedSize(viewport);
moveZoomedView(viewport, params.leftMargin, params.topMargin);
}
public void setCapturedSize(ImmutableViewportMetrics metrics) {
if (layerView == null) {
return;
}
float parentMinSize = Math.min(metrics.getWidth(), metrics.getHeight());
viewWidth = (int) (parentMinSize * W_CAPTURED_VIEW_IN_PERCENT / (ZOOM_FACTOR * 100.0)) * ZOOM_FACTOR;
viewHeight = (int) (parentMinSize * H_CAPTURED_VIEW_IN_PERCENT / (ZOOM_FACTOR * 100.0)) * ZOOM_FACTOR;
}
public void shouldBlockUpdate(boolean shouldBlockUpdate) {
stopUpdateView = shouldBlockUpdate;
}
public Bitmap.Config getBitmapConfig() {
return (GeckoAppShell.getScreenDepth() == 24) ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
}
public void startZoomDisplay(LayerView aLayerView, final int leftFromGecko, final int topFromGecko) {
if (layerView == null) {
layerView = aLayerView;
layerView.addOnZoomedViewListener(this);
layerView.setOnMetricsChangedZoomedViewportListener(this);
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
setCapturedSize(metrics);
}
startTimeReRender = 0;
shouldSetVisibleOnUpdate = true;
moveUsingGeckoPosition(leftFromGecko, topFromGecko);
}
public void stopZoomDisplay() {
shouldSetVisibleOnUpdate = false;
this.setVisibility(View.GONE);
ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
if (layerView != null) {
layerView.setOnMetricsChangedZoomedViewportListener(null);
layerView.removeOnZoomedViewListener(this);
layerView = null;
}
}
@Override
public void handleMessage(final String event, final JSONObject message) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
try {
if (event.equals("Gesture:nothingDoneOnLongPress") || event.equals("Gesture:clusteredLinksClicked")) {
final JSONObject clickPosition = message.getJSONObject("clickPosition");
int left = clickPosition.getInt("x");
int top = clickPosition.getInt("y");
// Start to display inside the zoomedView
LayerView geckoAppLayerView = GeckoAppShell.getLayerView();
if (geckoAppLayerView != null) {
startZoomDisplay(geckoAppLayerView, left, top);
}
} else if (event.equals("Window:Resize")) {
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
refreshZoomedViewSize(metrics);
} else if (event.equals("Content:LocationChange")) {
stopZoomDisplay();
}
} catch (JSONException e) {
Log.e(LOGTAG, "JSON exception", e);
}
}
});
}
private void moveUsingGeckoPosition(int leftFromGecko, int topFromGecko) {
if (layerView == null) {
return;
}
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
PointF convertedPosition = getZoomedViewTopLeftPositionFromTouchPosition((leftFromGecko * metrics.zoomFactor),
(topFromGecko * metrics.zoomFactor));
moveZoomedView(metrics, convertedPosition.x, convertedPosition.y);
}
@Override
public void onMetricsChanged(final ImmutableViewportMetrics viewport) {
// It can be called from a Gecko thread (forceViewportMetrics in GeckoLayerClient).
// Post to UI Thread to avoid Exception:
// "Only the original thread that created a view hierarchy can touch its views."
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (layerView == null) {
return;
}
shouldBlockUpdate(false);
refreshZoomedViewSize(viewport);
}
});
}
@Override
public void onPanZoomStopped() {
}
@Override
public void updateView(ByteBuffer data) {
final Bitmap sb3 = Bitmap.createBitmap(viewWidth, viewHeight, getBitmapConfig());
if (sb3 != null) {
data.rewind();
try {
sb3.copyPixelsFromBuffer(data);
} catch (Exception iae) {
Log.w(LOGTAG, iae.toString());
}
BitmapDrawable ob3 = new BitmapDrawable(getResources(), sb3);
if (zoomedImageView != null) {
zoomedImageView.setImageDrawable(ob3);
}
}
if (shouldSetVisibleOnUpdate) {
this.setVisibility(View.VISIBLE);
shouldSetVisibleOnUpdate = false;
}
lastStartTimeReRender = startTimeReRender;
startTimeReRender = 0;
}
private void updateBufferSize() {
int pixelSize = (GeckoAppShell.getScreenDepth() == 24) ? 4 : 2;
int capacity = viewWidth * viewHeight * pixelSize;
if (buffer == null || buffer.capacity() != capacity) {
buffer = DirectBufferAllocator.free(buffer);
buffer = DirectBufferAllocator.allocate(capacity);
}
}
private boolean isRendering() {
return (startTimeReRender != 0);
}
private boolean renderFrequencyTooHigh() {
return ((System.nanoTime() - lastStartTimeReRender) < MINIMUM_DELAY_BETWEEN_TWO_RENDER_CALLS_NS);
}
@Override
public void requestZoomedViewRender() {
if (stopUpdateView) {
return;
}
// remove pending runnable
ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
// "requestZoomedViewRender" can be called very often by Gecko (endDrawing in LayerRender) without
// any thing changed in the zoomed area (useless calls from the "zoomed area" point of view).
// "requestZoomedViewRender" can take time to re-render the zoomed view, it depends of the complexity
// of the html on this area.
// To avoid to slow down the application, the 2 following cases are tested:
// 1- Last render is still running, plan another render later.
if (isRendering()) {
// post a new runnable DELAY_BEFORE_NEXT_RENDER_REQUEST_MS later
// We need to post with a delay to be sure that the last call to requestZoomedViewRender will be done.
// For a static html page WITHOUT any animation/video, there is a last call to endDrawing and we need to make
// the zoomed render on this last call.
ThreadUtils.postDelayedToUiThread(requestRenderRunnable, DELAY_BEFORE_NEXT_RENDER_REQUEST_MS);
return;
}
// 2- Current render occurs too early, plan another render later.
if (renderFrequencyTooHigh()) {
// post a new runnable DELAY_BEFORE_NEXT_RENDER_REQUEST_MS later
// We need to post with a delay to be sure that the last call to requestZoomedViewRender will be done.
// For a page WITH animation/video, the animation/video can be stopped, and we need to make
// the zoomed render on this last call.
ThreadUtils.postDelayedToUiThread(requestRenderRunnable, DELAY_BEFORE_NEXT_RENDER_REQUEST_MS);
return;
}
startTimeReRender = System.nanoTime();
// Allocate the buffer if it's the first call.
// Change the buffer size if it's not the right size.
updateBufferSize();
int tabId = Tabs.getInstance().getSelectedTab().getId();
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
PointF origin = metrics.getOrigin();
PointF offset = metrics.getMarginOffset();
final int xPos = (int) (origin.x - offset.x) + xLastPosition;
final int yPos = (int) (origin.y - offset.y) + yLastPosition;
GeckoEvent e = GeckoEvent.createZoomedViewEvent(tabId, xPos, yPos, viewWidth,
viewHeight, (float) (2.0 * metrics.zoomFactor), buffer);
GeckoAppShell.sendEventToGecko(e);
}
}

View File

@ -86,7 +86,8 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
* that because mViewportMetrics might get reassigned in between reading the different
* fields. */
private volatile ImmutableViewportMetrics mViewportMetrics;
private LayerView.OnMetricsChangedListener mViewportChangeListener;
private LayerView.OnMetricsChangedListener mDynamicToolbarViewportChangeListener;
private LayerView.OnMetricsChangedListener mZoomedViewViewportChangeListener;
private ZoomConstraints mZoomConstraints;
@ -853,8 +854,11 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
* You must hold the monitor while calling this.
*/
private void viewportMetricsChanged(boolean notifyGecko) {
if (mViewportChangeListener != null) {
mViewportChangeListener.onMetricsChanged(mViewportMetrics);
if (mDynamicToolbarViewportChangeListener != null) {
mDynamicToolbarViewportChangeListener.onMetricsChanged(mViewportMetrics);
}
if (mZoomedViewViewportChangeListener != null) {
mZoomedViewViewportChangeListener.onMetricsChanged(mViewportMetrics);
}
mView.requestRender();
@ -910,8 +914,11 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
/** Implementation of PanZoomTarget */
@Override
public void panZoomStopped() {
if (mViewportChangeListener != null) {
mViewportChangeListener.onPanZoomStopped();
if (mDynamicToolbarViewportChangeListener != null) {
mDynamicToolbarViewportChangeListener.onPanZoomStopped();
}
if (mZoomedViewViewportChangeListener != null) {
mZoomedViewViewportChangeListener.onPanZoomStopped();
}
}
@ -982,8 +989,12 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
return layerPoint;
}
void setOnMetricsChangedListener(LayerView.OnMetricsChangedListener listener) {
mViewportChangeListener = listener;
void setOnMetricsChangedDynamicToolbarViewportListener(LayerView.OnMetricsChangedListener listener) {
mDynamicToolbarViewportChangeListener = listener;
}
void setOnMetricsChangedZoomedViewportListener(LayerView.OnMetricsChangedListener listener) {
mZoomedViewViewportChangeListener = listener;
}
public void addDrawListener(DrawListener listener) {

View File

@ -26,13 +26,17 @@ import android.graphics.RectF;
import android.opengl.GLES20;
import android.os.SystemClock;
import android.util.Log;
import org.mozilla.gecko.mozglue.JNITarget;
import org.mozilla.gecko.util.ThreadUtils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.ArrayList;
import java.util.List;
import javax.microedition.khronos.egl.EGLConfig;
@ -55,6 +59,8 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
private static final long NANOS_PER_MS = 1000000;
private static final int NANOS_PER_SECOND = 1000000000;
private static final int MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER = 5;
private final LayerView mView;
private final ScrollbarLayer mHorizScrollLayer;
private final ScrollbarLayer mVertScrollLayer;
@ -90,6 +96,10 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
private int mSampleHandle;
private int mTMatrixHandle;
private List<LayerView.OnZoomedViewListener> mZoomedViewListeners;
private float mViewLeft = 0.0f;
private float mViewTop = 0.0f;
// column-major matrix applied to each vertex to shift the viewport from
// one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by
// a factor of 2 to fill up the screen
@ -158,6 +168,7 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
mCoordBuffer = mCoordByteBuffer.asFloatBuffer();
Tabs.registerOnTabsChangedListener(this);
mZoomedViewListeners = new ArrayList<LayerView.OnZoomedViewListener>();
}
private Bitmap expandCanvasToPowerOfTwo(Bitmap image, IntSize size) {
@ -185,6 +196,7 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
mHorizScrollLayer.destroy();
mVertScrollLayer.destroy();
Tabs.unregisterOnTabsChangedListener(this);
mZoomedViewListeners.clear();
}
void onSurfaceCreated(EGLConfig config) {
@ -586,6 +598,42 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
}
public void maybeRequestZoomedViewRender(RenderContext context){
// Concurrently update of mZoomedViewListeners should not be an issue here
// because the following line is just a short-circuit
if (mZoomedViewListeners.size() == 0) {
return;
}
// When scrolling fast, do not request zoomed view render to avoid to slow down
// the scroll in the main view.
// Speed is estimated using the offset changes between 2 display frame calls
final float viewLeft = context.viewport.left - context.offset.x;
final float viewTop = context.viewport.top - context.offset.y;
boolean shouldWaitToRender = false;
if (Math.abs(mViewLeft - viewLeft) > MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER ||
Math.abs(mViewTop - viewTop) > MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER) {
shouldWaitToRender = true;
}
mViewLeft = viewLeft;
mViewTop = viewTop;
if (shouldWaitToRender) {
return;
}
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
for (LayerView.OnZoomedViewListener listener : mZoomedViewListeners) {
listener.requestZoomedViewRender();
}
}
});
}
/** This function is invoked via JNI; be careful when modifying signature. */
@JNITarget
public void endDrawing() {
@ -595,6 +643,8 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
PanningPerfAPI.recordFrameTime();
maybeRequestZoomedViewRender(mPageContext);
/* Used by robocop for testing purposes */
IntBuffer pixelBuffer = mPixelBuffer;
if (mUpdated && pixelBuffer != null) {
@ -642,4 +692,25 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
}
}
}
public void updateZoomedView(final ByteBuffer data) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
for (LayerView.OnZoomedViewListener listener : mZoomedViewListeners) {
listener.updateView(data);
}
}
});
}
public void addOnZoomedViewListener(LayerView.OnZoomedViewListener listener) {
ThreadUtils.assertOnUiThread();
mZoomedViewListeners.add(listener);
}
public void removeOnZoomedViewListener(LayerView.OnZoomedViewListener listener) {
ThreadUtils.assertOnUiThread();
mZoomedViewListeners.remove(listener);
}
}

View File

@ -5,7 +5,9 @@
package org.mozilla.gecko.gfx;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import org.mozilla.gecko.AndroidGamepadManager;
import org.mozilla.gecko.AppConstants.Versions;
@ -530,6 +532,19 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
}
}
@WrapElementForJNI(allowMultithread = true, stubName = "updateZoomedView")
public static void updateZoomedView(ByteBuffer data) {
data.position(0);
LayerView layerView = GeckoAppShell.getLayerView();
if (layerView != null) {
LayerRenderer layerRenderer = layerView.getRenderer();
if (layerRenderer != null){
layerRenderer.updateZoomedView(data);
}
}
return;
}
public interface Listener {
void renderRequested();
void sizeChanged(int width, int height);
@ -662,7 +677,27 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
public void onPanZoomStopped();
}
public void setOnMetricsChangedListener(OnMetricsChangedListener listener) {
mLayerClient.setOnMetricsChangedListener(listener);
public void setOnMetricsChangedDynamicToolbarViewportListener(OnMetricsChangedListener listener) {
mLayerClient.setOnMetricsChangedDynamicToolbarViewportListener(listener);
}
public void setOnMetricsChangedZoomedViewportListener(OnMetricsChangedListener listener) {
mLayerClient.setOnMetricsChangedZoomedViewportListener(listener);
}
// Public hooks for zoomed view
public interface OnZoomedViewListener {
public void requestZoomedViewRender();
public void updateView(ByteBuffer data);
}
public void addOnZoomedViewListener(OnZoomedViewListener listener) {
mRenderer.addOnZoomedViewListener(listener);
}
public void removeOnZoomedViewListener(OnZoomedViewListener listener) {
mRenderer.removeOnZoomedViewListener(listener);
}
}

View File

@ -500,6 +500,7 @@ gbjar.sources += [
'widget/ThumbnailView.java',
'widget/TwoWayView.java',
'ZoomConstraints.java',
'ZoomedView.java',
]
# The following sources are checked in to version control but
# generated by a script (widget/generate_themed_views.py). If you're

View File

@ -26,6 +26,12 @@
<include layout="@layout/text_selection_handles"/>
<ViewStub android:id="@+id/zoomed_view_stub"
android:inflatedId="@+id/zoomed_view"
android:layout="@layout/zoomed_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<FrameLayout android:id="@+id/camera_layout"
android:layout_height="wrap_content"
android:layout_width="wrap_content"

View File

@ -0,0 +1,33 @@
<?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/.
-->
<org.mozilla.gecko.ZoomedView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto"
android:id="@+id/zoomed_view_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@android:color/white"
android:visibility="gone" >
<ImageView
android:id="@+id/zoomed_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000000"
android:padding="1dip" />
<ImageView
android:id="@+id/dialog_close"
android:background="@drawable/close"
android:layout_height="20dp"
android:layout_width="20dp"
android:layout_gravity ="top|right" />
</org.mozilla.gecko.ZoomedView>

View File

@ -96,6 +96,14 @@ public final class ThreadUtils {
sUiHandler.post(runnable);
}
public static void postDelayedToUiThread(Runnable runnable, long timeout) {
sUiHandler.postDelayed(runnable, timeout);
}
public static void removeCallbacksFromUiThread(Runnable runnable) {
sUiHandler.removeCallbacks(runnable);
}
public static Thread getBackgroundThread() {
return sBackgroundThread;
}

View File

@ -57,8 +57,8 @@ public class FadedMultiColorTextView extends FadedTextView {
final float center = getHeight() / 2;
// Shrink height of gradient to prevent it overlaying parent view border.
final float top = center - getTextSize() + 1;
final float bottom = center + getTextSize() - 1;
final float top = center - getTextSize() + 2;
final float bottom = center + getTextSize() - 2;
canvas.drawRect(left, top, right, bottom, fadePaint);
}

View File

@ -4981,6 +4981,11 @@ var BrowserEventHandler = {
return;
}
this._inCluster = aEvent.hitCluster;
if (this._inCluster) {
return; // No highlight for a cluster of links
}
let uri = this._getLinkURI(target);
if (uri) {
try {
@ -5095,16 +5100,19 @@ var BrowserEventHandler = {
Cu.reportError(e);
}
// The _highlightElement was chosen after fluffing the touch events
// that led to this SingleTap, so by fluffing the mouse events, they
// should find the same target since we fluff them again below.
let data = JSON.parse(aData);
let {x, y} = data;
this._sendMouseEvent("mousemove", x, y);
this._sendMouseEvent("mousedown", x, y);
this._sendMouseEvent("mouseup", x, y);
if (this._inCluster) {
this._clusterClicked(x, y);
} else {
// The _highlightElement was chosen after fluffing the touch events
// that led to this SingleTap, so by fluffing the mouse events, they
// should find the same target since we fluff them again below.
this._sendMouseEvent("mousemove", x, y);
this._sendMouseEvent("mousedown", x, y);
this._sendMouseEvent("mouseup", x, y);
}
// scrollToFocusedInput does its own checks to find out if an element should be zoomed into
BrowserApp.scrollToFocusedInput(BrowserApp.selectedBrowser);
@ -5127,6 +5135,16 @@ var BrowserEventHandler = {
}
},
_clusterClicked: function(aX, aY) {
Messaging.sendRequest({
type: "Gesture:clusteredLinksClicked",
clickPosition: {
x: aX,
y: aY
}
});
},
onDoubleTap: function(aData) {
let metadata = BrowserApp.selectedTab.metadata;
if (!metadata.allowDoubleTapZoom) {

View File

@ -236,11 +236,6 @@ HelperAppLauncherDialog.prototype = {
Services.prefs.clearUserPref(this._getPrefName(mime));
},
promptForSaveToFile: function () {
throw new Components.Exception("Async version must be used",
Cr.NS_ERROR_NOT_AVAILABLE);
},
promptForSaveToFileAsync: function (aLauncher, aContext, aDefaultFile,
aSuggestedFileExt, aForcePrompt) {
Task.spawn(function* () {

View File

@ -509,14 +509,6 @@ body {
background-image: url('chrome://browser/skin/images/reader-dropdown-arrow-hdpi.png');
}
.step-control > .plus-button {
background-image: url('chrome://browser/skin/images/reader-plus-icon-hdpi.png');
}
.step-control > .minus-button {
background-image: url('chrome://browser/skin/images/reader-minus-icon-hdpi.png');
}
.toggle-button.on {
background-image: url('chrome://browser/skin/images/reader-toggle-on-icon-hdpi.png');
}
@ -539,14 +531,6 @@ body {
background-image: url('chrome://browser/skin/images/reader-dropdown-arrow-xhdpi.png');
}
.step-control > .plus-button {
background-image: url('chrome://browser/skin/images/reader-plus-icon-xhdpi.png');
}
.step-control > .minus-button {
background-image: url('chrome://browser/skin/images/reader-minus-icon-xhdpi.png');
}
.toggle-button.on {
background-image: url('chrome://browser/skin/images/reader-toggle-on-icon-xhdpi.png');
}

View File

@ -74,7 +74,7 @@ body {
#new-pref-toggle-button {
background-position: center center;
background-image: url("chrome://browser/skin/images/reader-plus-icon-xhdpi.png");
background-image: url("chrome://browser/skin/images/config-plus.png");
background-size: 48px 48px;
height: 48px;
width: 48px;

View File

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 B

View File

@ -85,12 +85,7 @@ chrome.jar:
skin/images/about-btn-darkgrey.png (images/about-btn-darkgrey.png)
skin/images/logo-hdpi.png (images/logo-hdpi.png)
skin/images/wordmark-hdpi.png (images/wordmark-hdpi.png)
skin/images/reader-plus-icon-mdpi.png (images/reader-plus-icon-mdpi.png)
skin/images/reader-plus-icon-hdpi.png (images/reader-plus-icon-hdpi.png)
skin/images/reader-plus-icon-xhdpi.png (images/reader-plus-icon-xhdpi.png)
skin/images/reader-minus-icon-mdpi.png (images/reader-minus-icon-mdpi.png)
skin/images/reader-minus-icon-hdpi.png (images/reader-minus-icon-hdpi.png)
skin/images/reader-minus-icon-xhdpi.png (images/reader-minus-icon-xhdpi.png)
skin/images/config-plus.png (images/config-plus.png)
skin/images/reader-dropdown-arrow-mdpi.png (images/reader-dropdown-arrow-mdpi.png)
skin/images/reader-dropdown-arrow-hdpi.png (images/reader-dropdown-arrow-hdpi.png)
skin/images/reader-dropdown-arrow-xhdpi.png (images/reader-dropdown-arrow-xhdpi.png)

View File

@ -37,24 +37,32 @@ Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// loglevel should be one of "Fatal", "Error", "Warn", "Info", "Config",
// log.appender.dump should be one of "Fatal", "Error", "Warn", "Info", "Config",
// "Debug", "Trace" or "All". If none is specified, "Error" will be used by
// default.
const PREF_LOG_LEVEL = "services.hawk.loglevel";
// Note however that Sync will also add this log to *its* DumpAppender, so
// in a Sync context it shouldn't be necessary to adjust this - however, that
// also means error logs are likely to be dump'd twice but that's OK.
const PREF_LOG_LEVEL = "services.common.hawk.log.appender.dump";
// A pref that can be set so "sensitive" information (eg, personally
// identifiable info, credentials, etc) will be logged.
const PREF_LOG_SENSITIVE_DETAILS = "services.hawk.log.sensitive";
const PREF_LOG_SENSITIVE_DETAILS = "services.common.hawk.log.sensitive";
XPCOMUtils.defineLazyGetter(this, "log", function() {
let log = Log.repository.getLogger("Hawk");
log.addAppender(new Log.DumpAppender());
log.level = Log.Level.Error;
// We set the log itself to "debug" and set the level from the preference to
// the appender. This allows other things to send the logs to different
// appenders, while still allowing the pref to control what is seen via dump()
log.level = Log.Level.Debug;
let appender = new Log.DumpAppender();
log.addAppender(appender);
appender.level = Log.Level.Error;
try {
let level =
Services.prefs.getPrefType(PREF_LOG_LEVEL) == Ci.nsIPrefBranch.PREF_STRING
&& Services.prefs.getCharPref(PREF_LOG_LEVEL);
log.level = Log.Level[level] || Log.Level.Error;
appender.level = Log.Level[level] || Log.Level.Error;
} catch (e) {
log.error(e);
}
@ -99,12 +107,17 @@ this.HawkClient.prototype = {
* @param restResponse
* A RESTResponse object from a RESTRequest
*
* @param errorString
* A string describing the error
* @param error
* A string or object describing the error
*/
_constructError: function(restResponse, errorString) {
_constructError: function(restResponse, error) {
let errorObj = {
error: errorString,
error: error,
// This object is likely to be JSON.stringify'd, but neither Error()
// objects nor Components.Exception objects do the right thing there,
// so we add a new element which is simply the .toString() version of
// the error object, so it does appear in JSON'd values.
errorString: error.toString(),
message: restResponse.statusText,
code: restResponse.status,
errno: restResponse.status
@ -190,6 +203,12 @@ this.HawkClient.prototype = {
let self = this;
function _onComplete(error) {
// |error| can be either a normal caught error or an explicitly created
// Components.Exception() error. Log it now as it might not end up
// correctly in the logs by the time it's passed through _constructError.
if (error) {
log.warn("hawk request error", error);
}
let restResponse = this.response;
let status = restResponse.status;
@ -262,10 +281,15 @@ this.HawkClient.prototype = {
};
let request = this.newHAWKAuthenticatedRESTRequest(uri, credentials, extra);
if (method == "post" || method == "put" || method == "patch") {
request[method](payloadObj, onComplete);
} else {
request[method](onComplete);
try {
if (method == "post" || method == "put" || method == "patch") {
request[method](payloadObj, onComplete);
} else {
request[method](onComplete);
}
} catch (ex) {
log.error("Failed to make hawk request", ex);
deferred.reject(ex);
}
return deferred.promise;

View File

@ -458,6 +458,7 @@ RESTRequest.prototype = {
if (!statusSuccess) {
let message = Components.Exception("", statusCode).name;
let error = Components.Exception(message, statusCode);
this._log.debug(this.method + " " + uri + " failed: " + statusCode + " - " + message);
this.onComplete(error);
this.onComplete = this.onProgress = null;
return;

View File

@ -9,4 +9,4 @@ pref("services.common.log.logger.rest.request", "Debug");
pref("services.common.log.logger.rest.response", "Debug");
pref("services.common.storageservice.sendVersionInfo", true);
pref("services.common.tokenserverclient.logger.level", "Info");
pref("services.common.log.logger.tokenserverclient", "Debug");

View File

@ -13,13 +13,13 @@ this.EXPORTED_SYMBOLS = [
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://services-common/rest.js");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://services-common/observers.js");
const Prefs = new Preferences("services.common.tokenserverclient.");
const PREF_LOG_LEVEL = "services.common.log.logger.tokenserverclient";
/**
* Represents a TokenServerClient error that occurred on the client.
@ -140,7 +140,11 @@ TokenServerClientServerError.prototype._toStringFields = function() {
*/
this.TokenServerClient = function TokenServerClient() {
this._log = Log.repository.getLogger("Common.TokenServerClient");
this._log.level = Log.Level[Prefs.get("logger.level")];
let level = "Debug";
try {
level = Services.prefs.getCharPref(PREF_LOG_LEVEL);
} catch (ex) {}
this._log.level = Log.Level[level];
}
TokenServerClient.prototype = {
/**
@ -404,7 +408,7 @@ TokenServerClient.prototype = {
}
}
this._log.debug("Successful token response: " + result.id);
this._log.debug("Successful token response");
cb(null, {
id: result.id,
key: result.key,

View File

@ -370,7 +370,7 @@ this.FxAccountsClient.prototype = {
this,
"fxaBackoffTimer"
);
}
}
deferred.reject(error);
}
);

View File

@ -285,7 +285,7 @@ HealthReporterState.prototype = Object.freeze({
yield this.save();
prefs.reset(["lastSubmitID", "lastPingTime"]);
} else {
this._log.warn("No prefs data found.");
this._log.debug("No prefs data found.");
}
},
});

View File

@ -185,7 +185,7 @@ this.BrowserIDManager.prototype = {
// Reset the world before we do anything async.
this.whenReadyToAuthenticate = Promise.defer();
this.whenReadyToAuthenticate.promise.then(null, (err) => {
this._log.error("Could not authenticate: " + err);
this._log.error("Could not authenticate", err);
});
// initializeWithCurrentIdentity() can be called after the
@ -244,11 +244,11 @@ this.BrowserIDManager.prototype = {
this._shouldHaveSyncKeyBundle = true; // but we probably don't have one...
this.whenReadyToAuthenticate.reject(err);
// report what failed...
this._log.error("Background fetch for key bundle failed: " + err);
this._log.error("Background fetch for key bundle failed", err);
});
// and we are done - the fetch continues on in the background...
}).then(null, err => {
this._log.error("Processing logged in account: " + err);
this._log.error("Processing logged in account", err);
});
},
@ -512,7 +512,9 @@ this.BrowserIDManager.prototype = {
return true;
},
// Refresh the sync token for our user.
// Refresh the sync token for our user. Returns a promise that resolves
// with a token (which may be null in one sad edge-case), or rejects with an
// error.
_fetchTokenForUser: function() {
let tokenServerURI = Svc.Prefs.get("tokenServerURI");
if (tokenServerURI.endsWith("/")) { // trailing slashes cause problems...
@ -527,18 +529,16 @@ this.BrowserIDManager.prototype = {
// return null for the token - sync calling unlockAndVerifyAuthState()
// before actually syncing will setup the error states if necessary.
if (!this._canFetchKeys()) {
log.info("_fetchTokenForUser has no keys to use.");
return null;
return Promise.resolve(null);
}
log.info("Fetching assertion and token from: " + tokenServerURI);
let maybeFetchKeys = () => {
// This is called at login time and every time we need a new token - in
// the latter case we already have kA and kB, so optimise that case.
if (userData.kA && userData.kB) {
return;
}
log.info("Fetching new keys");
return this._fxaService.getKeys().then(
newUserData => {
userData = newUserData;
@ -565,7 +565,7 @@ this.BrowserIDManager.prototype = {
}
let getAssertion = () => {
log.debug("Getting an assertion");
log.info("Getting an assertion from", tokenServerURI);
let audience = Services.io.newURI(tokenServerURI, null, null).prePath;
return fxa.getAssertion(audience);
};
@ -594,7 +594,7 @@ this.BrowserIDManager.prototype = {
if (err.response && err.response.status === 401) {
err = new AuthenticationError(err);
// A hawkclient error.
} else if (err.code === 401) {
} else if (err.code && err.code === 401) {
err = new AuthenticationError(err);
}
@ -602,13 +602,13 @@ this.BrowserIDManager.prototype = {
// properly: auth error getting assertion, auth error getting token (invalid generation
// and client-state error)
if (err instanceof AuthenticationError) {
this._log.error("Authentication error in _fetchTokenForUser: " + err);
this._log.error("Authentication error in _fetchTokenForUser", err);
// set it to the "fatal" LOGIN_FAILED_LOGIN_REJECTED reason.
this._authFailureReason = LOGIN_FAILED_LOGIN_REJECTED;
} else {
this._log.error("Non-authentication error in _fetchTokenForUser: "
+ (err.message || err));
// for now assume it is just a transient network related problem.
this._log.error("Non-authentication error in _fetchTokenForUser", err);
// for now assume it is just a transient network related problem
// (although sadly, it might also be a regular unhandled exception)
this._authFailureReason = LOGIN_FAILED_NETWORK_ERROR;
}
// this._authFailureReason being set to be non-null in the above if clause
@ -629,6 +629,9 @@ this.BrowserIDManager.prototype = {
this._log.debug("_ensureValidToken already has one");
return Promise.resolve();
}
// reset this._token as a safety net to reduce the possibility of us
// repeatedly attempting to use an invalid token if _fetchTokenForUser throws.
this._token = null;
return this._fetchTokenForUser().then(
token => {
this._token = token;
@ -657,7 +660,7 @@ this.BrowserIDManager.prototype = {
try {
cb.wait();
} catch (ex) {
this._log.error("Failed to fetch a token for authentication: " + ex);
this._log.error("Failed to fetch a token for authentication", ex);
return null;
}
if (!this._token) {
@ -708,6 +711,17 @@ BrowserIDClusterManager.prototype = {
_findCluster: function() {
let endPointFromIdentityToken = function() {
// The only reason (in theory ;) that we can end up with a null token
// is when this.identity._canFetchKeys() returned false. In turn, this
// should only happen if the master-password is locked or the credentials
// storage is screwed, and in those cases we shouldn't have started
// syncing so shouldn't get here anyway.
// But better safe than sorry! To keep things clearer, throw an explicit
// exception - the message will appear in the logs and the error will be
// treated as transient.
if (!this.identity._token) {
throw new Error("Can't get a cluster URL as we can't fetch keys.");
}
let endpoint = this.identity._token.endpoint;
// For Sync 1.5 storage endpoints, we use the base endpoint verbatim.
// However, it should end in "/" because we will extend it with
@ -742,6 +756,7 @@ BrowserIDClusterManager.prototype = {
cb(null, clusterURL);
}).then(
null, err => {
log.info("Failed to fetch the cluster URL", err);
// service.js's verifyLogin() method will attempt to fetch a cluster
// URL when it sees a 401. If it gets null, it treats it as a "real"
// auth error and sets Status.login to LOGIN_FAILED_LOGIN_REJECTED, which

View File

@ -592,8 +592,13 @@ ErrorHandler.prototype = {
fapp.level = Log.Level[Svc.Prefs.get("log.appender.file.level")];
root.addAppender(fapp);
// Arrange for the FxA logs to also go to our file.
Log.repository.getLogger("FirefoxAccounts").addAppender(fapp);
// Arrange for the FxA, Hawk and TokenServer logs to also go to our appenders.
for (let extra of ["FirefoxAccounts", "Hawk", "Common.TokenServerClient"]) {
let log = Log.repository.getLogger(extra);
for (let appender of [fapp, dapp, capp]) {
log.addAppender(appender);
}
}
},
observe: function observe(subject, topic, data) {

View File

@ -95,6 +95,7 @@ add_task(function test_initialializeWithNoKeys() {
yield browseridManager.whenReadyToAuthenticate.promise;
do_check_eq(Status.login, LOGIN_SUCCEEDED, "login succeeded even without keys");
do_check_false(browseridManager._canFetchKeys(), "_canFetchKeys reflects lack of keys");
do_check_eq(browseridManager._token, null, "we don't have a token");
});
add_test(function test_getResourceAuthenticator() {

View File

@ -51,6 +51,19 @@
PRLogModuleInfo* gStorageLog = nullptr;
#endif
// Checks that the protected code is running on the main-thread only if the
// connection was also opened on it.
#ifdef DEBUG
#define CHECK_MAINTHREAD_ABUSE() \
do { \
nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); \
NS_WARN_IF_FALSE(threadOpenedOn == mainThread || !NS_IsMainThread(), \
"Using Storage synchronous API on main-thread, but the connection was opened on another thread."); \
} while(0)
#else
#define CHECK_MAINTHREAD_ABUSE() do { /* Nothing */ } while(0)
#endif
namespace mozilla {
namespace storage {
@ -1422,6 +1435,7 @@ Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
NS_IMETHODIMP
Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
{
CHECK_MAINTHREAD_ABUSE();
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());

View File

@ -347,22 +347,32 @@ Service::minimizeMemory()
for (uint32_t i = 0; i < connections.Length(); i++) {
nsRefPtr<Connection> conn = connections[i];
if (conn->connectionReady()) {
NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
DebugOnly<nsresult> rv;
if (!conn->connectionReady())
continue;
if (!syncConn) {
nsCOMPtr<mozIStoragePendingStatement> ps;
rv = connections[i]->ExecuteSimpleSQLAsync(shrinkPragma, nullptr,
getter_AddRefs(ps));
} else {
rv = connections[i]->ExecuteSimpleSQL(shrinkPragma);
}
NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
bool onOpenedThread = false;
MOZ_ASSERT(NS_SUCCEEDED(rv),
"Should have been able to purge sqlite caches");
if (!syncConn) {
// This is a mozIStorageAsyncConnection, it can only be used on the main
// thread, so we can do a straight API call.
nsCOMPtr<mozIStoragePendingStatement> ps;
DebugOnly<nsresult> rv =
conn->ExecuteSimpleSQLAsync(shrinkPragma, nullptr, getter_AddRefs(ps));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches");
} else if (NS_SUCCEEDED(conn->threadOpenedOn->IsOnCurrentThread(&onOpenedThread)) &&
onOpenedThread) {
// We are on the opener thread, so we can just proceed.
conn->ExecuteSimpleSQL(shrinkPragma);
} else {
// We are on the wrong thread, the query should be executed on the
// opener thread, so we must dispatch to it.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethodWithArg<const nsCString>(
conn, &Connection::ExecuteSimpleSQL, shrinkPragma);
conn->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
}
}
}

View File

@ -15,9 +15,7 @@ HelperAppDlg.prototype = {
show: function (launcher, ctx, reason) {
launcher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToDisk;
launcher.launchWithApplication(null, false);
},
promptForSaveToFile: function (launcher, ctx, defaultFile, suggestedExtension, forcePrompt) { }
}
}

View File

@ -793,15 +793,6 @@ add_task(function test_common_initialize()
createInstance: function (aOuter, aIid) {
return {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
promptForSaveToFile: function (aLauncher, aWindowContext,
aDefaultFileName,
aSuggestedFileExtension,
aForcePrompt)
{
throw new Components.Exception(
"Synchronous promptForSaveToFile not implemented.",
Cr.NS_ERROR_NOT_AVAILABLE);
},
promptForSaveToFileAsync: function (aLauncher, aWindowContext,
aDefaultFileName,
aSuggestedFileExtension,

View File

@ -195,22 +195,6 @@ nsUnknownContentTypeDialog.prototype = {
bundle.GetStringFromName("badPermissions"));
},
// promptForSaveToFile: Display file picker dialog and return selected file.
// This is called by the External Helper App Service
// after the ucth dialog calls |saveToDisk| with a null
// target filename (no target, therefore user must pick).
//
// Alternatively, if the user has selected to have all
// files download to a specific location, return that
// location and don't ask via the dialog.
//
// Note - this function is called without a dialog, so it cannot access any part
// of the dialog XUL as other functions on this object do.
promptForSaveToFile: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) {
throw new Components.Exception("Async version must be used", Components.results.NS_ERROR_NOT_AVAILABLE);
},
promptForSaveToFileAsync: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) {
var result = null;

View File

@ -2237,24 +2237,16 @@ void nsExternalAppHandler::RequestSaveDestination(const nsAFlatString &aDefaultF
// picker is up would cause Cancel() to be called, and the dialog would be
// released, which would release this object too, which would crash.
// See Bug 249143
nsIFile* fileToUse;
nsRefPtr<nsExternalAppHandler> kungFuDeathGrip(this);
nsCOMPtr<nsIHelperAppLauncherDialog> dlg(mDialog);
rv = mDialog->PromptForSaveToFile(this,
GetDialogParent(),
aDefaultFile.get(),
aFileExtension.get(),
mForceSave, &fileToUse);
if (rv == NS_ERROR_NOT_AVAILABLE) {
// we need to use the async version -> nsIHelperAppLauncherDialog.promptForSaveToFileAsync.
rv = mDialog->PromptForSaveToFileAsync(this,
GetDialogParent(),
aDefaultFile.get(),
aFileExtension.get(),
mForceSave);
} else {
SaveDestinationAvailable(rv == NS_OK ? fileToUse : nullptr);
rv = mDialog->PromptForSaveToFileAsync(this,
GetDialogParent(),
aDefaultFile.get(),
aFileExtension.get(),
mForceSave);
if (NS_FAILED(rv)) {
Cancel(NS_BINDING_ABORTED);
}
}

View File

@ -21,7 +21,7 @@ interface nsIFile;
* will access methods of the nsIHelperAppLauncher passed in to show()
* in order to cause a "save to disk" or "open using" action.
*/
[scriptable, uuid(3ae4dca8-ac91-4891-adcf-3fbebed6170e)]
[scriptable, uuid(bfc739f3-8d75-4034-a6f8-1039a5996bad)]
interface nsIHelperAppLauncherDialog : nsISupports {
/**
* This request is passed to the helper app dialog because Gecko can not
@ -57,32 +57,6 @@ interface nsIHelperAppLauncherDialog : nsISupports {
in nsISupports aWindowContext,
in unsigned long aReason);
/**
* Invoke a save-to-file dialog instead of the full fledged helper app dialog.
* Returns the a nsIFile for the file name/location selected.
*
* @param aLauncher
* A nsIHelperAppLauncher to be invoked when a file is selected.
* @param aWindowContext
* Window associated with action.
* @param aDefaultFileName
* Default file name to provide (can be null)
* @param aSuggestedFileExtension
* Sugested file extension
* @param aForcePrompt
* Set to true to force prompting the user for thet file
* name/location, otherwise perferences may control if the user is
* prompted.
*
* @throws NS_ERROR_NOT_AVAILABLE if the async version of this function
* should be used.
*/
nsIFile promptForSaveToFile(in nsIHelperAppLauncher aLauncher,
in nsISupports aWindowContext,
in wstring aDefaultFileName,
in wstring aSuggestedFileExtension,
in boolean aForcePrompt);
/**
* Async invoke a save-to-file dialog instead of the full fledged helper app
* dialog. When the file is chosen (or the dialog is closed), the callback

View File

@ -84,9 +84,6 @@ function load() {
"The filename should be correctly sanitized");
gCallback();
},
promptForSaveToFile: function(aLauncher, aWindowContext, aDefaultFileName, aSuggestedFileExtension, aForcePrompt) {
return null;
},
QueryInterface: function(aIID) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (aIID.equals(SpecialPowers.Ci.nsISupports) ||

View File

@ -61,9 +61,7 @@ HelperAppDlg.prototype = {
show: function (launcher, ctx, reason, usePrivateUI) {
launcher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToFile;
launcher.launchWithApplication(null, false);
},
promptForSaveToFile: function (launcher, ctx, defaultFile, suggestedExtension, forcePrompt) { }
}
}
// Stolen from XPCOMUtils, since this handy function is not public there

View File

@ -83,6 +83,7 @@ protected:
, button(0)
, buttons(0)
, pressure(0)
, hitCluster(false)
, inputSource(nsIDOMMouseEvent::MOZ_SOURCE_MOUSE)
{
}
@ -127,6 +128,8 @@ public:
// Finger or touch pressure of event. It ranges between 0.0 and 1.0.
float pressure;
// Touch near a cluster of links (true)
bool hitCluster;
// Possible values at nsIDOMMouseEvent
uint16_t inputSource;
@ -143,6 +146,7 @@ public:
button = aEvent.button;
buttons = aEvent.buttons;
pressure = aEvent.pressure;
hitCluster = aEvent.hitCluster;
inputSource = aEvent.inputSource;
}

View File

@ -1704,6 +1704,91 @@ AndroidBridge::GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId,
return true;
}
static float
GetScaleFactor(nsPresContext* mPresContext) {
nsIPresShell* presShell = mPresContext->PresShell();
LayoutDeviceToLayerScale cumulativeResolution(presShell->GetCumulativeResolution().width);
return cumulativeResolution.scale;
}
nsresult
AndroidBridge::CaptureZoomedView (nsIDOMWindow *window, nsIntRect zoomedViewRect, Object::Param buffer,
float zoomFactor) {
nsresult rv;
struct timeval timeStart;
gettimeofday (&timeStart, NULL);
if (!buffer)
return NS_ERROR_FAILURE;
nsCOMPtr < nsIDOMWindowUtils > utils = do_GetInterface (window);
if (!utils)
return NS_ERROR_FAILURE;
JNIEnv* env = GetJNIEnv ();
AutoLocalJNIFrame jniFrame (env, 0);
nsCOMPtr < nsPIDOMWindow > win = do_QueryInterface (window);
if (!win) {
return NS_ERROR_FAILURE;
}
nsRefPtr < nsPresContext > presContext;
nsIDocShell* docshell = win->GetDocShell ();
if (docshell) {
docshell->GetPresContext (getter_AddRefs (presContext));
}
if (!presContext) {
return NS_ERROR_FAILURE;
}
nsCOMPtr < nsIPresShell > presShell = presContext->PresShell ();
float scaleFactor = GetScaleFactor(presContext) ;
nscolor bgColor = NS_RGB (255, 255, 255);
uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING | nsIPresShell::RENDER_DOCUMENT_RELATIVE);
nsRect r (presContext->DevPixelsToAppUnits(zoomedViewRect.x / scaleFactor),
presContext->DevPixelsToAppUnits(zoomedViewRect.y / scaleFactor ),
presContext->DevPixelsToAppUnits(zoomedViewRect.width / scaleFactor ),
presContext->DevPixelsToAppUnits(zoomedViewRect.height / scaleFactor ));
bool is24bit = (GetScreenDepth () == 24);
SurfaceFormat format = is24bit ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::R5G6B5;
gfxImageFormat iFormat = gfx::SurfaceFormatToImageFormat(format);
uint32_t stride = gfxASurface::FormatStrideForWidth(iFormat, zoomedViewRect.width);
uint8_t* data = static_cast<uint8_t*> (env->GetDirectBufferAddress (buffer.Get()));
if (!data) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT (gfxPlatform::GetPlatform ()->SupportsAzureContentForType (BackendType::CAIRO),
"Need BackendType::CAIRO support");
RefPtr < DrawTarget > dt = Factory::CreateDrawTargetForData (
BackendType::CAIRO, data, IntSize (zoomedViewRect.width, zoomedViewRect.height), stride,
format);
if (!dt) {
ALOG_BRIDGE ("Error creating DrawTarget");
return NS_ERROR_FAILURE;
}
nsRefPtr < gfxContext > context = new gfxContext (dt);
context->SetMatrix (context->CurrentMatrix ().Scale(zoomFactor, zoomFactor));
rv = presShell->RenderDocument (r, renderDocFlags, bgColor, context);
if (is24bit) {
gfxUtils::ConvertBGRAtoRGBA (data, stride * zoomedViewRect.height);
}
LayerView::updateZoomedView(buffer);
NS_ENSURE_SUCCESS (rv, rv);
return NS_OK;
}
nsresult AndroidBridge::CaptureThumbnail(nsIDOMWindow *window, int32_t bufW, int32_t bufH, int32_t tabId, Object::Param buffer, bool &shouldStore)
{
nsresult rv;

View File

@ -188,6 +188,7 @@ public:
bool GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult);
bool GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId, uint32_t aFrameId, nsCString & aResult);
nsresult CaptureZoomedView(nsIDOMWindow *window, nsIntRect zoomedViewRect, jni::Object::Param buffer, float zoomFactor);
nsresult CaptureThumbnail(nsIDOMWindow *window, int32_t bufW, int32_t bufH, int32_t tabId, jni::Object::Param buffer, bool &shouldStore);
void GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort);
void ContentDocumentChanged();

View File

@ -538,6 +538,14 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
break;
}
case ZOOMEDVIEW: {
mX = jenv->GetDoubleField(jobj, jXField);
mMetaState = jenv->GetIntField(jobj, jMetaStateField);
ReadPointArray(mPoints, jenv, jPoints, 2);
mByteBuffer = new RefCountedJavaObject(jenv, jenv->GetObjectField(jobj, jByteBufferField));
break;
}
case SCREENORIENTATION_CHANGED: {
mScreenOrientation = jenv->GetShortField(jobj, jScreenOrientationField);
break;

View File

@ -746,6 +746,7 @@ public:
GAMEPAD_ADDREMOVE = 45,
GAMEPAD_DATA = 46,
LONG_PRESS = 47,
ZOOMEDVIEW = 48,
dummy_java_enum_list_end
};

View File

@ -980,6 +980,14 @@ mozilla::jni::Object::LocalRef LayerView::RegisterCompositorWrapper()
return mozilla::jni::Method<RegisterCompositorWrapper_t>::Call(nullptr, nullptr);
}
constexpr char LayerView::updateZoomedView_t::name[];
constexpr char LayerView::updateZoomedView_t::signature[];
void LayerView::updateZoomedView(mozilla::jni::Object::Param a0)
{
return mozilla::jni::Method<updateZoomedView_t>::Call(nullptr, nullptr, a0);
}
constexpr char NativePanZoomController::name[];
constexpr char NativePanZoomController::RequestContentRepaintWrapper_t::name[];

Some files were not shown because too many files have changed in this diff Show More