/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/extensions/default/AutoUpdate/main.js
1 113 строк
40 KB
Swagatam Mitra
Fix for "GetItNow" action behaviour (#14691)
09 апр 2019, 21:12
09 апр 2019, 21:12
a408323
Код
Авторство
О чём код?
/* * Copyright (c) 2018 - present Adobe Systems Incorporated. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ /* eslint-disable indent */ /* eslint-disable max-len */ define(function (require, exports, module) { "use strict"; var CommandManager = brackets.getModule("command/CommandManager"), ProjectManager = brackets.getModule("project/ProjectManager"), Commands = brackets.getModule("command/Commands"), AppInit = brackets.getModule("utils/AppInit"), UpdateNotification = brackets.getModule("utils/UpdateNotification"), DocumentCommandHandlers = brackets.getModule("document/DocumentCommandHandlers"), NodeDomain = brackets.getModule("utils/NodeDomain"), FileSystem = brackets.getModule("filesystem/FileSystem"), PreferencesManager = brackets.getModule("preferences/PreferencesManager"), FileUtils = brackets.getModule("file/FileUtils"), Strings = brackets.getModule("strings"), HealthLogger = brackets.getModule("utils/HealthLogger"), StateHandlerModule = require("StateHandler"), MessageIds = require("MessageIds"), UpdateStatus = require("UpdateStatus"), UpdateInfoBar = require("UpdateInfoBar"); var _modulePath = FileUtils.getNativeModuleDirectoryPath(module), _nodePath = "node/AutoUpdateDomain", _domainPath = [_modulePath, _nodePath].join("/"), updateDomain, domainID; var appSupportDirectory = brackets.app.getApplicationSupportDirectory(), updateDir = appSupportDirectory + '/updateTemp', updateJsonPath = updateDir + '/' + 'updateHelper.json'; var StateHandler = StateHandlerModule.StateHandler, StateHandlerMessages = StateHandlerModule.MessageKeys; var updateJsonHandler; var MAX_DOWNLOAD_ATTEMPTS = 6, downloadAttemptsRemaining; // Below Strings are to identify an AutoUpdate Event. var autoUpdateEventNames = { AUTOUPDATE_DOWNLOAD_START: "DownloadStarted", AUTOUPDATE_DOWNLOAD_COMPLETED: "DownloadCompleted", AUTOUPDATE_DOWNLOAD_FAILED: "DownloadFailed", AUTOUPDATE_DOWNLOAD_COMPLETE_USER_CLICK_RESTART: "DownloadCompletedAndUserClickedRestart", AUTOUPDATE_DOWNLOAD_COMPLETE_USER_CLICK_LATER: "DownloadCompletedAndUserClickedLater", AUTOUPDATE_DOWNLOADCOMPLETE_UPDATE_BAR_RENDERED: "DownloadCompleteUpdateBarRendered", AUTOUPDATE_INSTALLATION_FAILED: "InstallationFailed", AUTOUPDATE_INSTALLATION_SUCCESS: "InstallationSuccess", AUTOUPDATE_CLEANUP_FAILED: "AutoUpdateCleanUpFailed" }; // function map for brackets functions var functionMap = {}; var _updateParams; //Namespacing the event var APP_QUIT_CANCELLED = DocumentCommandHandlers.APP_QUIT_CANCELLED + ".auto-update"; var _nodeErrorMessages = { UPDATEDIR_READ_FAILED: 0, UPDATEDIR_CLEAN_FAILED: 1, CHECKSUM_DID_NOT_MATCH: 2, INSTALLER_NOT_FOUND: 3, DOWNLOAD_ERROR: 4, NETWORK_SLOW_OR_DISCONNECTED: 5 }; var updateProgressKey = "autoUpdateInProgress", isAutoUpdateInitiated = false; /** * Checks if an auto update session is currently in progress * @returns {boolean} - true if an auto update session is currently in progress, false otherwise */ function checkIfAnotherSessionInProgress() { var result = $.Deferred(); if(updateJsonHandler) { var state = updateJsonHandler.state; updateJsonHandler.refresh() .done(function() { var val = updateJsonHandler.get(updateProgressKey); if(val !== null) { result.resolve(val); } else { result.reject(); } }) .fail(function() { updateJsonHandler.state = state; result.reject(); }); } return result.promise(); } /** * Checks if auto update preference is enabled or disabled * @private * @returns {boolean} - true if preference enabled, false otherwise */ function _isAutoUpdateEnabled() { return (PreferencesManager.get("autoUpdate.AutoUpdate") !== false); } /** * Receives messages from node * @param {object} event - event received from node * @param {object} msgObj - json containing - { * fn - function to execute on Brackets side * args - arguments to the above function * requester - ID of the current requester domain */ function receiveMessageFromNode(event, msgObj) { if (functionMap[msgObj.fn]) { if(domainID === msgObj.requester) { functionMap[msgObj.fn].apply(null, msgObj.args); } } } /* * Checks if Brackets version got updated * @returns {boolean} true if version updated, false otherwise */ function checkIfVersionUpdated() { var latestBuildNumber = updateJsonHandler.get("latestBuildNumber"), currentBuildNumber = Number(/-([0-9]+)/.exec(brackets.metadata.version)[1]); return latestBuildNumber === currentBuildNumber; } /** * Gets the arguments to a function in an array * @param {object} args - the arguments object * @returns {Array} - array of actual arguments */ function getFunctionArgs(args) { if (args.length > 1) { var fnArgs = new Array(args.length - 1), i; for (i = 1; i < args.length; ++i) { fnArgs[i - 1] = args[i]; } return fnArgs; } return []; } /** * Posts messages to node * @param {string} messageId - Message to be passed */ function postMessageToNode(messageId) { var msg = { fn: messageId, args: getFunctionArgs(arguments), requester: domainID }; updateDomain.exec('data', msg); } /** * Checks Install failure scenarios */ function checkInstallationStatus() { var searchParams = { "updateDir": updateDir, "installErrorStr": ["ERROR:"], "bracketsErrorStr": ["ERROR:"], "encoding": "utf8" }, // Below are the possible Windows Installer error strings, which will be searched for in the installer logs to track failures. winInstallErrorStrArr = [ "Installation success or error status", "Reconfiguration success or error status" ]; if (brackets.platform === "win") { searchParams.installErrorStr = winInstallErrorStrArr; searchParams.encoding = "utf16le"; } postMessageToNode(MessageIds.CHECK_INSTALLER_STATUS, searchParams); } /** * Checks and handles the update success and failure scenarios */ function checkUpdateStatus() { var filesToCache = ['.logs'], downloadCompleted = updateJsonHandler.get("downloadCompleted"), updateInitiatedInPrevSession = updateJsonHandler.get("updateInitiatedInPrevSession"); if (downloadCompleted && updateInitiatedInPrevSession) { var isNewVersion = checkIfVersionUpdated(); updateJsonHandler.reset(); if (isNewVersion) { // We get here if the update was successful UpdateInfoBar.showUpdateBar({ type: "success", title: Strings.UPDATE_SUCCESSFUL, description: "" }); HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_INSTALLATION_SUCCESS, "autoUpdate", "install", "complete", "" ); } else { // We get here if the update started but failed checkInstallationStatus(); UpdateInfoBar.showUpdateBar({ type: "error", title: Strings.UPDATE_FAILED, description: Strings.GO_TO_SITE }); } } else if (downloadCompleted && !updateInitiatedInPrevSession) { // We get here if the download was complete and user selected UpdateLater if (brackets.platform === "mac") { filesToCache = ['.dmg', '.json']; } else if (brackets.platform === "win") { filesToCache = ['.msi', '.json']; } } postMessageToNode(MessageIds.PERFORM_CLEANUP, filesToCache); } /** * Send Installer Error Code to Analytics Server */ function handleInstallationStatus(statusObj) { var errorCode = "", errorline = statusObj.installError; if (errorline) { errorCode = errorline.substr(errorline.lastIndexOf(':') + 2, errorline.length); } HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_INSTALLATION_FAILED, "autoUpdate", "install", "fail", errorCode ); } /** * Initializes the state of parsed content from updateHelper.json * returns Promise Object Which is resolved when parsing is success * and rejected if parsing is failed. */ function initState() { var result = $.Deferred(); updateJsonHandler.parse() .done(function() { result.resolve(); }) .fail(function (code) { var logMsg; switch (code) { case StateHandlerMessages.FILE_NOT_FOUND: logMsg = "AutoUpdate : updateHelper.json cannot be parsed, does not exist"; break; case StateHandlerMessages.FILE_NOT_READ: logMsg = "AutoUpdate : updateHelper.json could not be read"; break; case StateHandlerMessages.FILE_PARSE_EXCEPTION: logMsg = "AutoUpdate : updateHelper.json could not be parsed, exception encountered"; break; case StateHandlerMessages.FILE_READ_FAIL: logMsg = "AutoUpdate : updateHelper.json could not be parsed"; break; } console.log(logMsg); result.reject(); }); return result.promise(); } /** * Sets up the Auto Update environment */ function setupAutoUpdate() { updateJsonHandler = new StateHandler(updateJsonPath); updateDomain.on('data', receiveMessageFromNode); updateDomain.exec('initNode', { messageIds: MessageIds, updateDir: updateDir, requester: domainID }); } /** * Initializes the state for AutoUpdate process * @returns {$.Deferred} - a jquery promise, * that is resolved with success or failure * of state initialization */ function initializeState() { var result = $.Deferred(); FileSystem.resolve(updateDir, function (err) { if (!err) { result.resolve(); } else { var directory = FileSystem.getDirectoryForPath(updateDir); directory.create(function (error) { if (error) { console.error('AutoUpdate : Error in creating update directory in Appdata'); result.reject(); } else { result.resolve(); } }); } }); return result.promise(); } /** * Handles the auto update event, which is triggered when user clicks GetItNow in UpdateNotification dialog * @param {object} updateParams - json object containing update information { * installerName - name of the installer * downloadURL - download URL * latestBuildNumber - build number * checksum - checksum } */ function initiateAutoUpdate(updateParams) { _updateParams = updateParams; downloadAttemptsRemaining = MAX_DOWNLOAD_ATTEMPTS; initializeState() .done(function () { var setUpdateInProgress = function() { var initNodeFn = function () { isAutoUpdateInitiated = true; postMessageToNode(MessageIds.INITIALIZE_STATE, _updateParams); }; if (updateJsonHandler.get('latestBuildNumber') !== _updateParams.latestBuildNumber) { setUpdateStateInJSON('latestBuildNumber', _updateParams.latestBuildNumber) .done(initNodeFn); } else { initNodeFn(); } }; checkIfAnotherSessionInProgress() .done(function(inProgress) { if(inProgress) { UpdateInfoBar.showUpdateBar({ type: "error", title: Strings.AUTOUPDATE_ERROR, description: Strings.AUTOUPDATE_IN_PROGRESS }); } else { setUpdateStateInJSON("autoUpdateInProgress", true) .done(setUpdateInProgress); } }) .fail(function() { setUpdateStateInJSON("autoUpdateInProgress", true) .done(setUpdateInProgress); }); }) .fail(function () { UpdateInfoBar.showUpdateBar({ type: "error", title: Strings.INITIALISATION_FAILED, description: "" }); }); } /** * Typical signature of an update entry, with the most frequently used keys * @typedef {Object} Update~Entry * @property {Number} buildNumber - The build number for the update * @property {string} versionString - Version for the update * @property {string} releaseNotesURL - URL for release notes for the update * @property {array} newFeatures - Array of new features in the update * @property {boolean} prerelease - Boolean to distinguish prerelease from a stable release * @property {Object} platforms - JSON object, containing asset info for the update, for each platform * Asset info for each platform consists of : * @property {string} checksum - checksum of the asset * @property {string} downloadURL - download URL of the asset * */ /** * Handles and processes the update info, required for app auto update * @private * @param {Array} updates - array of {...Update~Entry} update entries */ function _updateProcessHandler(updates) { if (!updates) { console.warn("AutoUpdate : updates information not available."); return; } var OS = brackets.getPlatformInfo(), checksum, downloadURL, installerName, platforms, latestUpdate; latestUpdate = updates[0]; platforms = latestUpdate ? latestUpdate.platforms : null; if (platforms && platforms[OS]) { //If no checksum field is present then we're setting it to 0, just as a safety check, // although ideally this situation should never occur in releases post its introduction. checksum = platforms[OS].checksum ? platforms[OS].checksum : 0, downloadURL = platforms[OS].downloadURL ? platforms[OS].downloadURL : "", installerName = downloadURL ? downloadURL.split("/").pop() : ""; } else { // Update not present for current platform return false; } if (!checksum || !downloadURL || !installerName) { console.warn("AutoUpdate : asset information incorrect for the update"); return false; } var updateParams = { downloadURL: downloadURL, installerName: installerName, latestBuildNumber: latestUpdate.buildNumber, checksum: checksum }; //Initiate the auto update, with update params initiateAutoUpdate(updateParams); //Send a truthy value to ensure caller is informed about successful initialization of auto-update return true; } /** * Unregisters the App Quit event handler */ function resetAppQuitHandler() { DocumentCommandHandlers.off(APP_QUIT_CANCELLED); } /** * Unsets the Auto Update environment */ function unsetAutoUpdate() { updateJsonHandler = null; updateDomain.off('data'); resetAppQuitHandler(); } /** * Defines preference to enable/disable Auto Update */ function setupAutoUpdatePreference() { PreferencesManager.definePreference("autoUpdate.AutoUpdate", "boolean", true, { description: Strings.DESCRIPTION_AUTO_UPDATE }); // Set or unset the auto update, based on preference state change PreferencesManager.on("change", "autoUpdate.AutoUpdate", function () { if (_isAutoUpdateEnabled()) { setupAutoUpdate(); UpdateNotification.registerUpdateHandler(_updateProcessHandler); } else { unsetAutoUpdate(); UpdateNotification.resetToDefaultUpdateHandler(); } }); } /** * Creates the Node Domain for Auto Update */ function setupAutoUpdateDomain() { updateDomain = new NodeDomain("AutoUpdate", _domainPath); } /** * Overriding the appReady for Auto update */ AppInit.appReady(function () { // Auto Update is supported on Win and Mac, as of now if (brackets.platform === "linux" || !(brackets.app.setUpdateParams)) { return; } setupAutoUpdateDomain(); //Bail out if update domain could not be created if (!updateDomain) { return; } // Check if the update domain is properly initialised updateDomain.promise() .done(function () { setupAutoUpdatePreference(); if (_isAutoUpdateEnabled()) { domainID = (new Date()).getTime().toString(); setupAutoUpdate(); UpdateNotification.registerUpdateHandler(_updateProcessHandler); } }) .fail(function (err) { console.error("AutoUpdate : node domain could not be initialized."); return; }); }); /** * Enables/disables the state of "Auto Update In Progress" in UpdateHandler.json */ function nodeDomainInitialized(reset) { initState() .done(function () { var inProgress = updateJsonHandler.get(updateProgressKey); if (inProgress && reset) { setUpdateStateInJSON(updateProgressKey, !reset) .always(checkUpdateStatus); } else if (!inProgress) { checkUpdateStatus(); } }); } /** * Enables/disables the state of "Check For Updates" menu entry under Help Menu */ function enableCheckForUpdateEntry(enable) { var cfuCommand = CommandManager.get(Commands.HELP_CHECK_FOR_UPDATE); cfuCommand.setEnabled(enable); UpdateNotification.enableUpdateNotificationIcon(enable); } /** * Checks if it is the first iteration of download * @returns {boolean} - true if first iteration, false if it is a retrial of download */ function isFirstIterationDownload() { return (downloadAttemptsRemaining === MAX_DOWNLOAD_ATTEMPTS); } /** * Resets the update state in updatehelper.json in case of failure, * and logs an error with the message * @param {string} message - the message to be logged onto console */ function resetStateInFailure(message) { updateJsonHandler.reset(); UpdateInfoBar.showUpdateBar({ type: "error", title: Strings.UPDATE_FAILED, description: "" }); enableCheckForUpdateEntry(true); console.error(message); } /** * Sets the update state in updateHelper.json in Appdata * @param {string} key - key to be set * @param {string} value - value to be set * @returns {$.Deferred} - a jquery promise, that is resolved with * success or failure of state update in json file */ function setUpdateStateInJSON(key, value) { var result = $.Deferred(); updateJsonHandler.set(key, value) .done(function () { result.resolve(); }) .fail(function () { resetStateInFailure("AutoUpdate : Could not modify updatehelper.json"); result.reject(); }); return result.promise(); } /** * Handles a safe download of the latest installer, * safety is ensured by cleaning up any pre-existing installers * from update directory before beginning a fresh download */ function handleSafeToDownload() { var downloadFn = function () { if (isFirstIterationDownload()) { // For the first iteration of download, show download //status info in Status bar, and pass download to node UpdateStatus.showUpdateStatus("initial-download"); postMessageToNode(MessageIds.DOWNLOAD_INSTALLER, true); } else { /* For the retry iterations of download, modify the download status info in Status bar, and pass download to node */ var attempt = (MAX_DOWNLOAD_ATTEMPTS - downloadAttemptsRemaining); if (attempt > 1) { var info = attempt.toString() + "/5"; var status = { target: "retry-download", spans: [{ id: "attempt", val: info }] }; UpdateStatus.modifyUpdateStatus(status); } else { UpdateStatus.showUpdateStatus("retry-download"); } postMessageToNode(MessageIds.DOWNLOAD_INSTALLER, false); } --downloadAttemptsRemaining; }; if(!isAutoUpdateInitiated) { isAutoUpdateInitiated = true; updateJsonHandler.refresh() .done(function() { setUpdateStateInJSON('downloadCompleted', false) .done(downloadFn); }); } else { setUpdateStateInJSON('downloadCompleted', false) .done(downloadFn); } } /** * Checks if there is an active internet connection available * @returns {boolean} - true if online, false otherwise */ function checkIfOnline() { return window.navigator.onLine; } /** * Attempts a download of the latest installer, while cleaning up any existing downloaded installers */ function attemptToDownload() { if (checkIfOnline()) { postMessageToNode(MessageIds.PERFORM_CLEANUP, ['.json'], true); } else { enableCheckForUpdateEntry(true); UpdateStatus.cleanUpdateStatus(); HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_DOWNLOAD_FAILED, "autoUpdate", "download", "fail", "No Internet connection available." ); UpdateInfoBar.showUpdateBar({ type: "warning", title: Strings.DOWNLOAD_FAILED, description: Strings.INTERNET_UNAVAILABLE }); setUpdateStateInJSON("autoUpdateInProgress", false); } } /** * Validates the checksum of a file against a given checksum * @param {object} params - json containing { * filePath - path to the file, * expectedChecksum - the checksum to validate against } */ function validateChecksum(params) { postMessageToNode(MessageIds.VALIDATE_INSTALLER, params); } /** * Gets the latest installer, by either downloading a new one or fetching the cached download. */ function getLatestInstaller() { var downloadCompleted = updateJsonHandler.get('downloadCompleted'); if (!downloadCompleted) { attemptToDownload(); } else { validateChecksum(); } } /** * Handles the show status information callback from Node. * It modifies the info displayed on Status bar. * @param {object} statusObj - json containing status info { * target - id of string to display, * spans - Array containing json objects of type - { * id - span id, * val - string to fill the span element with } * } */ function showStatusInfo(statusObj) { if (statusObj.target === "initial-download") { HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_DOWNLOAD_START, "autoUpdate", "download", "started", "" ); UpdateStatus.modifyUpdateStatus(statusObj); } UpdateStatus.displayProgress(statusObj); } /** * Handles the error messages from Node, in a popup displayed to the user. * @param {string} message - error string */ function showErrorMessage(message) { var analyticsDescriptionMessage = ""; switch (message) { case _nodeErrorMessages.UPDATEDIR_READ_FAILED: analyticsDescriptionMessage = "Update directory could not be read."; break; case _nodeErrorMessages.UPDATEDIR_CLEAN_FAILED: analyticsDescriptionMessage = "Update directory could not be cleaned."; break; } console.log("AutoUpdate : Clean-up failed! Reason : " + analyticsDescriptionMessage + ".\n"); HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_CLEANUP_FAILED, "autoUpdate", "cleanUp", "fail", analyticsDescriptionMessage ); } /** * Handles the Cancel button click by user in * Unsaved changes prompt, which would come up if user * has dirty files and he/she clicked UpdateNow */ function dirtyFileSaveCancelled() { UpdateInfoBar.showUpdateBar({ type: "warning", title: Strings.WARNING_TYPE, description: Strings.UPDATE_ON_NEXT_LAUNCH }); } /** * Registers the App Quit event handler, in case of dirty * file save cancelled scenario, while Auto Update is scheduled to run on quit */ function setAppQuitHandler() { resetAppQuitHandler(); DocumentCommandHandlers.on(APP_QUIT_CANCELLED, dirtyFileSaveCancelled); } /** * Initiates the update process, when user clicks UpdateNow in the update popup * @param {string} formattedInstallerPath - formatted path to the latest installer * @param {string} formattedLogFilePath - formatted path to the installer log file * @param {string} installStatusFilePath - path to the install status log file */ function initiateUpdateProcess(formattedInstallerPath, formattedLogFilePath, installStatusFilePath) { // Get additional update parameters on Mac : installDir, appName, and updateDir function getAdditionalParams() { var retval = {}; var installDir = FileUtils.getNativeBracketsDirectoryPath(); if (installDir) { var appPath = installDir.split("/Contents/www")[0]; installDir = appPath.substr(0, appPath.lastIndexOf('/')); var appName = appPath.substr(appPath.lastIndexOf('/') + 1); retval = { installDir: installDir, appName: appName, updateDir: updateDir }; } return retval; } // Update function, to carry out app update var updateFn = function () { var infoObj = { installerPath: formattedInstallerPath, logFilePath: formattedLogFilePath, installStatusFilePath: installStatusFilePath }; if (brackets.platform === "mac") { var additionalParams = getAdditionalParams(), key; for (key in additionalParams) { if (additionalParams.hasOwnProperty(key)) { infoObj[key] = additionalParams[key]; } } } // Set update parameters for app update if (brackets.app.setUpdateParams) { brackets.app.setUpdateParams(JSON.stringify(infoObj), function (err) { if (err) { resetStateInFailure("AutoUpdate : Update parameters could not be set for the installer. Error encountered: " + err); } else { setAppQuitHandler(); CommandManager.execute(Commands.FILE_QUIT); } }); } else { resetStateInFailure("AutoUpdate : setUpdateParams could not be found in shell"); } }; setUpdateStateInJSON('updateInitiatedInPrevSession', true) .done(updateFn); } /** * Detaches the Update Bar Buttons event handlers */ function detachUpdateBarBtnHandlers() { UpdateInfoBar.off(UpdateInfoBar.RESTART_BTN_CLICKED); UpdateInfoBar.off(UpdateInfoBar.LATER_BTN_CLICKED); } /** * Handles the installer validation callback from Node * @param {object} statusObj - json containing - { * valid - (boolean)true for a valid installer, false otherwise, * installerPath, logFilePath, * installStatusFilePath - for a valid installer, * err - for an invalid installer } */ function handleValidationStatus(statusObj) { enableCheckForUpdateEntry(true); UpdateStatus.cleanUpdateStatus(); if (statusObj.valid) { // Installer is validated successfully var statusValidFn = function () { // Restart button click handler var restartBtnClicked = function () { HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_DOWNLOAD_COMPLETE_USER_CLICK_RESTART, "autoUpdate", "installNotification", "installNow ", "click" ); detachUpdateBarBtnHandlers(); initiateUpdateProcess(statusObj.installerPath, statusObj.logFilePath, statusObj.installStatusFilePath); }; // Later button click handler var laterBtnClicked = function () { HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_DOWNLOAD_COMPLETE_USER_CLICK_LATER, "autoUpdate", "installNotification", "cancel", "click" ); detachUpdateBarBtnHandlers(); setUpdateStateInJSON('updateInitiatedInPrevSession', false); }; //attaching UpdateBar handlers UpdateInfoBar.on(UpdateInfoBar.RESTART_BTN_CLICKED, restartBtnClicked); UpdateInfoBar.on(UpdateInfoBar.LATER_BTN_CLICKED, laterBtnClicked); UpdateInfoBar.showUpdateBar({ title: Strings.DOWNLOAD_COMPLETE, description: Strings.CLICK_RESTART_TO_UPDATE, needButtons: true }); setUpdateStateInJSON("autoUpdateInProgress", false); HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_DOWNLOADCOMPLETE_UPDATE_BAR_RENDERED, "autoUpdate", "installNotification", "render", "" ); }; if(!isAutoUpdateInitiated) { isAutoUpdateInitiated = true; updateJsonHandler.refresh() .done(function() { setUpdateStateInJSON('downloadCompleted', true) .done(statusValidFn); }); } else { setUpdateStateInJSON('downloadCompleted', true) .done(statusValidFn); } } else { // Installer validation failed if (updateJsonHandler.get("downloadCompleted")) { // If this was a cached download, retry downloading updateJsonHandler.reset(); var statusInvalidFn = function () { downloadAttemptsRemaining = MAX_DOWNLOAD_ATTEMPTS; getLatestInstaller(); }; setUpdateStateInJSON('downloadCompleted', false) .done(statusInvalidFn); } else { // If this is a new download, prompt the message on update bar var descriptionMessage = "", analyticsDescriptionMessage = ""; switch (statusObj.err) { case _nodeErrorMessages.CHECKSUM_DID_NOT_MATCH: descriptionMessage = Strings.CHECKSUM_DID_NOT_MATCH; analyticsDescriptionMessage = "Checksum didn't match."; break; case _nodeErrorMessages.INSTALLER_NOT_FOUND: descriptionMessage = Strings.INSTALLER_NOT_FOUND; analyticsDescriptionMessage = "Installer not found."; break; } HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_DOWNLOAD_FAILED, "autoUpdate", "download", "fail", analyticsDescriptionMessage ); UpdateInfoBar.showUpdateBar({ type: "error", title: Strings.VALIDATION_FAILED, description: descriptionMessage }); setUpdateStateInJSON("autoUpdateInProgress", false); } } } /** * Handles the download failure callback from Node * @param {string} message - reason of download failure */ function handleDownloadFailure(message) { console.log("AutoUpdate : Download of latest installer failed in Attempt " + (MAX_DOWNLOAD_ATTEMPTS - downloadAttemptsRemaining) + ".\n Reason : " + message); if (downloadAttemptsRemaining) { // Retry the downloading attemptToDownload(); } else { // Download could not completed, all attempts exhausted enableCheckForUpdateEntry(true); UpdateStatus.cleanUpdateStatus(); var descriptionMessage = "", analyticsDescriptionMessage = ""; if (message === _nodeErrorMessages.DOWNLOAD_ERROR) { descriptionMessage = Strings.DOWNLOAD_ERROR; analyticsDescriptionMessage = "Error occurred while downloading."; } else if (message === _nodeErrorMessages.NETWORK_SLOW_OR_DISCONNECTED) { descriptionMessage = Strings.NETWORK_SLOW_OR_DISCONNECTED; analyticsDescriptionMessage = "Network is Disconnected or too slow."; } HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_DOWNLOAD_FAILED, "autoUpdate", "download", "fail", analyticsDescriptionMessage ); UpdateInfoBar.showUpdateBar({ type: "error", title: Strings.DOWNLOAD_FAILED, description: descriptionMessage }); setUpdateStateInJSON("autoUpdateInProgress", false); } } /** * Handles the completion of node state initialization */ function handleInitializationComplete() { enableCheckForUpdateEntry(false); getLatestInstaller(); } /** * Handles Download completion callback from Node */ function handleDownloadSuccess() { HealthLogger.sendAnalyticsData( autoUpdateEventNames.AUTOUPDATE_DOWNLOAD_COMPLETED, "autoUpdate", "download", "complete", "" ); UpdateStatus.showUpdateStatus("validating-installer"); validateChecksum(); } function _handleAppClose() { postMessageToNode(MessageIds.REMOVE_FROM_REQUESTERS); } /** * Generates a map for brackets side functions */ function registerBracketsFunctions() { functionMap["brackets.notifyinitializationComplete"] = handleInitializationComplete; functionMap["brackets.showStatusInfo"] = showStatusInfo; functionMap["brackets.notifyDownloadSuccess"] = handleDownloadSuccess; functionMap["brackets.showErrorMessage"] = showErrorMessage; functionMap["brackets.notifyDownloadFailure"] = handleDownloadFailure; functionMap["brackets.notifySafeToDownload"] = handleSafeToDownload; functionMap["brackets.notifyvalidationStatus"] = handleValidationStatus; functionMap["brackets.notifyInstallationStatus"] = handleInstallationStatus; ProjectManager.on("beforeProjectClose beforeAppClose", _handleAppClose); } functionMap["brackets.nodeDomainInitialized"] = nodeDomainInitialized; functionMap["brackets.registerBracketsFunctions"] = registerBracketsFunctions; });