diff options
Diffstat (limited to 'toolkit/mozapps/installer')
23 files changed, 12445 insertions, 0 deletions
diff --git a/toolkit/mozapps/installer/find-dupes.py b/toolkit/mozapps/installer/find-dupes.py new file mode 100644 index 0000000000..1931481b7c --- /dev/null +++ b/toolkit/mozapps/installer/find-dupes.py @@ -0,0 +1,148 @@ +# 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/. + +import sys +import hashlib +import functools +from mozbuild.preprocessor import Preprocessor +from mozbuild.util import DefinesAction +from mozpack.packager.unpack import UnpackFinder +from mozpack.files import DeflatedFile +from collections import OrderedDict +from io import StringIO +import argparse +import buildconfig + +""" +Find files duplicated in a given packaged directory, independently of its +package format. +""" + + +def normalize_osx_path(p): + """ + Strips the first 3 elements of an OSX app path + + >>> normalize_osx_path('Nightly.app/foo/bar/baz') + 'baz' + """ + bits = p.split("/") + if len(bits) > 3 and bits[0].endswith(".app"): + return "/".join(bits[3:]) + return p + + +def is_l10n_file(path): + return ( + "/locale/" in path + or "/localization/" in path + or path.startswith("localization/") + ) + + +def normalize_path(p): + return normalize_osx_path(p) + + +def find_dupes(source, allowed_dupes, bail=True): + chunk_size = 1024 * 10 + allowed_dupes = set(allowed_dupes) + checksums = OrderedDict() + for p, f in UnpackFinder(source): + checksum = hashlib.sha1() + content_size = 0 + for buf in iter(functools.partial(f.open().read, chunk_size), b""): + checksum.update(buf) + content_size += len(buf) + m = checksum.digest() + if m not in checksums: + if isinstance(f, DeflatedFile): + compressed = f.file.compressed_size + else: + compressed = content_size + checksums[m] = (content_size, compressed, []) + checksums[m][2].append(p) + total = 0 + total_compressed = 0 + num_dupes = 0 + unexpected_dupes = [] + for m, (size, compressed, paths) in sorted( + checksums.items(), key=lambda x: x[1][1] + ): + if len(paths) > 1: + _compressed = " (%d compressed)" % compressed if compressed != size else "" + _times = " (%d times)" % (len(paths) - 1) if len(paths) > 2 else "" + print("Duplicates {} bytes{}{}:".format(size, _compressed, _times)) + print("".join(" %s\n" % p for p in paths)) + total += (len(paths) - 1) * size + total_compressed += (len(paths) - 1) * compressed + num_dupes += 1 + + for p in paths: + if not is_l10n_file(p) and normalize_path(p) not in allowed_dupes: + unexpected_dupes.append(p) + + if num_dupes: + total_compressed = ( + "%d compressed" % total_compressed + if total_compressed != total + else "uncompressed" + ) + print( + "WARNING: Found {} duplicated files taking {} bytes ({})".format( + num_dupes, total, total_compressed + ) + ) + + if unexpected_dupes: + errortype = "ERROR" if bail else "WARNING" + print("{}: The following duplicated files are not allowed:".format(errortype)) + print("\n".join(unexpected_dupes)) + if bail: + sys.exit(1) + + +def main(): + parser = argparse.ArgumentParser(description="Find duplicate files in directory.") + parser.add_argument( + "--warning", + "-w", + action="store_true", + help="Only warn about duplicates, do not exit with an error", + ) + parser.add_argument( + "--file", + "-f", + action="append", + dest="dupes_files", + default=[], + help="Add exceptions to the duplicate list from this file", + ) + parser.add_argument("-D", action=DefinesAction) + parser.add_argument("-U", action="append", default=[]) + parser.add_argument("directory", help="The directory to check for duplicates in") + + args = parser.parse_args() + + allowed_dupes = [] + for filename in args.dupes_files: + pp = Preprocessor() + pp.context.update(buildconfig.defines["ALLDEFINES"]) + if args.D: + pp.context.update(args.D) + for undefine in args.U: + if undefine in pp.context: + del pp.context[undefine] + pp.out = StringIO() + pp.do_filter("substitution") + pp.do_include(filename) + allowed_dupes.extend( + [line.partition("#")[0].rstrip() for line in pp.out.getvalue().splitlines()] + ) + + find_dupes(args.directory, bail=not args.warning, allowed_dupes=allowed_dupes) + + +if __name__ == "__main__": + main() diff --git a/toolkit/mozapps/installer/informulate.py b/toolkit/mozapps/installer/informulate.py new file mode 100644 index 0000000000..0f646450c5 --- /dev/null +++ b/toolkit/mozapps/installer/informulate.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# 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/. + +# Generate build info files for use by other tools. +# This script assumes it is being run in a Mozilla CI build. + +from argparse import ArgumentParser +import datetime +import buildconfig +import json +import mozinfo +import os + + +def main(): + parser = ArgumentParser() + parser.add_argument("output_json", help="Output JSON file") + parser.add_argument("buildhub_json", help="Output buildhub JSON file") + parser.add_argument("output_txt", help="Output text file") + # TODO: Move package-name.mk variables into moz.configure. + parser.add_argument("pkg_platform", help="Package platform identifier") + parser.add_argument( + "--no-download", action="store_true", help="Do not include download information" + ) + parser.add_argument("--package", help="Path to application package file") + parser.add_argument("--installer", help="Path to application installer file") + args = parser.parse_args() + mozinfo.find_and_update_from_json() + + important_substitutions = [ + "target_alias", + "host_alias", + "MOZ_UPDATE_CHANNEL", + "MOZ_APP_VENDOR", + "MOZ_APP_NAME", + "MOZ_APP_VERSION", + "MOZ_APP_MAXVERSION", + "MOZ_APP_ID", + "MOZ_SOURCE_REPO", + ] + other_substitutions = [ + "CC", + "CXX", + "AS", + ] + + all_key_value_pairs = { + x.lower(): buildconfig.substs[x] for x in important_substitutions + } + + def stringify(x): + if isinstance(x, (tuple, list)): + return " ".join(x) + return x or "" + + all_key_value_pairs.update( + {x.lower(): stringify(buildconfig.substs.get(x)) for x in other_substitutions} + ) + build_id = os.environ["MOZ_BUILD_DATE"] + all_key_value_pairs.update( + { + "buildid": build_id, + "moz_source_stamp": buildconfig.substs["MOZ_SOURCE_CHANGESET"], + "moz_pkg_platform": args.pkg_platform, + } + ) + + with open(args.output_json, "wt") as f: + json.dump(all_key_value_pairs, f, indent=2, sort_keys=True) + f.write("\n") + + with open(args.buildhub_json, "wt") as f: + build_time = datetime.datetime.strptime(build_id, "%Y%m%d%H%M%S") + s = buildconfig.substs + record = { + "build": { + "id": build_id, + "date": build_time.isoformat() + "Z", + "as": all_key_value_pairs["as"], + "cc": all_key_value_pairs["cc"], + "cxx": all_key_value_pairs["cxx"], + "host": s["host_alias"], + "target": s["target_alias"], + }, + "source": { + "product": s["MOZ_APP_NAME"], + "repository": s["MOZ_SOURCE_REPO"], + "tree": os.environ["MH_BRANCH"], + "revision": s["MOZ_SOURCE_CHANGESET"], + }, + "target": { + "platform": args.pkg_platform, + "os": mozinfo.info["os"], + # This would be easier if the locale was specified at configure time. + "locale": os.environ.get("AB_CD", "en-US"), + "version": s["MOZ_APP_VERSION_DISPLAY"] or s["MOZ_APP_VERSION"], + "channel": s["MOZ_UPDATE_CHANNEL"], + }, + } + + if args.no_download: + package = None + elif args.installer and os.path.exists(args.installer): + package = args.installer + else: + package = args.package + if package: + st = os.stat(package) + mtime = datetime.datetime.fromtimestamp(st.st_mtime) + record["download"] = { + # The release pipeline will update these keys. + "url": os.path.basename(package), + "mimetype": "application/octet-stream", + "date": mtime.isoformat() + "Z", + "size": st.st_size, + } + + json.dump(record, f, indent=2, sort_keys=True) + f.write("\n") + + with open(args.output_txt, "wt") as f: + f.write("buildID={}\n".format(build_id)) + + +if __name__ == "__main__": + main() diff --git a/toolkit/mozapps/installer/js-compare-ast.js b/toolkit/mozapps/installer/js-compare-ast.js new file mode 100644 index 0000000000..ed660d3f8a --- /dev/null +++ b/toolkit/mozapps/installer/js-compare-ast.js @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This script compares the AST of two JavaScript files passed as arguments. + * The script exits with a 0 status code if both files parse properly and the + * ASTs of both files are identical modulo location differences. The script + * exits with status code 1 if any of these conditions don't hold. + * + * This script is used as part of packaging to verify minified JavaScript files + * are identical to their original files. + */ + +// Available to the js shell. +/* global snarf, scriptArgs, quit */ + +"use strict"; + +function ast(filename) { + return JSON.stringify(Reflect.parse(snarf(filename), { loc: 0 })); +} + +if (scriptArgs.length !== 2) { + throw new Error("usage: js js-compare-ast.js FILE1.js FILE2.js"); +} + +var ast0 = ast(scriptArgs[0]); +var ast1 = ast(scriptArgs[1]); + +quit(ast0 == ast1 ? 0 : 1); diff --git a/toolkit/mozapps/installer/l10n-repack.py b/toolkit/mozapps/installer/l10n-repack.py new file mode 100644 index 0000000000..a6f7ebb4dd --- /dev/null +++ b/toolkit/mozapps/installer/l10n-repack.py @@ -0,0 +1,80 @@ +# 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/. + +""" +Replace localized parts of a packaged directory with data from a langpack +directory. +""" + +from mozpack.packager import l10n +from argparse import ArgumentParser +import buildconfig + +# Set of files or directories not listed in a chrome.manifest but that are +# localized. +NON_CHROME = set( + [ + "**/crashreporter*.ini", + "dictionaries", + "defaultagent_localized.ini", + "defaults/profile", + "defaults/pref*/*-l10n.js", + "locale.ini", + "update.locale", + "updater.ini", + "extensions/langpack-*@*", + "distribution/extensions/langpack-*@*", + "**/multilocale.txt", + ] +) + + +def valid_extra_l10n(arg): + if "=" not in arg: + raise ValueError("Invalid value") + return tuple(arg.split("=", 1)) + + +def main(): + parser = ArgumentParser() + parser.add_argument("build", help="Directory containing the build to repack") + parser.add_argument("l10n", help="Directory containing the staged langpack") + parser.add_argument( + "extra_l10n", + nargs="*", + metavar="BASE=PATH", + type=valid_extra_l10n, + help="Extra directories with staged localized files " + "to be considered under the given base in the " + "repacked build", + ) + parser.add_argument( + "--non-resource", + nargs="+", + metavar="PATTERN", + default=[], + help="Extra files not to be considered as resources", + ) + parser.add_argument( + "--minify", + action="store_true", + default=False, + help="Make some files more compact while packaging", + ) + args = parser.parse_args() + + buildconfig.substs["USE_ELF_HACK"] = False + buildconfig.substs["PKG_STRIP"] = False + l10n.repack( + args.build, + args.l10n, + extra_l10n=dict(args.extra_l10n), + non_resources=args.non_resource, + non_chrome=NON_CHROME, + minify=args.minify, + ) + + +if __name__ == "__main__": + main() diff --git a/toolkit/mozapps/installer/linux/rpm/mozilla.desktop b/toolkit/mozapps/installer/linux/rpm/mozilla.desktop new file mode 100644 index 0000000000..79048fcf7c --- /dev/null +++ b/toolkit/mozapps/installer/linux/rpm/mozilla.desktop @@ -0,0 +1,21 @@ +#filter substitution +# 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/. + +[Desktop Entry] +Version=1.0 +Name=@MOZ_APP_DISPLAYNAME@ +GenericName=Web Browser +Comment=Your web, the way you like it +Exec=@MOZ_APP_NAME@ +Icon=@MOZ_APP_NAME@ +Terminal=false +Type=Application +StartupWMClass=@MOZ_APP_REMOTINGNAME@ +MimeType=text/html;text/xml;application/xhtml+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https; +StartupNotify=true +X-MultipleArgs=false +X-Desktop-File-Install-Version=0.16 +Categories=Network;WebBrowser; +Encoding=UTF-8 diff --git a/toolkit/mozapps/installer/linux/rpm/mozilla.spec b/toolkit/mozapps/installer/linux/rpm/mozilla.spec new file mode 100644 index 0000000000..21f7c3a60c --- /dev/null +++ b/toolkit/mozapps/installer/linux/rpm/mozilla.spec @@ -0,0 +1,116 @@ +# 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/. + +%global __jar_repack %{nil} + +#Use a consistent string to refer to the package by +%define pr_name "%{moz_app_displayname} %{moz_app_version}" + +Name: %{moz_app_name} +Version: %{moz_numeric_app_version} +Release: %{?moz_rpm_release:%{moz_rpm_release}}%{?buildid:.%{buildid}} +Summary: %{pr_name} +Group: Applications/Internet +License: MPL 2 +Vendor: Mozilla +URL: http://www.mozilla.org/projects/firefox/ +Source0: %{name}.desktop +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +#AutoProv: no + +BuildRequires: desktop-file-utils + + +%description +%{pr_name}. This package was built from +%{moz_source_repo}/rev/%{moz_source_stamp} + +#We only want a subpackage for the SDK if the required +#files were generated. Like the tests subpackage, we +#probably only need to conditionaly define the %files +#section. +%if %{?createtests:1} +%package tests +Summary: %{pr_name} tests +Group: Developement/Libraries +requires: %{name} = %{version}-%{release} +%description tests +%{pr_name} test harness files and test cases +%endif + +%prep +echo No-op prep + + +%build +echo No-op build + + +%install +rm -rf $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT +desktop-file-validate %{SOURCE0} +desktop-file-install --vendor mozilla \ + --dir $RPM_BUILD_ROOT%{_datadir}/applications \ + %{SOURCE0} +#In order to make branding work in a generic way, We find +#all the icons that are likely to be used for desktop files +#and install them appropriately +find %{moz_branding_directory} -name "default*.png" | tee icons.list +for i in $(cat icons.list) ; do + size=$(echo $i | sed "s/.*default\([0-9]*\).png$/\1/") + icondir=$RPM_BUILD_ROOT/%{_datadir}/icons/hicolor/${size}x${size}/apps/ + mkdir -p $icondir + cp -a $i ${icondir}%{name}.png +done +rm icons.list #cleanup + +%if %{?createtests:1} +#wastefully creates a zip file, but ensures that we stage all test suites +make package-tests +testdir=$RPM_BUILD_ROOT/%{_datadir}/%{_testsinstalldir}/tests +mkdir -p $testdir +cp -a dist/test-stage/* $testdir/ +%endif + +%clean +rm -rf $RPM_BUILD_ROOT + + +%post +#this is needed to get gnome-panel to update the icons +update-desktop-database &> /dev/null || : +touch --no-create %{_datadir}/icons/hicolor || : +if [ -x %{_bindir}/gtk-update-icon-cache ] ; then + %{_bindir}/gtk-update-icon-cache --quiet ${_datadir}/icons/hicolor &> /dev/null || : +fi + + +%postun +#this is needed to get gnome-panel to update the icons +update-desktop-database &> /dev/null || : +touch --no-create %{_datadir}/icons/hicolor || : +if [ -x %{_bindir}/gtk-update-icon-cache ] ; then + %{_bindir}/gtk-update-icon-cache --quiet ${_datadir}/icons/hicolor &> /dev/null || : +fi + + +%files +%defattr(-,root,root,-) +%{_installdir} +%{_bindir} +%{_datadir}/applications/ +%{_datadir}/icons/ +%doc + + +%if %{?createtests:1} +%files tests +%{_datadir}/%{_testsinstalldir}/tests/ +%endif + +#%changelog +#* %{name} %{version} %{moz_rpm_release} +#- Please see %{moz_source_repo}/shortlog/%{moz_source_stamp} diff --git a/toolkit/mozapps/installer/moz.build b/toolkit/mozapps/installer/moz.build new file mode 100644 index 0000000000..b92363a166 --- /dev/null +++ b/toolkit/mozapps/installer/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Firefox", "Installer") diff --git a/toolkit/mozapps/installer/package-name.mk b/toolkit/mozapps/installer/package-name.mk new file mode 100644 index 0000000000..36239fe79f --- /dev/null +++ b/toolkit/mozapps/installer/package-name.mk @@ -0,0 +1,135 @@ +# 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/. + +# assemble package names, see convention at +# http://developer.mozilla.org/index.php?title=En/Package_Filename_Convention +# Note that release packages are named during the post-build release +# automation, so they aren't part of this file. + +ifndef PACKAGE_NAME_MK_INCLUDED +PACKAGE_NAME_MK_INCLUDED := 1 + +ifndef MOZ_PKG_VERSION +MOZ_PKG_VERSION = $(MOZ_APP_VERSION) +endif + +ifndef MOZ_PKG_PLATFORM +MOZ_PKG_PLATFORM := $(TARGET_RAW_OS)-$(TARGET_RAW_CPU) + +ifeq ($(MOZ_BUILD_APP),mobile/android) +MOZ_PKG_PLATFORM := android-$(TARGET_RAW_CPU) +endif + +# TARGET_RAW_OS/TARGET_RAW_CPU may be unintuitive, so we hardcode some special formats +ifeq ($(OS_ARCH),WINNT) +ifeq ($(TARGET_CPU),x86) +MOZ_PKG_PLATFORM := win32 +else +ifeq ($(TARGET_CPU),aarch64) +MOZ_PKG_PLATFORM := win64-aarch64 +else +MOZ_PKG_PLATFORM := win64 +endif +endif +endif +ifeq ($(OS_ARCH),Darwin) +MOZ_PKG_PLATFORM := mac +endif +ifeq ($(TARGET_RAW_OS),linux-gnu) +MOZ_PKG_PLATFORM := linux-$(TARGET_RAW_CPU) +endif +endif #MOZ_PKG_PLATFORM + +ifdef MOZ_PKG_SPECIAL +MOZ_PKG_PLATFORM := $(MOZ_PKG_PLATFORM)-$(MOZ_PKG_SPECIAL) +endif + +MOZ_PKG_DIR ?= $(MOZ_APP_NAME) + +ifndef MOZ_PKG_APPNAME +MOZ_PKG_APPNAME = $(MOZ_APP_NAME) +endif + +ifdef MOZ_SIMPLE_PACKAGE_NAME +PKG_BASENAME := $(MOZ_SIMPLE_PACKAGE_NAME) +else +PKG_BASENAME = $(MOZ_PKG_APPNAME)-$(MOZ_PKG_VERSION).$(AB_CD).$(MOZ_PKG_PLATFORM) +endif +PKG_PATH = +SDK_PATH = +PKG_INST_BASENAME = $(PKG_BASENAME).installer +PKG_STUB_BASENAME = $(PKG_BASENAME).installer-stub +PKG_INST_PATH = install/sea/ +PKG_UPDATE_BASENAME = $(PKG_BASENAME) +CHECKSUMS_FILE_BASENAME = $(PKG_BASENAME) +MOZ_INFO_BASENAME = $(PKG_BASENAME) +PKG_UPDATE_PATH = update/ +COMPLETE_MAR = $(PKG_UPDATE_PATH)$(PKG_UPDATE_BASENAME).complete.mar +ifdef MOZ_SIMPLE_PACKAGE_NAME +PKG_LANGPACK_BASENAME = $(MOZ_SIMPLE_PACKAGE_NAME).langpack +PKG_LANGPACK_PATH = +else +PKG_LANGPACK_BASENAME = $(MOZ_PKG_APPNAME)-$(MOZ_PKG_VERSION).$(AB_CD).langpack +PKG_LANGPACK_PATH = $(MOZ_PKG_PLATFORM)/xpi/ +endif +LANGPACK = $(PKG_LANGPACK_PATH)$(PKG_LANGPACK_BASENAME).xpi +PKG_SRCPACK_BASENAME = source +PKG_BUNDLE_BASENAME = $(MOZ_PKG_APPNAME)-$(MOZ_PKG_VERSION) +PKG_SRCPACK_PATH = + +# Symbol package naming +SYMBOL_FULL_ARCHIVE_BASENAME = $(PKG_BASENAME).crashreporter-symbols-full +SYMBOL_ARCHIVE_BASENAME = $(PKG_BASENAME).crashreporter-symbols + +# Generated file package naming +GENERATED_SOURCE_FILE_PACKAGE = $(PKG_BASENAME).generated-files.tar.gz + +# Code coverage package naming +CODE_COVERAGE_ARCHIVE_BASENAME = $(PKG_BASENAME).code-coverage-gcno + +# Mozsearch package naming +MOZSEARCH_ARCHIVE_BASENAME = $(PKG_BASENAME).mozsearch-index +MOZSEARCH_INCLUDEMAP_BASENAME = $(PKG_BASENAME).mozsearch-distinclude +MOZSEARCH_SCIP_INDEX_BASENAME = $(PKG_BASENAME).mozsearch-scip-index +MOZSEARCH_JAVA_INDEX_BASENAME = $(PKG_BASENAME).mozsearch-java-index + +# Mozharness naming +MOZHARNESS_PACKAGE = mozharness.zip + +# Test package naming +TEST_PACKAGE = $(PKG_BASENAME).common.tests.tar.gz +CPP_TEST_PACKAGE = $(PKG_BASENAME).cppunittest.tests.tar.gz +XPC_TEST_PACKAGE = $(PKG_BASENAME).xpcshell.tests.tar.gz +MOCHITEST_PACKAGE = $(PKG_BASENAME).mochitest.tests.tar.gz +REFTEST_PACKAGE = $(PKG_BASENAME).reftest.tests.tar.gz +WP_TEST_PACKAGE = $(PKG_BASENAME).web-platform.tests.tar.gz +TALOS_PACKAGE = $(PKG_BASENAME).talos.tests.tar.gz +AWSY_PACKAGE = $(PKG_BASENAME).awsy.tests.tar.gz +GTEST_PACKAGE = $(PKG_BASENAME).gtest.tests.tar.gz + +# `.xpt` artifacts: for use in artifact builds. +XPT_ARTIFACTS_ARCHIVE_BASENAME = $(PKG_BASENAME).xpt_artifacts + +ifneq (,$(wildcard $(DIST)/bin/application.ini)) +BUILDID = $(shell $(PYTHON3) $(MOZILLA_DIR)/config/printconfigsetting.py $(DIST)/bin/application.ini App BuildID) +else +BUILDID = $(shell $(PYTHON3) $(MOZILLA_DIR)/config/printconfigsetting.py $(DIST)/bin/platform.ini Build BuildID) +endif + +MOZ_SOURCESTAMP_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME).txt +MOZ_BUILDINFO_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME).json +MOZ_BUILDHUB_JSON = $(DIST)/$(PKG_PATH)/buildhub.json +MOZ_BUILDID_INFO_TXT_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME)_info.txt +MOZ_MOZINFO_FILE = $(DIST)/$(PKG_PATH)/$(MOZ_INFO_BASENAME).mozinfo.json +MOZ_TEST_PACKAGES_FILE = $(DIST)/$(PKG_PATH)/$(PKG_BASENAME).test_packages.json + +# JavaScript Shell +ifdef MOZ_SIMPLE_PACKAGE_NAME +JSSHELL_NAME := $(MOZ_SIMPLE_PACKAGE_NAME).jsshell.zip +else +JSSHELL_NAME = jsshell-$(MOZ_PKG_PLATFORM).zip +endif +PKG_JSSHELL = $(DIST)/$(JSSHELL_NAME) + +endif # PACKAGE_NAME_MK_INCLUDED diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk new file mode 100644 index 0000000000..aeba614e40 --- /dev/null +++ b/toolkit/mozapps/installer/packager.mk @@ -0,0 +1,236 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include $(MOZILLA_DIR)/toolkit/mozapps/installer/package-name.mk +include $(MOZILLA_DIR)/toolkit/mozapps/installer/upload-files.mk + +# This is how we create the binary packages we release to the public. + +# browser/locales/Makefile uses this makefile for its variable defs, but +# doesn't want the libs:: rule. +ifndef PACKAGER_NO_LIBS +libs:: make-package +endif + +ifdef MOZ_AUTOMATION +# This allows `RUN_{FIND_DUPES,MOZHARNESS_ZIP}=1 ./mach package` to test locally. +RUN_FIND_DUPES ?= $(MOZ_AUTOMATION) +RUN_MOZHARNESS_ZIP ?= $(MOZ_AUTOMATION) +endif + +export USE_ELF_HACK + +stage-package: multilocale.txt locale-manifest.in $(MOZ_PKG_MANIFEST) $(MOZ_PKG_MANIFEST_DEPS) + NO_PKG_FILES="$(NO_PKG_FILES)" \ + $(PYTHON3) $(MOZILLA_DIR)/toolkit/mozapps/installer/packager.py $(DEFINES) $(ACDEFINES) \ + --format $(MOZ_PACKAGER_FORMAT) \ + $(addprefix --removals ,$(MOZ_PKG_REMOVALS)) \ + $(if $(filter-out 0,$(MOZ_PKG_FATAL_WARNINGS)),,--ignore-errors) \ + $(if $(MOZ_AUTOMATION),,--ignore-broken-symlinks) \ + $(if $(MOZ_PACKAGER_MINIFY),--minify) \ + $(if $(MOZ_PACKAGER_MINIFY_JS),--minify-js \ + $(addprefix --js-binary ,$(JS_BINARY)) \ + ) \ + $(addprefix --jarlog ,$(wildcard $(JARLOG_FILE_AB_CD))) \ + $(addprefix --compress ,$(JAR_COMPRESSION)) \ + $(MOZ_PKG_MANIFEST) '$(DIST)' '$(DIST)'/$(MOZ_PKG_DIR)$(if $(MOZ_PKG_MANIFEST),,$(_BINPATH:%=/%)) \ + $(if $(filter omni,$(MOZ_PACKAGER_FORMAT)),$(if $(NON_OMNIJAR_FILES),--non-resource $(NON_OMNIJAR_FILES))) +ifdef RUN_FIND_DUPES + $(PYTHON3) $(MOZILLA_DIR)/toolkit/mozapps/installer/find-dupes.py $(DEFINES) $(ACDEFINES) $(MOZ_PKG_DUPEFLAGS) $(DIST)/$(MOZ_PKG_DIR) +endif # RUN_FIND_DUPES +ifndef MOZ_IS_COMM_TOPDIR +ifdef RUN_MOZHARNESS_ZIP + # Package mozharness + $(call py_action,test_archive $(MOZHARNESS_PACKAGE), \ + mozharness \ + $(ABS_DIST)/$(PKG_PATH)$(MOZHARNESS_PACKAGE)) +endif # RUN_MOZHARNESS_ZIP +endif # MOZ_IS_COMM_TOPDIR +ifdef MOZ_PACKAGE_JSSHELL + # Package JavaScript Shell + @echo 'Packaging JavaScript Shell...' + $(RM) $(PKG_JSSHELL) + $(MAKE_JSSHELL) +endif # MOZ_PACKAGE_JSSHELL +ifdef MOZ_AUTOMATION +ifdef MOZ_ARTIFACT_BUILD_SYMBOLS + @echo 'Packaging existing crashreporter symbols from artifact build...' + $(NSINSTALL) -D $(DIST)/$(PKG_PATH) + cd $(DIST)/crashreporter-symbols && \ + zip -r5D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym' -i '*.txt' +ifeq ($(MOZ_ARTIFACT_BUILD_SYMBOLS),full) + $(call py_action,symbols_archive $(SYMBOL_FULL_ARCHIVE_BASENAME).tar.zst,'$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).tar.zst' \ + $(abspath $(DIST)/crashreporter-symbols) \ + --full-archive) +endif +endif # MOZ_ARTIFACT_BUILD_SYMBOLS +endif # MOZ_AUTOMATION +ifdef MOZ_CODE_COVERAGE + @echo 'Generating chrome-map for coverage data...' + $(PYTHON3) $(topsrcdir)/mach build-backend -b ChromeMap + @echo 'Packaging code coverage data...' + $(RM) $(CODE_COVERAGE_ARCHIVE_BASENAME).zip + $(PYTHON3) -mmozbuild.codecoverage.packager \ + --output-file='$(DIST)/$(PKG_PATH)$(CODE_COVERAGE_ARCHIVE_BASENAME).zip' +endif +ifdef ENABLE_MOZSEARCH_PLUGIN + @echo 'Generating mozsearch index tarball...' + $(RM) $(MOZSEARCH_ARCHIVE_BASENAME).zip + cd $(topobjdir)/mozsearch_index && \ + zip -r5D '$(ABS_DIST)/$(PKG_PATH)$(MOZSEARCH_ARCHIVE_BASENAME).zip' . + @echo 'Generating mozsearch distinclude map...' + cd $(topobjdir)/ && cp _build_manifests/install/dist_include '$(ABS_DIST)/$(PKG_PATH)$(MOZSEARCH_INCLUDEMAP_BASENAME).map' + @echo 'Generating mozsearch scip index...' + $(RM) $(MOZSEARCH_SCIP_INDEX_BASENAME).zip + cp $(topsrcdir)/.cargo/config.in $(topsrcdir)/.cargo/config + cd $(topsrcdir)/ && \ + CARGO=$(MOZ_FETCHES_DIR)/rustc/bin/cargo \ + RUSTC=$(MOZ_FETCHES_DIR)/rustc/bin/rustc \ + $(MOZ_FETCHES_DIR)/rustc/bin/rust-analyzer scip . && \ + zip -r5D '$(ABS_DIST)/$(PKG_PATH)$(MOZSEARCH_SCIP_INDEX_BASENAME).zip' \ + index.scip + rm $(topsrcdir)/.cargo/config +ifeq ($(MOZ_BUILD_APP),mobile/android) + @echo 'Generating mozsearch java/kotlin semanticdb tarball...' + $(RM) $(MOZSEARCH_JAVA_INDEX_BASENAME).zip + cd $(topsrcdir)/ && \ + $(PYTHON3) $(topsrcdir)/mach android compile-all && \ + cd $(topobjdir)/mozsearch_java_index && \ + zip -r5D '$(ABS_DIST)/$(PKG_PATH)$(MOZSEARCH_JAVA_INDEX_BASENAME).zip' . +endif # MOZ_BUILD_APP == mobile/android +endif +ifeq (Darwin, $(OS_ARCH)) +ifneq (,$(MOZ_ASAN)$(LIBFUZZER)$(MOZ_UBSAN)) + @echo "Rewriting sanitizer runtime dylib paths for all binaries in $(DIST)/$(MOZ_PKG_DIR)/$(_BINPATH) ..." + $(PYTHON3) $(MOZILLA_DIR)/build/unix/rewrite_sanitizer_dylib.py '$(DIST)/$(MOZ_PKG_DIR)/$(_BINPATH)' +endif # MOZ_ASAN || LIBFUZZER || MOZ_UBSAN +endif # Darwin +ifndef MOZ_ARTIFACT_BUILDS + @echo 'Generating XPT artifacts archive ($(XPT_ARTIFACTS_ARCHIVE_BASENAME).zip)' + $(call py_action,zip $(XPT_ARTIFACTS_ARCHIVE_BASENAME).zip,-C $(topobjdir)/config/makefiles/xpidl '$(ABS_DIST)/$(PKG_PATH)$(XPT_ARTIFACTS_ARCHIVE_BASENAME).zip' '*.xpt') +else + @echo 'Packaging existing XPT artifacts from artifact build into archive ($(XPT_ARTIFACTS_ARCHIVE_BASENAME).zip)' + $(call py_action,zip $(XPT_ARTIFACTS_ARCHIVE_BASENAME).zip,-C $(ABS_DIST)/xpt_artifacts '$(ABS_DIST)/$(PKG_PATH)$(XPT_ARTIFACTS_ARCHIVE_BASENAME).zip' '*.xpt') +endif # MOZ_ARTIFACT_BUILDS + +prepare-package: stage-package + +make-package-internal: prepare-package make-sourcestamp-file + @echo 'Compressing...' + $(call MAKE_PACKAGE,$(DIST)) + +make-package: FORCE + $(MAKE) make-package-internal +ifeq (WINNT,$(OS_ARCH)) +ifeq ($(MOZ_PKG_FORMAT),ZIP) + $(MAKE) -C windows ZIP_IN='$(ABS_DIST)/$(PACKAGE)' installer +endif +endif +ifdef MOZ_AUTOMATION + cp $(DEPTH)/mozinfo.json $(MOZ_MOZINFO_FILE) + $(PYTHON3) $(MOZILLA_DIR)/toolkit/mozapps/installer/informulate.py \ + $(MOZ_BUILDINFO_FILE) $(MOZ_BUILDHUB_JSON) $(MOZ_BUILDID_INFO_TXT_FILE) \ + $(MOZ_PKG_PLATFORM) \ + $(if $(or $(filter-out mobile/android,$(MOZ_BUILD_APP)),$(MOZ_ANDROID_WITH_FENNEC)), \ + --package=$(DIST)/$(PACKAGE) --installer=$(INSTALLER_PACKAGE), \ + --no-download \ + ) +endif + $(TOUCH) $@ + +GARBAGE += make-package + +make-sourcestamp-file:: + $(NSINSTALL) -D $(DIST)/$(PKG_PATH) + @awk '$$2 == "MOZ_BUILDID" {print $$3}' $(DEPTH)/buildid.h > $(MOZ_SOURCESTAMP_FILE) +ifdef MOZ_INCLUDE_SOURCE_INFO + @awk '$$2 == "MOZ_SOURCE_URL" {print $$3}' $(DEPTH)/source-repo.h >> $(MOZ_SOURCESTAMP_FILE) +endif + +# The install target will install the application to prefix/lib/appname-version +install:: prepare-package +ifneq (,$(filter WINNT Darwin,$(OS_TARGET))) + $(error "make install" is not supported on this platform. Use "make package" instead.) +endif + $(NSINSTALL) -D $(DESTDIR)$(installdir) + (cd $(DIST)/$(MOZ_PKG_DIR) && $(TAR) --exclude=precomplete $(TAR_CREATE_FLAGS) - .) | \ + (cd $(DESTDIR)$(installdir) && tar -xf -) + $(NSINSTALL) -D $(DESTDIR)$(bindir) + $(RM) -f $(DESTDIR)$(bindir)/$(MOZ_APP_NAME) + ln -s $(installdir)/$(MOZ_APP_NAME) $(DESTDIR)$(bindir) + +upload: + $(PYTHON3) -u $(MOZILLA_DIR)/build/upload.py --base-path $(DIST) $(UPLOAD_FILES) + mkdir -p `dirname $(CHECKSUM_FILE)` + @$(PYTHON3) $(MOZILLA_DIR)/build/checksums.py \ + -o $(CHECKSUM_FILE) \ + $(CHECKSUM_ALGORITHM_PARAM) \ + $(UPLOAD_PATH) + @echo 'CHECKSUM FILE START' + @cat $(CHECKSUM_FILE) + @echo 'CHECKSUM FILE END' + $(PYTHON3) -u $(MOZILLA_DIR)/build/upload.py --base-path $(DIST) $(CHECKSUM_FILES) + +# source-package creates a source tarball from the files in MOZ_PKG_SRCDIR, +# which is either set to a clean checkout or defaults to $topsrcdir +source-package: + @echo 'Generate the sourcestamp file' + # Make sure to have repository information available and then generate the + # sourcestamp file. + $(MAKE) -C $(DEPTH) 'source-repo.h' 'buildid.h' + $(MAKE) make-sourcestamp-file + @echo 'Packaging source tarball...' + # We want to include the sourcestamp file in the source tarball, so copy it + # in the root source directory. This is useful to enable telemetry submissions + # from builds made from the source package with the correct revision information. + # Don't bother removing it as this is only used by automation. + @cp $(MOZ_SOURCESTAMP_FILE) '$(MOZ_PKG_SRCDIR)/sourcestamp.txt' + $(MKDIR) -p $(DIST)/$(PKG_SRCPACK_PATH) + (cd $(MOZ_PKG_SRCDIR) && $(CREATE_SOURCE_TAR) - ./ ) | xz -9e > $(SOURCE_TAR) + +hg-bundle: + $(MKDIR) -p $(DIST)/$(PKG_SRCPACK_PATH) + $(CREATE_HG_BUNDLE_CMD) + +source-upload: + $(MAKE) upload UPLOAD_FILES='$(SOURCE_UPLOAD_FILES)' CHECKSUM_FILE='$(SOURCE_CHECKSUM_FILE)' + + +ALL_LOCALES = $(if $(filter en-US,$(LOCALES)),$(LOCALES),$(LOCALES) en-US) + +# Firefox uses @RESPATH@. +# Fennec uses @BINPATH@ and doesn't have the @RESPATH@ variable defined. +ifeq ($(MOZ_BUILD_APP),mobile/android) +BASE_PATH:=@BINPATH@ +MULTILOCALE_DIR = $(DIST)/$(BINPATH)/res +else +BASE_PATH:=@RESPATH@ +MULTILOCALE_DIR = $(DIST)/$(RESPATH)/res +endif + +# This version of the target uses MOZ_CHROME_MULTILOCALE to build multilocale.txt +# and places it in dist/bin/res - it should be used when packaging a build. +multilocale.txt: LOCALES?=$(MOZ_CHROME_MULTILOCALE) +multilocale.txt: + $(call py_action,file_generate $@,$(MOZILLA_DIR)/toolkit/locales/gen_multilocale.py main '$(MULTILOCALE_DIR)/multilocale.txt' $(MDDEPDIR)/multilocale.txt.pp '$(MULTILOCALE_DIR)/multilocale.txt' $(ALL_LOCALES)) + +# This version of the target uses AB_CD to build multilocale.txt and places it +# in the $(XPI_NAME)/res dir - it should be used when repackaging a build. +multilocale.txt-%: LOCALES?=$(AB_CD) +multilocale.txt-%: MULTILOCALE_DIR=$(DIST)/xpi-stage/$(XPI_NAME)/res +multilocale.txt-%: + $(call py_action,file_generate multilocale.txt,$(MOZILLA_DIR)/toolkit/locales/gen_multilocale.py main '$(MULTILOCALE_DIR)/multilocale.txt' $(MDDEPDIR)/multilocale.txt.pp '$(MULTILOCALE_DIR)/multilocale.txt' $(ALL_LOCALES)) + +locale-manifest.in: LOCALES?=$(MOZ_CHROME_MULTILOCALE) +locale-manifest.in: $(GLOBAL_DEPS) FORCE + printf '\n[multilocale]\n' > $@ + printf '$(BASE_PATH)/res/multilocale.txt\n' >> $@ + for LOCALE in $(ALL_LOCALES) ;\ + do \ + for ENTRY in $(MOZ_CHROME_LOCALE_ENTRIES) ;\ + do \ + printf "$$ENTRY""$$LOCALE"'@JAREXT@\n' >> $@; \ + printf "$$ENTRY""$$LOCALE"'.manifest\n' >> $@; \ + done \ + done diff --git a/toolkit/mozapps/installer/packager.py b/toolkit/mozapps/installer/packager.py new file mode 100644 index 0000000000..29364045dd --- /dev/null +++ b/toolkit/mozapps/installer/packager.py @@ -0,0 +1,295 @@ +# 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/. + +from mozpack.packager.formats import ( + FlatFormatter, + JarFormatter, + OmniJarFormatter, +) +from mozpack.packager import ( + preprocess_manifest, + preprocess, + Component, + SimpleManifestSink, +) +from mozpack.files import ( + GeneratedFile, + FileFinder, + File, +) +from mozpack.copier import ( + FileCopier, + Jarrer, +) +from mozpack.errors import errors +from mozpack.files import ExecutableFile +import mozpack.path as mozpath +import buildconfig +from argparse import ArgumentParser +from collections import OrderedDict +from createprecomplete import generate_precomplete +import os +import plistlib +from io import StringIO +import subprocess + + +class PackagerFileFinder(FileFinder): + def get(self, path): + f = super(PackagerFileFinder, self).get(path) + # Normalize Info.plist files, and remove the MozillaDeveloper*Path + # entries which are only needed on unpackaged builds. + if mozpath.basename(path) == "Info.plist": + info = plistlib.load(f.open(), dict_type=OrderedDict) + info.pop("MozillaDeveloperObjPath", None) + info.pop("MozillaDeveloperRepoPath", None) + return GeneratedFile(plistlib.dumps(info, sort_keys=False)) + return f + + +class RemovedFiles(GeneratedFile): + """ + File class for removed-files. Is used as a preprocessor parser. + """ + + def __init__(self, copier): + self.copier = copier + GeneratedFile.__init__(self, "") + + def handle_line(self, f): + f = f.strip() + if not f: + return + if self.copier.contains(f): + errors.error("Removal of packaged file(s): %s" % f) + self.content = f + "\n" + + +def split_define(define): + """ + Give a VAR[=VAL] string, returns a (VAR, VAL) tuple, where VAL defaults to + 1. Numeric VALs are returned as ints. + """ + if "=" in define: + name, value = define.split("=", 1) + try: + value = int(value) + except ValueError: + pass + return (name, value) + return (define, 1) + + +class NoPkgFilesRemover(object): + """ + Formatter wrapper to handle NO_PKG_FILES. + """ + + def __init__(self, formatter, has_manifest): + assert "NO_PKG_FILES" in os.environ + self._formatter = formatter + self._files = os.environ["NO_PKG_FILES"].split() + if has_manifest: + self._error = errors.error + self._msg = "NO_PKG_FILES contains file listed in manifest: %s" + else: + self._error = errors.warn + self._msg = "Skipping %s" + + def add_base(self, base, *args): + self._formatter.add_base(base, *args) + + def add(self, path, content): + if not any(mozpath.match(path, spec) for spec in self._files): + self._formatter.add(path, content) + else: + self._error(self._msg % path) + + def add_manifest(self, entry): + self._formatter.add_manifest(entry) + + def contains(self, path): + return self._formatter.contains(path) + + +def main(): + parser = ArgumentParser() + parser.add_argument( + "-D", + dest="defines", + action="append", + metavar="VAR[=VAL]", + help="Define a variable", + ) + parser.add_argument( + "--format", + default="omni", + help="Choose the chrome format for packaging " + + "(omni, jar or flat ; default: %(default)s)", + ) + parser.add_argument("--removals", default=None, help="removed-files source file") + parser.add_argument( + "--ignore-errors", + action="store_true", + default=False, + help="Transform errors into warnings.", + ) + parser.add_argument( + "--ignore-broken-symlinks", + action="store_true", + default=False, + help="Do not fail when processing broken symlinks.", + ) + parser.add_argument( + "--minify", + action="store_true", + default=False, + help="Make some files more compact while packaging", + ) + parser.add_argument( + "--minify-js", + action="store_true", + help="Minify JavaScript files while packaging.", + ) + parser.add_argument( + "--js-binary", + help="Path to js binary. This is used to verify " + "minified JavaScript. If this is not defined, " + "minification verification will not be performed.", + ) + parser.add_argument( + "--jarlog", default="", help="File containing jar " + "access logs" + ) + parser.add_argument( + "--compress", + choices=("none", "deflate"), + default="deflate", + help="Use given jar compression (default: deflate)", + ) + parser.add_argument("manifest", default=None, nargs="?", help="Manifest file name") + parser.add_argument("source", help="Source directory") + parser.add_argument("destination", help="Destination directory") + parser.add_argument( + "--non-resource", + nargs="+", + metavar="PATTERN", + default=[], + help="Extra files not to be considered as resources", + ) + args = parser.parse_args() + + defines = dict(buildconfig.defines["ALLDEFINES"]) + if args.ignore_errors: + errors.ignore_errors() + + if args.defines: + for name, value in [split_define(d) for d in args.defines]: + defines[name] = value + + compress = { + "none": False, + "deflate": True, + }[args.compress] + + copier = FileCopier() + if args.format == "flat": + formatter = FlatFormatter(copier) + elif args.format == "jar": + formatter = JarFormatter(copier, compress=compress) + elif args.format == "omni": + formatter = OmniJarFormatter( + copier, + buildconfig.substs["OMNIJAR_NAME"], + compress=compress, + non_resources=args.non_resource, + ) + else: + errors.fatal("Unknown format: %s" % args.format) + + # Adjust defines according to the requested format. + if isinstance(formatter, OmniJarFormatter): + defines["MOZ_OMNIJAR"] = 1 + elif "MOZ_OMNIJAR" in defines: + del defines["MOZ_OMNIJAR"] + + respath = "" + if "RESPATH" in defines: + respath = SimpleManifestSink.normalize_path(defines["RESPATH"]) + while respath.startswith("/"): + respath = respath[1:] + + with errors.accumulate(): + finder_args = dict( + minify=args.minify, + minify_js=args.minify_js, + ignore_broken_symlinks=args.ignore_broken_symlinks, + ) + if args.js_binary: + finder_args["minify_js_verify_command"] = [ + args.js_binary, + os.path.join( + os.path.abspath(os.path.dirname(__file__)), "js-compare-ast.js" + ), + ] + finder = PackagerFileFinder(args.source, find_executables=True, **finder_args) + if "NO_PKG_FILES" in os.environ: + sinkformatter = NoPkgFilesRemover(formatter, args.manifest is not None) + else: + sinkformatter = formatter + sink = SimpleManifestSink(finder, sinkformatter) + if args.manifest: + preprocess_manifest(sink, args.manifest, defines) + else: + sink.add(Component(""), "bin/*") + sink.close(args.manifest is not None) + + if args.removals: + removals_in = StringIO(open(args.removals).read()) + removals_in.name = args.removals + removals = RemovedFiles(copier) + preprocess(removals_in, removals, defines) + copier.add(mozpath.join(respath, "removed-files"), removals) + + # If a pdb file is present and we were instructed to copy it, include it. + # Run on all OSes to capture MinGW builds + if buildconfig.substs.get("MOZ_COPY_PDBS"): + # We want to mutate the copier while we're iterating through it, so copy + # the items to a list first. + copier_items = [(p, f) for p, f in copier] + for p, f in copier_items: + if isinstance(f, ExecutableFile): + pdbname = os.path.splitext(f.inputs()[0])[0] + ".pdb" + if os.path.exists(pdbname): + copier.add(os.path.basename(pdbname), File(pdbname)) + + # Setup preloading + if args.jarlog: + if not os.path.exists(args.jarlog): + raise Exception("Cannot find jar log: %s" % args.jarlog) + omnijars = [] + if isinstance(formatter, OmniJarFormatter): + omnijars = [ + mozpath.join(base, buildconfig.substs["OMNIJAR_NAME"]) + for base in sink.packager.get_bases(addons=False) + ] + + from mozpack.mozjar import JarLog + + log = JarLog(args.jarlog) + for p, f in copier: + if not isinstance(f, Jarrer): + continue + if respath: + p = mozpath.relpath(p, respath) + if p in log: + f.preload(log[p]) + elif p in omnijars: + raise Exception("No jar log data for %s" % p) + + copier.copy(args.destination) + generate_precomplete(os.path.normpath(os.path.join(args.destination, respath))) + + +if __name__ == "__main__": + main() diff --git a/toolkit/mozapps/installer/strip.py b/toolkit/mozapps/installer/strip.py new file mode 100644 index 0000000000..ef81b1a2b6 --- /dev/null +++ b/toolkit/mozapps/installer/strip.py @@ -0,0 +1,25 @@ +# 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/. + +""" +Strip all files that can be stripped in the given directory. +""" + +import sys +from mozpack.files import FileFinder +from mozpack.copier import FileCopier + + +def strip(dir): + copier = FileCopier() + # The FileFinder will give use ExecutableFile instances for files + # that can be stripped, and copying ExecutableFiles defaults to + # stripping them when buildconfig.substs['PKG_STRIP'] is set. + for p, f in FileFinder(dir, find_executables=True): + copier.add(p, f) + copier.copy(dir) + + +if __name__ == "__main__": + strip(sys.argv[1]) diff --git a/toolkit/mozapps/installer/unify.py b/toolkit/mozapps/installer/unify.py new file mode 100644 index 0000000000..8b93a8dcea --- /dev/null +++ b/toolkit/mozapps/installer/unify.py @@ -0,0 +1,77 @@ +# 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/. + +import argparse +import os +import buildconfig +from mozpack.packager.formats import ( + FlatFormatter, + JarFormatter, + OmniJarFormatter, +) +from mozpack.packager import SimplePackager +from mozpack.copier import ( + FileCopier, + Jarrer, +) +from mozpack.errors import errors +from mozpack.files import FileFinder +from mozpack.mozjar import JAR_DEFLATED +from mozpack.packager.unpack import UnpackFinder +from mozpack.unify import UnifiedBuildFinder + + +def main(): + parser = argparse.ArgumentParser( + description="Merge two builds of a Gecko-based application into a Universal build" + ) + parser.add_argument("app1", help="Directory containing the application") + parser.add_argument("app2", help="Directory containing the application to merge") + parser.add_argument( + "--non-resource", + nargs="+", + metavar="PATTERN", + default=[], + help="Extra files not to be considered as resources", + ) + + options = parser.parse_args() + + buildconfig.substs["OS_ARCH"] = "Darwin" + buildconfig.substs["LIPO"] = os.environ.get("LIPO") + + app1_finder = UnpackFinder(FileFinder(options.app1, find_executables=True)) + app2_finder = UnpackFinder(FileFinder(options.app2, find_executables=True)) + app_finder = UnifiedBuildFinder(app1_finder, app2_finder) + + copier = FileCopier() + compress = min(app1_finder.compressed, JAR_DEFLATED) + if app1_finder.kind == "flat": + formatter = FlatFormatter(copier) + elif app1_finder.kind == "jar": + formatter = JarFormatter(copier, compress=compress) + elif app1_finder.kind == "omni": + formatter = OmniJarFormatter( + copier, + app1_finder.omnijar, + compress=compress, + non_resources=options.non_resource, + ) + + with errors.accumulate(): + packager = SimplePackager(formatter) + for p, f in app_finder: + packager.add(p, f) + packager.close() + + # Transplant jar preloading information. + for path, log in app1_finder.jarlogs.items(): + assert isinstance(copier[path], Jarrer) + copier[path].preload(log) + + copier.copy(options.app1, skip_if_older=False) + + +if __name__ == "__main__": + main() diff --git a/toolkit/mozapps/installer/unpack.py b/toolkit/mozapps/installer/unpack.py new file mode 100644 index 0000000000..34edb59e38 --- /dev/null +++ b/toolkit/mozapps/installer/unpack.py @@ -0,0 +1,25 @@ +# 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/. + +import argparse +import sys +import os +from mozpack.packager.unpack import unpack +import buildconfig + + +def main(): + parser = argparse.ArgumentParser(description="Unpack a Gecko-based application") + parser.add_argument("directory", help="Location of the application") + parser.add_argument("--omnijar", help="Name of the omnijar") + + options = parser.parse_args(sys.argv[1:]) + + buildconfig.substs["USE_ELF_HACK"] = False + buildconfig.substs["PKG_STRIP"] = False + unpack(options.directory, options.omnijar) + + +if __name__ == "__main__": + main() diff --git a/toolkit/mozapps/installer/upload-files.mk b/toolkit/mozapps/installer/upload-files.mk new file mode 100644 index 0000000000..6e1283e238 --- /dev/null +++ b/toolkit/mozapps/installer/upload-files.mk @@ -0,0 +1,434 @@ +# 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/. + +ifndef MOZ_PKG_FORMAT + ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) + MOZ_PKG_FORMAT = DMG + else + ifeq (WINNT,$(OS_ARCH)) + MOZ_PKG_FORMAT = ZIP + else + ifeq (SunOS,$(OS_ARCH)) + MOZ_PKG_FORMAT = BZ2 + else + ifeq (gtk,$(MOZ_WIDGET_TOOLKIT)) + MOZ_PKG_FORMAT = BZ2 + else + ifeq (android,$(MOZ_WIDGET_TOOLKIT)) + MOZ_PKG_FORMAT = APK + else + MOZ_PKG_FORMAT = TGZ + endif + endif + endif + endif + endif +endif # MOZ_PKG_FORMAT + +ifeq ($(OS_ARCH),WINNT) +INSTALLER_DIR = windows +endif + +ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) +ifndef _APPNAME +_APPNAME = $(MOZ_MACBUNDLE_NAME) +endif +ifndef _BINPATH +_BINPATH = $(_APPNAME)/Contents/MacOS +endif # _BINPATH +ifndef _RESPATH +# Resource path for the precomplete file +_RESPATH = $(_APPNAME)/Contents/Resources +endif +endif + +PACKAGE_BASE_DIR = $(ABS_DIST) +PACKAGE = $(PKG_PATH)$(PKG_BASENAME)$(PKG_SUFFIX) + +# JavaScript Shell packaging +JSSHELL_BINS = \ + js$(BIN_SUFFIX) \ + $(DLL_PREFIX)mozglue$(DLL_SUFFIX) \ + $(NULL) + +ifndef MOZ_SYSTEM_NSPR + ifdef MOZ_FOLD_LIBS + JSSHELL_BINS += $(DLL_PREFIX)nss3$(DLL_SUFFIX) + else + JSSHELL_BINS += \ + $(DLL_PREFIX)nspr4$(DLL_SUFFIX) \ + $(DLL_PREFIX)plds4$(DLL_SUFFIX) \ + $(DLL_PREFIX)plc4$(DLL_SUFFIX) \ + $(NULL) + endif # MOZ_FOLD_LIBS +endif # MOZ_SYSTEM_NSPR + +ifdef MSVC_C_RUNTIME_DLL + JSSHELL_BINS += $(MSVC_C_RUNTIME_DLL) +endif +ifdef MSVC_C_RUNTIME_1_DLL + JSSHELL_BINS += $(MSVC_C_RUNTIME_1_DLL) +endif +ifdef MSVC_CXX_RUNTIME_DLL + JSSHELL_BINS += $(MSVC_CXX_RUNTIME_DLL) +endif + +ifdef LLVM_SYMBOLIZER + JSSHELL_BINS += $(notdir $(LLVM_SYMBOLIZER)) +endif +ifdef MOZ_CLANG_RT_ASAN_LIB_PATH + JSSHELL_BINS += $(notdir $(MOZ_CLANG_RT_ASAN_LIB_PATH)) +endif + +ifdef FUZZING_INTERFACES + JSSHELL_BINS += fuzz-tests$(BIN_SUFFIX) +endif + +MAKE_JSSHELL = $(call py_action,zip $(JSSHELL_NAME),-C $(DIST)/bin --strip $(abspath $(PKG_JSSHELL)) $(JSSHELL_BINS)) + +ifneq (,$(PGO_JARLOG_PATH)) + # The backslash subst is to work around an issue with our version of mozmake, + # where backslashes get slurped in command-line arguments if a command is run + # with a double-quote character. The command to packager.py happens to be one + # of these commands, where double-quotes appear in certain ACDEFINES values. + # This turns a jarlog path like "Z:\task..." into "Z:task", which fails. + # Switching the backslashes for forward slashes works around the issue. + JARLOG_FILE_AB_CD = $(subst \,/,$(PGO_JARLOG_PATH)) +else + JARLOG_FILE_AB_CD = $(topobjdir)/jarlog/$(AB_CD).log +endif + +TAR_CREATE_FLAGS := --exclude=.mkdir.done $(TAR_CREATE_FLAGS) +CREATE_FINAL_TAR = $(TAR) -c --owner=0 --group=0 --numeric-owner \ + --mode=go-w --exclude=.mkdir.done -f +UNPACK_TAR = tar -xf- + +ifeq ($(MOZ_PKG_FORMAT),TAR) + PKG_SUFFIX = .tar + INNER_MAKE_PACKAGE = cd $(1) && $(CREATE_FINAL_TAR) - $(MOZ_PKG_DIR) > $(PACKAGE) + INNER_UNMAKE_PACKAGE = cd $(1) && $(UNPACK_TAR) < $(UNPACKAGE) +endif + +ifeq ($(MOZ_PKG_FORMAT),TGZ) + PKG_SUFFIX = .tar.gz + INNER_MAKE_PACKAGE = cd $(1) && $(CREATE_FINAL_TAR) - $(MOZ_PKG_DIR) | gzip -vf9 > $(PACKAGE) + INNER_UNMAKE_PACKAGE = cd $(1) && gunzip -c $(UNPACKAGE) | $(UNPACK_TAR) +endif + +ifeq ($(MOZ_PKG_FORMAT),BZ2) + PKG_SUFFIX = .tar.bz2 + ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) + INNER_MAKE_PACKAGE = cd $(1) && $(CREATE_FINAL_TAR) - -C $(MOZ_PKG_DIR) $(_APPNAME) | bzip2 -vf > $(PACKAGE) + else + INNER_MAKE_PACKAGE = cd $(1) && $(CREATE_FINAL_TAR) - $(MOZ_PKG_DIR) | bzip2 -vf > $(PACKAGE) + endif + INNER_UNMAKE_PACKAGE = cd $(1) && bunzip2 -c $(UNPACKAGE) | $(UNPACK_TAR) +endif + +ifeq ($(MOZ_PKG_FORMAT),ZIP) + PKG_SUFFIX = .zip + INNER_MAKE_PACKAGE = $(call py_action,zip,'$(PACKAGE)' '$(MOZ_PKG_DIR)' -x '**/.mkdir.done',$(1)) + INNER_UNMAKE_PACKAGE = $(call py_action,make_unzip,$(UNPACKAGE),$(1)) +endif + +#Create an RPM file +ifeq ($(MOZ_PKG_FORMAT),RPM) + PKG_SUFFIX = .rpm + MOZ_NUMERIC_APP_VERSION = $(shell echo $(MOZ_PKG_VERSION) | sed 's/[^0-9.].*//' ) + MOZ_RPM_RELEASE = $(shell echo $(MOZ_PKG_VERSION) | sed 's/[0-9.]*//' ) + + RPMBUILD_TOPDIR=$(ABS_DIST)/rpmbuild + RPMBUILD_RPMDIR=$(ABS_DIST) + RPMBUILD_SRPMDIR=$(ABS_DIST) + RPMBUILD_SOURCEDIR=$(RPMBUILD_TOPDIR)/SOURCES + RPMBUILD_SPECDIR=$(topsrcdir)/toolkit/mozapps/installer/linux/rpm + RPMBUILD_BUILDDIR=$(ABS_DIST)/.. + + SPEC_FILE = $(RPMBUILD_SPECDIR)/mozilla.spec + RPM_INCIDENTALS=$(topsrcdir)/toolkit/mozapps/installer/linux/rpm + + RPM_CMD = \ + echo Creating RPM && \ + $(PYTHON3) -m mozbuild.action.preprocessor \ + -DMOZ_APP_NAME=$(MOZ_APP_NAME) \ + -DMOZ_APP_DISPLAYNAME='$(MOZ_APP_DISPLAYNAME)' \ + -DMOZ_APP_REMOTINGNAME='$(MOZ_APP_REMOTINGNAME)' \ + $(RPM_INCIDENTALS)/mozilla.desktop \ + -o $(RPMBUILD_SOURCEDIR)/$(MOZ_APP_NAME).desktop && \ + rm -rf $(ABS_DIST)/$(TARGET_RAW_CPU) && \ + $(RPMBUILD) -bb \ + $(SPEC_FILE) \ + --target $(TARGET_RAW_CPU) \ + --buildroot $(RPMBUILD_TOPDIR)/BUILDROOT \ + --define 'moz_app_name $(MOZ_APP_NAME)' \ + --define 'moz_app_displayname $(MOZ_APP_DISPLAYNAME)' \ + --define 'moz_app_version $(MOZ_APP_VERSION)' \ + --define 'moz_numeric_app_version $(MOZ_NUMERIC_APP_VERSION)' \ + --define 'moz_rpm_release $(MOZ_RPM_RELEASE)' \ + --define 'buildid $(BUILDID)' \ + --define 'moz_source_repo $(shell awk '$$2 == "MOZ_SOURCE_REPO" {print $$3}' $(DEPTH)/source-repo.h)' \ + --define 'moz_source_stamp $(shell awk '$$2 == "MOZ_SOURCE_STAMP" {print $$3}' $(DEPTH)/source-repo.h)' \ + --define 'moz_branding_directory $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)' \ + --define '_topdir $(RPMBUILD_TOPDIR)' \ + --define '_rpmdir $(RPMBUILD_RPMDIR)' \ + --define '_sourcedir $(RPMBUILD_SOURCEDIR)' \ + --define '_specdir $(RPMBUILD_SPECDIR)' \ + --define '_srcrpmdir $(RPMBUILD_SRPMDIR)' \ + --define '_builddir $(RPMBUILD_BUILDDIR)' \ + --define '_prefix $(prefix)' \ + --define '_libdir $(libdir)' \ + --define '_bindir $(bindir)' \ + --define '_datadir $(datadir)' \ + --define '_installdir $(installdir)' + + ifdef ENABLE_TESTS + RPM_CMD += \ + --define 'createtests yes' \ + --define '_testsinstalldir $(shell basename $(installdir))' + endif + + #For each of the main/tests rpms we want to make sure that + #if they exist that they are in objdir/dist/ and that they get + #uploaded and that they are beside the other build artifacts + MAIN_RPM= $(MOZ_APP_NAME)-$(MOZ_NUMERIC_APP_VERSION)-$(MOZ_RPM_RELEASE).$(BUILDID).$(TARGET_RAW_CPU)$(PKG_SUFFIX) + UPLOAD_EXTRA_FILES += $(MAIN_RPM) + RPM_CMD += && mv $(TARGET_RAW_CPU)/$(MAIN_RPM) $(ABS_DIST)/ + + ifdef ENABLE_TESTS + TESTS_RPM=$(MOZ_APP_NAME)-tests-$(MOZ_NUMERIC_APP_VERSION)-$(MOZ_RPM_RELEASE).$(BUILDID).$(TARGET_RAW_CPU)$(PKG_SUFFIX) + UPLOAD_EXTRA_FILES += $(TESTS_RPM) + RPM_CMD += && mv $(TARGET_RAW_CPU)/$(TESTS_RPM) $(ABS_DIST)/ + endif + + INNER_MAKE_PACKAGE = cd $(1) && $(RPM_CMD) + #Avoiding rpm repacks, going to try creating/uploading xpi in rpm files instead + INNER_UNMAKE_PACKAGE = $(error Try using rpm2cpio and cpio) + +endif #Create an RPM file + + +ifeq ($(MOZ_PKG_FORMAT),APK) +INNER_MAKE_PACKAGE = true +INNER_UNMAKE_PACKAGE = true +endif + +ifeq ($(MOZ_PKG_FORMAT),DMG) + PKG_SUFFIX = .dmg + + _ABS_MOZSRCDIR = $(shell cd $(MOZILLA_DIR) && pwd) + PKG_DMG_SOURCE = $(MOZ_PKG_DIR) + INNER_MAKE_PACKAGE = \ + $(call py_action,make_dmg, \ + $(if $(MOZ_PKG_MAC_DSSTORE),--dsstore '$(MOZ_PKG_MAC_DSSTORE)') \ + $(if $(MOZ_PKG_MAC_BACKGROUND),--background '$(MOZ_PKG_MAC_BACKGROUND)') \ + $(if $(MOZ_PKG_MAC_ICON),--icon '$(MOZ_PKG_MAC_ICON)') \ + --volume-name '$(MOZ_APP_DISPLAYNAME)' \ + '$(PKG_DMG_SOURCE)' '$(PACKAGE)', \ + $(1)) + INNER_UNMAKE_PACKAGE = \ + $(call py_action,unpack_dmg, \ + $(if $(MOZ_PKG_MAC_DSSTORE),--dsstore '$(MOZ_PKG_MAC_DSSTORE)') \ + $(if $(MOZ_PKG_MAC_BACKGROUND),--background '$(MOZ_PKG_MAC_BACKGROUND)') \ + $(if $(MOZ_PKG_MAC_ICON),--icon '$(MOZ_PKG_MAC_ICON)') \ + $(UNPACKAGE) $(MOZ_PKG_DIR), \ + $(1)) +endif + +MAKE_PACKAGE = $(INNER_MAKE_PACKAGE) + +NO_PKG_FILES += \ + core \ + bsdecho \ + js \ + js-config \ + jscpucfg \ + nsinstall \ + viewer \ + TestGtkEmbed \ + elf-dynstr-gc \ + mangle* \ + maptsv* \ + mfc* \ + msdump* \ + msmap* \ + nm2tsv* \ + nsinstall* \ + res/samples \ + res/throbber \ + shlibsign* \ + certutil* \ + pk12util* \ + BadCertAndPinningServer* \ + DelegatedCredentialsServer* \ + EncryptedClientHelloServer* \ + FaultyServer* \ + OCSPStaplingServer* \ + SanctionsTestServer* \ + GenerateOCSPResponse* \ + chrome/chrome.rdf \ + chrome/app-chrome.manifest \ + chrome/overlayinfo \ + components/compreg.dat \ + components/xpti.dat \ + content_unit_tests \ + necko_unit_tests \ + *.dSYM \ + $(NULL) + +# If a manifest has not been supplied, the following +# files should be excluded from the package too +ifndef MOZ_PKG_MANIFEST + NO_PKG_FILES += ssltunnel* +endif + +ifdef MOZ_DMD + NO_PKG_FILES += SmokeDMD +endif + +DEFINES += -DDLL_PREFIX=$(DLL_PREFIX) -DDLL_SUFFIX=$(DLL_SUFFIX) -DBIN_SUFFIX=$(BIN_SUFFIX) + +ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) + DEFINES += -DDIR_MACOS=Contents/MacOS/ -DDIR_RESOURCES=Contents/Resources/ +else + DEFINES += -DDIR_MACOS= -DDIR_RESOURCES= +endif + +ifdef MOZ_FOLD_LIBS + DEFINES += -DMOZ_FOLD_LIBS=1 +endif + +# The following target stages files into two directories: one directory for +# core files, and one for optional extensions based on the information in +# the MOZ_PKG_MANIFEST file. + +PKG_ARG = , '$(pkg)' + +ifndef MOZ_PACKAGER_FORMAT + MOZ_PACKAGER_FORMAT = $(error MOZ_PACKAGER_FORMAT is not set) +endif + +ifneq (android,$(MOZ_WIDGET_TOOLKIT)) + JAR_COMPRESSION ?= none +endif + +ifeq ($(OS_TARGET), WINNT) + INSTALLER_PACKAGE = $(DIST)/$(PKG_INST_PATH)$(PKG_INST_BASENAME).exe +endif + +# These are necessary because some of our packages/installers contain spaces +# in their filenames and GNU Make's $(wildcard) function doesn't properly +# deal with them. +empty := +space = $(empty) $(empty) +QUOTED_WILDCARD = $(if $(wildcard $(subst $(space),?,$(1))),'$(1)') +ESCAPE_SPACE = $(subst $(space),\$(space),$(1)) +ESCAPE_WILDCARD = $(subst $(space),?,$(1)) + +# This variable defines which OpenSSL algorithm to use to +# generate checksums for files that we upload +CHECKSUM_ALGORITHM_PARAM = -d sha512 -d md5 -d sha1 + +# This variable defines where the checksum file will be located +CHECKSUM_FILE = '$(DIST)/$(PKG_PATH)/$(CHECKSUMS_FILE_BASENAME).checksums' +CHECKSUM_FILES = $(CHECKSUM_FILE) + +# Upload MAR tools only if AB_CD is unset or en_US +ifeq (,$(AB_CD:en-US=)) + ifeq (WINNT,$(OS_TARGET)) + UPLOAD_EXTRA_FILES += host/bin/mar.exe + UPLOAD_EXTRA_FILES += host/bin/mbsdiff.exe + else + UPLOAD_EXTRA_FILES += host/bin/mar + UPLOAD_EXTRA_FILES += host/bin/mbsdiff + endif +endif + +UPLOAD_FILES= \ + $(call QUOTED_WILDCARD,$(DIST)/$(PACKAGE)) \ + $(call QUOTED_WILDCARD,$(INSTALLER_PACKAGE)) \ + $(call QUOTED_WILDCARD,$(DIST)/$(LANGPACK)) \ + $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(MOZHARNESS_PACKAGE)) \ + $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip) \ + $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(GENERATED_SOURCE_FILE_PACKAGE)) \ + $(call QUOTED_WILDCARD,$(MOZ_SOURCESTAMP_FILE)) \ + $(call QUOTED_WILDCARD,$(MOZ_BUILDINFO_FILE)) \ + $(call QUOTED_WILDCARD,$(MOZ_BUILDHUB_JSON)) \ + $(call QUOTED_WILDCARD,$(MOZ_BUILDID_INFO_TXT_FILE)) \ + $(call QUOTED_WILDCARD,$(MOZ_MOZINFO_FILE)) \ + $(call QUOTED_WILDCARD,$(MOZ_TEST_PACKAGES_FILE)) \ + $(call QUOTED_WILDCARD,$(PKG_JSSHELL)) \ + $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).tar.zst) \ + $(call QUOTED_WILDCARD,$(topobjdir)/$(MOZ_BUILD_APP)/installer/windows/instgen/setup.exe) \ + $(call QUOTED_WILDCARD,$(topobjdir)/$(MOZ_BUILD_APP)/installer/windows/instgen/setup-stub.exe) \ + $(call QUOTED_WILDCARD,$(topsrcdir)/toolchains.json) \ + $(call QUOTED_WILDCARD,$(topobjdir)/config.status) \ + $(if $(UPLOAD_EXTRA_FILES), $(foreach f, $(UPLOAD_EXTRA_FILES), $(wildcard $(DIST)/$(f)))) + +ifneq ($(filter-out en-US,$(AB_CD)),) + UPLOAD_FILES += \ + $(call QUOTED_WILDCARD,$(topobjdir)/$(MOZ_BUILD_APP)/installer/windows/l10ngen/setup.exe) \ + $(call QUOTED_WILDCARD,$(topobjdir)/$(MOZ_BUILD_APP)/installer/windows/l10ngen/setup-stub.exe) +endif + +ifdef MOZ_CODE_COVERAGE + UPLOAD_FILES += \ + $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(CODE_COVERAGE_ARCHIVE_BASENAME).zip) \ + $(call QUOTED_WILDCARD,$(topobjdir)/chrome-map.json) \ + $(NULL) +endif + + +ifdef ENABLE_MOZSEARCH_PLUGIN + UPLOAD_FILES += $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(MOZSEARCH_ARCHIVE_BASENAME).zip) + UPLOAD_FILES += $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(MOZSEARCH_SCIP_INDEX_BASENAME).zip) + UPLOAD_FILES += $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(MOZSEARCH_INCLUDEMAP_BASENAME).map) +ifeq ($(MOZ_BUILD_APP),mobile/android) + UPLOAD_FILES += $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(MOZSEARCH_JAVA_INDEX_BASENAME).zip) +endif +endif + +ifdef MOZ_STUB_INSTALLER + UPLOAD_FILES += $(call QUOTED_WILDCARD,$(DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe) +endif + +# Upload `.xpt` artifacts for use in artifact builds. +UPLOAD_FILES += $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(XPT_ARTIFACTS_ARCHIVE_BASENAME).zip) + +ifndef MOZ_PKG_SRCDIR + MOZ_PKG_SRCDIR = $(topsrcdir) +endif + +SRC_TAR_PREFIX = $(MOZ_APP_NAME)-$(MOZ_PKG_VERSION) +SRC_TAR_EXCLUDE_PATHS += \ + --exclude='.hg*' \ + --exclude='.git' \ + --exclude='.gitattributes' \ + --exclude='.gitkeep' \ + --exclude='.gitmodules' \ + --exclude='CVS' \ + --exclude='.cvs*' \ + --exclude='.mozconfig*' \ + --exclude='*.pyc' \ + --exclude='$(MOZILLA_DIR)/Makefile' \ + --exclude='$(MOZILLA_DIR)/dist' +ifdef MOZ_OBJDIR + SRC_TAR_EXCLUDE_PATHS += --exclude='$(MOZ_OBJDIR)' +endif +CREATE_SOURCE_TAR = $(TAR) -c --owner=0 --group=0 --numeric-owner \ + --mode=go-w $(SRC_TAR_EXCLUDE_PATHS) --transform='s,^\./,$(SRC_TAR_PREFIX)/,' -f + +SOURCE_TAR = $(DIST)/$(PKG_SRCPACK_PATH)$(PKG_SRCPACK_BASENAME).tar.xz +HG_BUNDLE_FILE = $(DIST)/$(PKG_SRCPACK_PATH)$(PKG_BUNDLE_BASENAME).bundle +SOURCE_CHECKSUM_FILE = $(DIST)/$(PKG_SRCPACK_PATH)$(PKG_SRCPACK_BASENAME).checksums +SOURCE_UPLOAD_FILES = $(SOURCE_TAR) + +HG ?= hg +CREATE_HG_BUNDLE_CMD = $(HG) -v -R $(topsrcdir) bundle --base null +ifdef HG_BUNDLE_REVISION + CREATE_HG_BUNDLE_CMD += -r $(HG_BUNDLE_REVISION) +endif +CREATE_HG_BUNDLE_CMD += $(HG_BUNDLE_FILE) +ifdef UPLOAD_HG_BUNDLE + SOURCE_UPLOAD_FILES += $(HG_BUNDLE_FILE) +endif diff --git a/toolkit/mozapps/installer/windows/nsis/common.nsh b/toolkit/mozapps/installer/windows/nsis/common.nsh new file mode 100755 index 0000000000..a5e3977fb0 --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/common.nsh @@ -0,0 +1,8842 @@ +# 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/. + + +################################################################################ +# Helper defines and macros for toolkit applications + +/** + * Avoid creating macros / functions that overwrite registers (see the + * GetLongPath macro for one way to avoid this)! + * + * Before using the registers exchange the passed in params and save existing + * register values to the stack. + * + * Exch $R9 ; exhange the original $R9 with the top of the stack + * Exch 1 ; exchange the top of the stack with 1 below the top of the stack + * Exch $R8 ; exchange the original $R8 with the top of the stack + * Exch 2 ; exchange the top of the stack with 2 below the top of the stack + * Exch $R7 ; exchange the original $R7 with the top of the stack + * Push $R6 ; push the original $R6 onto the top of the stack + * Push $R5 ; push the original $R5 onto the top of the stack + * Push $R4 ; push the original $R4 onto the top of the stack + * + * <do stuff> + * + * ; Restore the values. + * Pop $R4 ; restore the value for $R4 from the top of the stack + * Pop $R5 ; restore the value for $R5 from the top of the stack + * Pop $R6 ; restore the value for $R6 from the top of the stack + * Exch $R7 ; exchange the new $R7 value with the top of the stack + * Exch 2 ; exchange the top of the stack with 2 below the top of the stack + * Exch $R8 ; exchange the new $R8 value with the top of the stack + * Exch 1 ; exchange the top of the stack with 2 below the top of the stack + * Exch $R9 ; exchange the new $R9 value with the top of the stack + * + * + * When inserting macros in common.nsh from another macro in common.nsh that + * can be used from the uninstaller _MOZFUNC_UN will be undefined when it is + * inserted. Use the following to redefine _MOZFUNC_UN with its original value + * (see the RegCleanMain macro for an example). + * + * !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + * !insertmacro ${_MOZFUNC_UN_TMP}FileJoin + * !insertmacro ${_MOZFUNC_UN_TMP}LineFind + * !insertmacro ${_MOZFUNC_UN_TMP}TextCompareNoDetails + * !insertmacro ${_MOZFUNC_UN_TMP}TrimNewLines + * !undef _MOZFUNC_UN + * !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + * !undef _MOZFUNC_UN_TMP + */ + +; When including a file provided by NSIS check if its verbose macro is defined +; to prevent loading the file a second time. +!ifmacrondef TEXTFUNC_VERBOSE + !include TextFunc.nsh +!endif + +!ifmacrondef FILEFUNC_VERBOSE + !include FileFunc.nsh +!endif + +!ifmacrondef LOGICLIB_VERBOSITY + !include LogicLib.nsh +!endif + +!ifndef WINMESSAGES_INCLUDED + !include WinMessages.nsh +!endif + +; When including WinVer.nsh check if ___WINVER__NSH___ is defined to prevent +; loading the file a second time. +!ifndef ___WINVER__NSH___ + !include WinVer.nsh +!endif + +; When including x64.nsh check if ___X64__NSH___ is defined to prevent +; loading the file a second time. +!ifndef ___X64__NSH___ + !include x64.nsh +!endif + +; NSIS provided macros that we have overridden. +!include overrides.nsh + +!define SHORTCUTS_LOG "shortcuts_log.ini" +!define TO_BE_DELETED "tobedeleted" + +; !define SHCNF_DWORD 0x0003 +; !define SHCNF_FLUSH 0x1000 +!ifndef SHCNF_DWORDFLUSH + !define SHCNF_DWORDFLUSH 0x1003 +!endif +!ifndef SHCNE_ASSOCCHANGED + !define SHCNE_ASSOCCHANGED 0x08000000 +!endif + +################################################################################ +# Macros for debugging + +/** + * The following two macros assist with verifying that a macro doesn't + * overwrite any registers. + * + * Usage: + * ${debugSetRegisters} + * <do stuff> + * ${debugDisplayRegisters} + */ + +/** + * Sets all register values to their name to assist with verifying that a macro + * doesn't overwrite any registers. + */ +!macro debugSetRegisters + StrCpy $0 "$$0" + StrCpy $1 "$$1" + StrCpy $2 "$$2" + StrCpy $3 "$$3" + StrCpy $4 "$$4" + StrCpy $5 "$$5" + StrCpy $6 "$$6" + StrCpy $7 "$$7" + StrCpy $8 "$$8" + StrCpy $9 "$$9" + StrCpy $R0 "$$R0" + StrCpy $R1 "$$R1" + StrCpy $R2 "$$R2" + StrCpy $R3 "$$R3" + StrCpy $R4 "$$R4" + StrCpy $R5 "$$R5" + StrCpy $R6 "$$R6" + StrCpy $R7 "$$R7" + StrCpy $R8 "$$R8" + StrCpy $R9 "$$R9" +!macroend +!define debugSetRegisters "!insertmacro debugSetRegisters" + +/** + * Displays all register values to assist with verifying that a macro doesn't + * overwrite any registers. + */ +!macro debugDisplayRegisters + MessageBox MB_OK \ + "Register Values:$\n\ + $$0 = $0$\n$$1 = $1$\n$$2 = $2$\n$$3 = $3$\n$$4 = $4$\n\ + $$5 = $5$\n$$6 = $6$\n$$7 = $7$\n$$8 = $8$\n$$9 = $9$\n\ + $$R0 = $R0$\n$$R1 = $R1$\n$$R2 = $R2$\n$$R3 = $R3$\n$$R4 = $R4$\n\ + $$R5 = $R5$\n$$R6 = $R6$\n$$R7 = $R7$\n$$R8 = $R8$\n$$R9 = $R9" +!macroend +!define debugDisplayRegisters "!insertmacro debugDisplayRegisters" + + +################################################################################ +# Modern User Interface (MUI) override macros + +; Removed macros in nsis 2.33u (ported from nsis 2.22) +; MUI_LANGUAGEFILE_DEFINE +; MUI_LANGUAGEFILE_LANGSTRING_PAGE +; MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE +; MUI_LANGUAGEFILE_LANGSTRING_DEFINE +; MUI_LANGUAGEFILE_UNLANGSTRING_PAGE + +!macro MOZ_MUI_LANGUAGEFILE_DEFINE DEFINE NAME + + !ifndef "${DEFINE}" + !define "${DEFINE}" "${${NAME}}" + !endif + !undef "${NAME}" + +!macroend + +!macro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE PAGE NAME + + !ifdef MUI_${PAGE}PAGE + LangString "${NAME}" 0 "${${NAME}}" + !undef "${NAME}" + !else + !undef "${NAME}" + !endif + +!macroend + +!macro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE PAGE NAME + + !ifdef MUI_${PAGE}PAGE | MUI_UN${PAGE}PAGE + LangString "${NAME}" 0 "${${NAME}}" + !undef "${NAME}" + !else + !undef "${NAME}" + !endif + +!macroend + +!macro MOZ_MUI_LANGUAGEFILE_LANGSTRING_DEFINE DEFINE NAME + + !ifdef "${DEFINE}" + LangString "${NAME}" 0 "${${NAME}}" + !endif + !undef "${NAME}" + +!macroend + +!macro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE PAGE NAME + + !ifdef MUI_UNINSTALLER + !ifdef MUI_UN${PAGE}PAGE + LangString "${NAME}" 0 "${${NAME}}" + !undef "${NAME}" + !else + !undef "${NAME}" + !endif + !else + !undef "${NAME}" + !endif + +!macroend + +; Modified version of the following MUI macros to support Mozilla localization. +; MUI_LANGUAGE +; MUI_LANGUAGEFILE_BEGIN +; MOZ_MUI_LANGUAGEFILE_END +; See <NSIS App Dir>/Contrib/Modern UI/System.nsh for more information +!define MUI_INSTALLOPTIONS_READ "!insertmacro MUI_INSTALLOPTIONS_READ" + +!macro MOZ_MUI_LANGUAGE LANGUAGE + !verbose push + !verbose ${MUI_VERBOSE} + !include "${LANGUAGE}.nsh" + !verbose pop +!macroend + +!macro MOZ_MUI_LANGUAGEFILE_BEGIN LANGUAGE + !insertmacro MUI_INSERT + !ifndef "MUI_LANGUAGEFILE_${LANGUAGE}_USED" + !define "MUI_LANGUAGEFILE_${LANGUAGE}_USED" + LoadLanguageFile "${LANGUAGE}.nlf" + !else + !error "Modern UI language file ${LANGUAGE} included twice!" + !endif +!macroend + +; Custom version of MUI_LANGUAGEFILE_END. The macro to add the default MUI +; strings and the macros for several strings that are part of the NSIS MUI and +; not in our locale files have been commented out. +!macro MOZ_MUI_LANGUAGEFILE_END + +# !include "${NSISDIR}\Contrib\Modern UI\Language files\Default.nsh" + !ifdef MUI_LANGUAGEFILE_DEFAULT_USED + !undef MUI_LANGUAGEFILE_DEFAULT_USED + !warning "${LANGUAGE} Modern UI language file version doesn't match. Using default English texts for missing strings." + !endif + + !insertmacro MOZ_MUI_LANGUAGEFILE_DEFINE "MUI_${LANGUAGE}_LANGNAME" "MUI_LANGNAME" + + !ifndef MUI_LANGDLL_PUSHLIST + !define MUI_LANGDLL_PUSHLIST "'${MUI_${LANGUAGE}_LANGNAME}' ${LANG_${LANGUAGE}} " + !else + !ifdef MUI_LANGDLL_PUSHLIST_TEMP + !undef MUI_LANGDLL_PUSHLIST_TEMP + !endif + !define MUI_LANGDLL_PUSHLIST_TEMP "${MUI_LANGDLL_PUSHLIST}" + !undef MUI_LANGDLL_PUSHLIST + !define MUI_LANGDLL_PUSHLIST "'${MUI_${LANGUAGE}_LANGNAME}' ${LANG_${LANGUAGE}} ${MUI_LANGDLL_PUSHLIST_TEMP}" + !endif + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE WELCOME "MUI_TEXT_WELCOME_INFO_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE WELCOME "MUI_TEXT_WELCOME_INFO_TEXT" + +!ifdef MUI_TEXT_LICENSE_TITLE + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_TEXT_LICENSE_TITLE" +!endif +!ifdef MUI_TEXT_LICENSE_SUBTITLE + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_TEXT_LICENSE_SUBTITLE" +!endif +!ifdef MUI_INNERTEXT_LICENSE_TOP + !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE LICENSE "MUI_INNERTEXT_LICENSE_TOP" +!endif + +# !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_INNERTEXT_LICENSE_BOTTOM" + +!ifdef MUI_INNERTEXT_LICENSE_BOTTOM_CHECKBOX + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_INNERTEXT_LICENSE_BOTTOM_CHECKBOX" +!endif + +!ifdef MUI_INNERTEXT_LICENSE_BOTTOM_RADIOBUTTONS + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_INNERTEXT_LICENSE_BOTTOM_RADIOBUTTONS" +!endif + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE COMPONENTS "MUI_TEXT_COMPONENTS_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE COMPONENTS "MUI_TEXT_COMPONENTS_SUBTITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE COMPONENTS "MUI_INNERTEXT_COMPONENTS_DESCRIPTION_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE COMPONENTS "MUI_INNERTEXT_COMPONENTS_DESCRIPTION_INFO" + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE DIRECTORY "MUI_TEXT_DIRECTORY_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE DIRECTORY "MUI_TEXT_DIRECTORY_SUBTITLE" + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE STARTMENU "MUI_TEXT_STARTMENU_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE STARTMENU "MUI_TEXT_STARTMENU_SUBTITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE STARTMENU "MUI_INNERTEXT_STARTMENU_TOP" +# !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE STARTMENU "MUI_INNERTEXT_STARTMENU_CHECKBOX" + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_INSTALLING_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_INSTALLING_SUBTITLE" + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_FINISH_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_FINISH_SUBTITLE" + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_ABORT_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_ABORT_SUBTITLE" + + !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_BUTTONTEXT_FINISH" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_INFO_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_INFO_TEXT" + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_INFO_REBOOT" + !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_REBOOTNOW" + !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_REBOOTLATER" +# !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_RUN" +# !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_SHOWREADME" + +; Support for using the existing MUI_TEXT_ABORTWARNING string +!ifdef MOZ_MUI_CUSTOM_ABORT + LangString MOZ_MUI_TEXT_ABORTWARNING 0 "${MUI_TEXT_ABORTWARNING}" +!endif + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_DEFINE MUI_ABORTWARNING "MUI_TEXT_ABORTWARNING" + + + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE WELCOME "MUI_UNTEXT_WELCOME_INFO_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE WELCOME "MUI_UNTEXT_WELCOME_INFO_TEXT" + + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE CONFIRM "MUI_UNTEXT_CONFIRM_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE CONFIRM "MUI_UNTEXT_CONFIRM_SUBTITLE" + +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNTEXT_LICENSE_TITLE" +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNTEXT_LICENSE_SUBTITLE" + +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNINNERTEXT_LICENSE_BOTTOM" +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNINNERTEXT_LICENSE_BOTTOM_CHECKBOX" +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNINNERTEXT_LICENSE_BOTTOM_RADIOBUTTONS" + +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE COMPONENTS "MUI_UNTEXT_COMPONENTS_TITLE" +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE COMPONENTS "MUI_UNTEXT_COMPONENTS_SUBTITLE" + +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE DIRECTORY "MUI_UNTEXT_DIRECTORY_TITLE" +# !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE DIRECTORY "MUI_UNTEXT_DIRECTORY_SUBTITLE" + + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_UNINSTALLING_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_UNINSTALLING_SUBTITLE" + + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_FINISH_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_FINISH_SUBTITLE" + + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_ABORT_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_ABORT_SUBTITLE" + + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE FINISH "MUI_UNTEXT_FINISH_INFO_TITLE" + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE FINISH "MUI_UNTEXT_FINISH_INFO_TEXT" + !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE FINISH "MUI_UNTEXT_FINISH_INFO_REBOOT" + + !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_DEFINE MUI_UNABORTWARNING "MUI_UNTEXT_ABORTWARNING" + + !ifndef MUI_LANGDLL_LANGUAGES + !define MUI_LANGDLL_LANGUAGES "'${LANGFILE_${LANGUAGE}_NAME}' '${LANG_${LANGUAGE}}' " + !define MUI_LANGDLL_LANGUAGES_CP "'${LANGFILE_${LANGUAGE}_NAME}' '${LANG_${LANGUAGE}}' '${LANG_${LANGUAGE}_CP}' " + !else + !ifdef MUI_LANGDLL_LANGUAGES_TEMP + !undef MUI_LANGDLL_LANGUAGES_TEMP + !endif + !define MUI_LANGDLL_LANGUAGES_TEMP "${MUI_LANGDLL_LANGUAGES}" + !undef MUI_LANGDLL_LANGUAGES + + !ifdef MUI_LANGDLL_LANGUAGES_CP_TEMP + !undef MUI_LANGDLL_LANGUAGES_CP_TEMP + !endif + !define MUI_LANGDLL_LANGUAGES_CP_TEMP "${MUI_LANGDLL_LANGUAGES_CP}" + !undef MUI_LANGDLL_LANGUAGES_CP + + !define MUI_LANGDLL_LANGUAGES "'${LANGFILE_${LANGUAGE}_NAME}' '${LANG_${LANGUAGE}}' ${MUI_LANGDLL_LANGUAGES_TEMP}" + !define MUI_LANGDLL_LANGUAGES_CP "'${LANGFILE_${LANGUAGE}_NAME}' '${LANG_${LANGUAGE}}' '${LANG_${LANGUAGE}_CP}' ${MUI_LANGDLL_LANGUAGES_CP_TEMP}" + !endif + +!macroend + +/** + * Creates an InstallOptions file with a UTF-16LE BOM and adds the RTL value + * to the Settings section. + * + * @param _FILE + * The name of the file to be created in $PLUGINSDIR. + */ +!macro InitInstallOptionsFile _FILE + Push $R9 + + FileOpen $R9 "$PLUGINSDIR\${_FILE}" w + FileWriteWord $R9 "65279" + FileClose $R9 + WriteIniStr "$PLUGINSDIR\${_FILE}" "Settings" "RTL" "$(^RTL)" + + Pop $R9 +!macroend + + +################################################################################ +# Macros for handling files in use + +/** + * Checks for files in use in the $INSTDIR directory. To check files in + * sub-directories this macro would need to be rewritten to create + * sub-directories in the temporary directory used to backup the files that are + * checked. + * + * Example usage: + * + * ; The first string to be pushed onto the stack MUST be "end" to indicate + * ; that there are no more files in the $INSTDIR directory to check. + * Push "end" + * Push "freebl3.dll" + * ; The last file pushed should be the app's main exe so if it is in use this + * ; macro will return after the first check. + * Push "${FileMainEXE}" + * ${CheckForFilesInUse} $R9 + * + * !IMPORTANT - this macro uses the $R7, $R8, and $R9 registers and makes no + * attempt to restore their original values. + * + * @return _RESULT + * false if all of the files popped from the stack are not in use. + * True if any of the files popped from the stack are in use. + * $R7 = Temporary backup directory where the files will be copied to. + * $R8 = value popped from the stack. This will either be a file name for a file + * in the $INSTDIR directory or "end" to indicate that there are no + * additional files to check. + * $R9 = _RESULT + */ +!macro CheckForFilesInUse + + !ifndef ${_MOZFUNC_UN}CheckForFilesInUse + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}CheckForFilesInUse "!insertmacro ${_MOZFUNC_UN}CheckForFilesInUseCall" + + Function ${_MOZFUNC_UN}CheckForFilesInUse + ; Create a temporary backup directory. + GetTempFileName $R7 "$INSTDIR" + Delete "$R7" + SetOutPath "$R7" + StrCpy $R9 "false" + + Pop $R8 + ${While} $R8 != "end" + ${Unless} ${FileExists} "$INSTDIR\$R8" + Pop $R8 ; get next file to check before continuing + ${Continue} + ${EndUnless} + + ClearErrors + CopyFiles /SILENT "$INSTDIR\$R8" "$R7\$R8" ; try to copy + ${If} ${Errors} + ; File is in use + StrCpy $R9 "true" + ${Break} + ${EndIf} + + Delete "$INSTDIR\$R8" ; delete original + ${If} ${Errors} + ; File is in use + StrCpy $R9 "true" + Delete "$R7\$R8" ; delete temp copy + ${Break} + ${EndIf} + + Pop $R8 ; get next file to check + ${EndWhile} + + ; clear stack + ${While} $R8 != "end" + Pop $R8 + ${EndWhile} + + ; restore everything + SetOutPath "$INSTDIR" + CopyFiles /SILENT "$R7\*" "$INSTDIR\" + RmDir /r "$R7" + SetOutPath "$EXEDIR" + ClearErrors + + Push $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CheckForFilesInUseCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call CheckForFilesInUse + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.CheckForFilesInUseCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.CheckForFilesInUse + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.CheckForFilesInUse + !ifndef un.CheckForFilesInUse + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro CheckForFilesInUse + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +!macro GetCommonDirectory + + !ifndef ${_MOZFUNC_UN}GetCommonDirectory + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}GetCommonDirectory "!insertmacro ${_MOZFUNC_UN}GetCommonDirectoryCall" + + Function ${_MOZFUNC_UN}GetCommonDirectory + Push $0 ; Save $0 + + ; This gets C:\ProgramData or the equivalent. + ${GetCommonAppDataFolder} $0 + + ; Add our subdirectory, this is hardcoded as grandparent of the update directory in + ; several other places. + StrCpy $0 "$0\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38" + + Exch $0 ; Restore original $0 and put our $0 on the stack. + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro GetCommonDirectoryCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call GetCommonDirectory + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.GetCommonDirectoryCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.GetCommonDirectory + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.GetCommonDirectory + !ifndef un.GetCommonDirectory + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro GetCommonDirectory + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * The macros below will automatically prepend un. to the function names when + * they are defined (e.g. !define un.RegCleanMain). + */ +!verbose push +!verbose 3 +!ifndef _MOZFUNC_VERBOSE + !define _MOZFUNC_VERBOSE 3 +!endif +!verbose ${_MOZFUNC_VERBOSE} +!define MOZFUNC_VERBOSE "!insertmacro MOZFUNC_VERBOSE" +!define _MOZFUNC_UN +!define _MOZFUNC_S +!verbose pop + +!macro MOZFUNC_VERBOSE _VERBOSE + !verbose push + !verbose 3 + !undef _MOZFUNC_VERBOSE + !define _MOZFUNC_VERBOSE ${_VERBOSE} + !verbose pop +!macroend + +/** + * Displays a MessageBox and then calls abort to prevent continuing to the + * next page when the specified Window Class is found. + * + * @param _WINDOW_CLASS + * The Window Class to search for with FindWindow. + * @param _MSG + * The message text to display in the message box. + * + * $R7 = return value from FindWindow + * $R8 = _WINDOW_CLASS + * $R9 = _MSG + */ +!macro ManualCloseAppPrompt + + !ifndef ${_MOZFUNC_UN}ManualCloseAppPrompt + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}ManualCloseAppPrompt "!insertmacro ${_MOZFUNC_UN}ManualCloseAppPromptCall" + + Function ${_MOZFUNC_UN}ManualCloseAppPrompt + Exch $R9 + Exch 1 + Exch $R8 + Push $R7 + + FindWindow $R7 "$R8" + ${If} $R7 <> 0 ; integer comparison + MessageBox MB_OK|MB_ICONQUESTION "$R9" + Abort + ${EndIf} + + Pop $R7 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro ManualCloseAppPromptCall _WINDOW_CLASS _MSG + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_WINDOW_CLASS}" + Push "${_MSG}" + Call ManualCloseAppPrompt + !verbose pop +!macroend + +!macro un.ManualCloseAppPromptCall _WINDOW_CLASS _MSG + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_WINDOW_CLASS}" + Push "${_MSG}" + Call un.ManualCloseAppPrompt + !verbose pop +!macroend + +!macro un.ManualCloseAppPrompt + !ifndef un.ManualCloseAppPrompt + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro ManualCloseAppPrompt + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + + +################################################################################ +# Macros for working with the registry + +/** + * Writes a registry string using SHCTX and the supplied params and logs the + * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1. + * + * Define NO_LOG to prevent all logging when calling this from the uninstaller. + * + * @param _ROOT + * The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.). + * This will only be used for logging. + * @param _KEY + * The subkey in relation to the key root. + * @param _NAME + * The key value name to write to. + * @param _STR + * The string to write to the key value name. + * @param _LOG_UNINSTALL + * 0 = don't add to uninstall log, 1 = add to uninstall log. + * + * $R5 = _ROOT + * $R6 = _KEY + * $R7 = _NAME + * $R8 = _STR + * $R9 = _LOG_UNINSTALL + */ +!macro WriteRegStr2 + + !ifndef ${_MOZFUNC_UN}WriteRegStr2 + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}WriteRegStr2 "!insertmacro ${_MOZFUNC_UN}WriteRegStr2Call" + + Function ${_MOZFUNC_UN}WriteRegStr2 + Exch $R9 + Exch 1 + Exch $R8 + Exch 2 + Exch $R7 + Exch 3 + Exch $R6 + Exch 4 + Exch $R5 + + ClearErrors + WriteRegStr SHCTX "$R6" "$R7" "$R8" + + !ifndef NO_LOG + ${If} ${Errors} + ${LogMsg} "** ERROR Adding Registry String: $R5 | $R6 | $R7 | $R8 **" + ${Else} + ${If} $R9 == 1 ; add to the uninstall log? + ${LogUninstall} "RegVal: $R5 | $R6 | $R7" + ${EndIf} + ${LogMsg} "Added Registry String: $R5 | $R6 | $R7 | $R8" + ${EndIf} + !endif + + Exch $R5 + Exch 4 + Exch $R6 + Exch 3 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro WriteRegStr2Call _ROOT _KEY _NAME _STR _LOG_UNINSTALL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_ROOT}" + Push "${_KEY}" + Push "${_NAME}" + Push "${_STR}" + Push "${_LOG_UNINSTALL}" + Call WriteRegStr2 + !verbose pop +!macroend + +!macro un.WriteRegStr2Call _ROOT _KEY _NAME _STR _LOG_UNINSTALL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_ROOT}" + Push "${_KEY}" + Push "${_NAME}" + Push "${_STR}" + Push "${_LOG_UNINSTALL}" + Call un.WriteRegStr2 + !verbose pop +!macroend + +!macro un.WriteRegStr2 + !ifndef un.WriteRegStr2 + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro WriteRegStr2 + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Writes a registry dword using SHCTX and the supplied params and logs the + * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1. + * + * Define NO_LOG to prevent all logging when calling this from the uninstaller. + * + * @param _ROOT + * The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.). + * This will only be used for logging. + * @param _KEY + * The subkey in relation to the key root. + * @param _NAME + * The key value name to write to. + * @param _DWORD + * The dword to write to the key value name. + * @param _LOG_UNINSTALL + * 0 = don't add to uninstall log, 1 = add to uninstall log. + * + * $R5 = _ROOT + * $R6 = _KEY + * $R7 = _NAME + * $R8 = _DWORD + * $R9 = _LOG_UNINSTALL + */ +!macro WriteRegDWORD2 + + !ifndef ${_MOZFUNC_UN}WriteRegDWORD2 + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}WriteRegDWORD2 "!insertmacro ${_MOZFUNC_UN}WriteRegDWORD2Call" + + Function ${_MOZFUNC_UN}WriteRegDWORD2 + Exch $R9 + Exch 1 + Exch $R8 + Exch 2 + Exch $R7 + Exch 3 + Exch $R6 + Exch 4 + Exch $R5 + + ClearErrors + WriteRegDWORD SHCTX "$R6" "$R7" "$R8" + + !ifndef NO_LOG + ${If} ${Errors} + ${LogMsg} "** ERROR Adding Registry DWord: $R5 | $R6 | $R7 | $R8 **" + ${Else} + ${If} $R9 == 1 ; add to the uninstall log? + ${LogUninstall} "RegVal: $R5 | $R6 | $R7" + ${EndIf} + ${LogMsg} "Added Registry DWord: $R5 | $R6 | $R7 | $R8" + ${EndIf} + !endif + + Exch $R5 + Exch 4 + Exch $R6 + Exch 3 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro WriteRegDWORD2Call _ROOT _KEY _NAME _DWORD _LOG_UNINSTALL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_ROOT}" + Push "${_KEY}" + Push "${_NAME}" + Push "${_DWORD}" + Push "${_LOG_UNINSTALL}" + Call WriteRegDWORD2 + !verbose pop +!macroend + +!macro un.WriteRegDWORD2Call _ROOT _KEY _NAME _DWORD _LOG_UNINSTALL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_ROOT}" + Push "${_KEY}" + Push "${_NAME}" + Push "${_DWORD}" + Push "${_LOG_UNINSTALL}" + Call un.WriteRegDWORD2 + !verbose pop +!macroend + +!macro un.WriteRegDWORD2 + !ifndef un.WriteRegDWORD2 + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro WriteRegDWORD2 + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Writes a registry string to HKCR using the supplied params and logs the + * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1. + * + * Define NO_LOG to prevent all logging when calling this from the uninstaller. + * + * @param _ROOT + * The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.). + * This will only be used for logging. + * @param _KEY + * The subkey in relation to the key root. + * @param _NAME + * The key value name to write to. + * @param _STR + * The string to write to the key value name. + * @param _LOG_UNINSTALL + * 0 = don't add to uninstall log, 1 = add to uninstall log. + * + * $R5 = _ROOT + * $R6 = _KEY + * $R7 = _NAME + * $R8 = _STR + * $R9 = _LOG_UNINSTALL + */ +!macro WriteRegStrHKCR + + !ifndef ${_MOZFUNC_UN}WriteRegStrHKCR + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}WriteRegStrHKCR "!insertmacro ${_MOZFUNC_UN}WriteRegStrHKCRCall" + + Function ${_MOZFUNC_UN}WriteRegStrHKCR + Exch $R9 + Exch 1 + Exch $R8 + Exch 2 + Exch $R7 + Exch 3 + Exch $R6 + Exch 4 + Exch $R5 + + ClearErrors + WriteRegStr HKCR "$R6" "$R7" "$R8" + + !ifndef NO_LOG + ${If} ${Errors} + ${LogMsg} "** ERROR Adding Registry String: $R5 | $R6 | $R7 | $R8 **" + ${Else} + ${If} $R9 == 1 ; add to the uninstall log? + ${LogUninstall} "RegVal: $R5 | $R6 | $R7" + ${EndIf} + ${LogMsg} "Added Registry String: $R5 | $R6 | $R7 | $R8" + ${EndIf} + !endif + + Exch $R5 + Exch 4 + Exch $R6 + Exch 3 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro WriteRegStrHKCRCall _ROOT _KEY _NAME _STR _LOG_UNINSTALL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_ROOT}" + Push "${_KEY}" + Push "${_NAME}" + Push "${_STR}" + Push "${_LOG_UNINSTALL}" + Call WriteRegStrHKCR + !verbose pop +!macroend + +!macro un.WriteRegStrHKCRCall _ROOT _KEY _NAME _STR _LOG_UNINSTALL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_ROOT}" + Push "${_KEY}" + Push "${_NAME}" + Push "${_STR}" + Push "${_LOG_UNINSTALL}" + Call un.WriteRegStrHKCR + !verbose pop +!macroend + +!macro un.WriteRegStrHKCR + !ifndef un.WriteRegStrHKCR + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro WriteRegStrHKCR + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +!ifndef KEY_SET_VALUE + !define KEY_SET_VALUE 0x0002 +!endif +!ifndef KEY_WOW64_64KEY + !define KEY_WOW64_64KEY 0x0100 +!endif +!ifndef HAVE_64BIT_BUILD + !define CREATE_KEY_SAM ${KEY_SET_VALUE} +!else + !define CREATE_KEY_SAM ${KEY_SET_VALUE}|${KEY_WOW64_64KEY} +!endif + +/** + * Creates a registry key. This will log the actions to the install and + * uninstall logs. Alternatively you can set a registry value to create the key + * and then delete the value. + * + * Define NO_LOG to prevent all logging when calling this from the uninstaller. + * + * @param _ROOT + * The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.). + * @param _KEY + * The subkey in relation to the key root. + * @param _LOG_UNINSTALL + * 0 = don't add to uninstall log, 1 = add to uninstall log. + * + * $R4 = [out] handle to newly created registry key. If this is not a key + * located in one of the predefined registry keys this must be closed + * with RegCloseKey (this should not be needed unless someone decides to + * do something extremely squirrelly with NSIS). + * $R5 = return value from RegCreateKeyExW (represented by R5 in the system call). + * $R6 = [in] hKey passed to RegCreateKeyExW. + * $R7 = _ROOT + * $R8 = _KEY + * $R9 = _LOG_UNINSTALL + */ +!macro CreateRegKey + + !ifndef ${_MOZFUNC_UN}CreateRegKey + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}CreateRegKey "!insertmacro ${_MOZFUNC_UN}CreateRegKeyCall" + + Function ${_MOZFUNC_UN}CreateRegKey + Exch $R9 + Exch 1 + Exch $R8 + Exch 2 + Exch $R7 + Push $R6 + Push $R5 + Push $R4 + + StrCmp $R7 "HKCR" +1 +2 + StrCpy $R6 "0x80000000" + StrCmp $R7 "HKCU" +1 +2 + StrCpy $R6 "0x80000001" + StrCmp $R7 "HKLM" +1 +2 + StrCpy $R6 "0x80000002" + + ; see definition of RegCreateKey + System::Call "Advapi32::RegCreateKeyExW(i R6, w R8, i 0, i 0, i 0,\ + i ${CREATE_KEY_SAM}, i 0, *i .R4,\ + i 0) i .R5" + + !ifndef NO_LOG + ; if $R5 is not 0 then there was an error creating the registry key. + ${If} $R5 <> 0 + ${LogMsg} "** ERROR Adding Registry Key: $R7 | $R8 **" + ${Else} + ${If} $R9 == 1 ; add to the uninstall log? + ${LogUninstall} "RegKey: $R7 | $R8" + ${EndIf} + ${LogMsg} "Added Registry Key: $R7 | $R8" + ${EndIf} + !endif + + StrCmp $R5 0 +1 +2 + System::Call "Advapi32::RegCloseKey(iR4)" + + Pop $R4 + Pop $R5 + Pop $R6 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CreateRegKeyCall _ROOT _KEY _LOG_UNINSTALL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_ROOT}" + Push "${_KEY}" + Push "${_LOG_UNINSTALL}" + Call CreateRegKey + !verbose pop +!macroend + +!macro un.CreateRegKeyCall _ROOT _KEY _LOG_UNINSTALL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_ROOT}" + Push "${_KEY}" + Push "${_LOG_UNINSTALL}" + Call un.CreateRegKey + !verbose pop +!macroend + +!macro un.CreateRegKey + !ifndef un.CreateRegKey + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro CreateRegKey + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Helper for checking for the existence of a registry key. + * SHCTX is the root key to search. + * + * @param _MAIN_KEY + * Sub key to iterate for the key in question + * @param _KEY + * Key name to search for + * @return _RESULT + * 'true' / 'false' result + */ +!macro CheckIfRegistryKeyExists + !ifndef CheckIfRegistryKeyExists + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define CheckIfRegistryKeyExists "!insertmacro CheckIfRegistryKeyExistsCall" + + Function CheckIfRegistryKeyExists + ; stack: main key, key + Exch $R9 ; main key, stack: old R9, key + Exch 1 ; stack: key, old R9 + Exch $R8 ; key, stack: old R8, old R9 + Push $R7 + Push $R6 + Push $R5 + + StrCpy $R5 "false" + StrCpy $R7 "0" # loop index + ${Do} + EnumRegKey $R6 SHCTX "$R9" "$R7" + ${If} "$R6" == "$R8" + StrCpy $R5 "true" + ${Break} + ${EndIf} + IntOp $R7 $R7 + 1 + ${LoopWhile} $R6 != "" + ClearErrors + + StrCpy $R9 $R5 + + Pop $R5 + Pop $R6 + Pop $R7 ; stack: old R8, old R9 + Pop $R8 ; stack: old R9 + Exch $R9 ; stack: result + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CheckIfRegistryKeyExistsCall _MAIN_KEY _KEY _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Push "${_MAIN_KEY}" + Call CheckIfRegistryKeyExists + Pop ${_RESULT} + !verbose pop +!macroend + +/** + * Read the value of an installer pref that's been set by the product. + * + * @param _KEY ($R1) + * Sub key containing all the installer prefs + * Usually "Software\Mozilla\${AppName}" + * @param _PREF ($R2) + * Name of the pref to look up + * @return _RESULT ($R3) + * 'true' or 'false' (only boolean prefs are supported) + * If no value exists for the requested pref, the result is 'false' + */ +!macro GetInstallerRegistryPref + !ifndef ${_MOZFUNC_UN}GetInstallerRegistryPref + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}GetInstallerRegistryPref "!insertmacro GetInstallerRegistryPrefCall" + + Function ${_MOZFUNC_UN}GetInstallerRegistryPref + ; stack: key, pref + Exch $R1 ; key, stack: old R1, pref + Exch 1 ; stack: pref, old R1 + Exch $R2 ; pref, stack: old R2, old R1 + Push $R3 + + StrCpy $R3 0 + + ; These prefs are always stored in the native registry. + SetRegView 64 + + ClearErrors + ReadRegDWORD $R3 HKCU "$R1\Installer\$AppUserModelID" "$R2" + + SetRegView lastused + + ${IfNot} ${Errors} + ${AndIf} $R3 != 0 + StrCpy $R1 "true" + ${Else} + StrCpy $R1 "false" + ${EndIf} + + ; stack: old R3, old R2, old R1 + Pop $R3 ; stack: old R2, old R1 + Pop $R2 ; stack: old R1 + Exch $R1 ; stack: result + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro GetInstallerRegistryPrefCall _KEY _PREF _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PREF}" + Push "${_KEY}" + Call GetInstallerRegistryPref + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.GetInstallerRegistryPrefCall _KEY _PREF _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PREF}" + Push "${_KEY}" + Call un.GetInstallerRegistryPref + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.GetInstallerRegistryPref + !ifndef un.GetInstallerRegistryPref + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro GetInstallerRegistryPref + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +################################################################################ +# Macros for adding file and protocol handlers + +/** + * Writes common registry values for a handler using SHCTX. + * + * @param _KEY + * The subkey in relation to the key root. + * @param _VALOPEN + * The path and args to launch the application. + * @param _VALICON + * The path to the binary that contains the icon group for the default icon + * followed by a comma and either the icon group's resource index or the icon + * group's resource id prefixed with a minus sign + * @param _DISPNAME + * The display name for the handler. If emtpy no value will be set. + * @param _ISPROTOCOL + * Sets protocol handler specific registry values when "true". + * Deletes protocol handler specific registry values when "delete". + * Otherwise doesn't touch handler specific registry values. + * @param _ISDDE + * Sets DDE specific registry values when "true". + * + * $R3 = string value of the current registry key path. + * $R4 = _KEY + * $R5 = _VALOPEN + * $R6 = _VALICON + * $R7 = _DISPNAME + * $R8 = _ISPROTOCOL + * $R9 = _ISDDE + */ +!macro AddHandlerValues + + !ifndef ${_MOZFUNC_UN}AddHandlerValues + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}AddHandlerValues "!insertmacro ${_MOZFUNC_UN}AddHandlerValuesCall" + + Function ${_MOZFUNC_UN}AddHandlerValues + Exch $R9 + Exch 1 + Exch $R8 + Exch 2 + Exch $R7 + Exch 3 + Exch $R6 + Exch 4 + Exch $R5 + Exch 5 + Exch $R4 + Push $R3 + + StrCmp "$R7" "" +6 +1 + ReadRegStr $R3 SHCTX "$R4" "FriendlyTypeName" + + StrCmp "$R3" "" +1 +3 + WriteRegStr SHCTX "$R4" "" "$R7" + WriteRegStr SHCTX "$R4" "FriendlyTypeName" "$R7" + + StrCmp "$R8" "true" +1 +2 + WriteRegStr SHCTX "$R4" "URL Protocol" "" + StrCmp "$R8" "delete" +1 +2 + DeleteRegValue SHCTX "$R4" "URL Protocol" + StrCpy $R3 "" + ReadRegDWord $R3 SHCTX "$R4" "EditFlags" + StrCmp $R3 "" +1 +3 ; Only add EditFlags if a value doesn't exist + DeleteRegValue SHCTX "$R4" "EditFlags" + WriteRegDWord SHCTX "$R4" "EditFlags" 0x00000002 + + StrCmp "$R6" "" +2 +1 + WriteRegStr SHCTX "$R4\DefaultIcon" "" "$R6" + + StrCmp "$R5" "" +2 +1 + WriteRegStr SHCTX "$R4\shell\open\command" "" "$R5" + +!ifdef DDEApplication + StrCmp "$R9" "true" +1 +11 + WriteRegStr SHCTX "$R4\shell\open\ddeexec" "" "$\"%1$\",,0,0,,,," + WriteRegStr SHCTX "$R4\shell\open\ddeexec" "NoActivateHandler" "" + WriteRegStr SHCTX "$R4\shell\open\ddeexec\Application" "" "${DDEApplication}" + WriteRegStr SHCTX "$R4\shell\open\ddeexec\Topic" "" "WWW_OpenURL" + ; The ifexec key may have been added by another application so try to + ; delete it to prevent it from breaking this app's shell integration. + ; Also, IE 6 and below doesn't remove this key when it sets itself as the + ; default handler and if this key exists IE's shell integration breaks. + DeleteRegKey HKLM "$R4\shell\open\ddeexec\ifexec" + DeleteRegKey HKCU "$R4\shell\open\ddeexec\ifexec" +!endif + + ClearErrors + + Pop $R3 + Exch $R4 + Exch 5 + Exch $R5 + Exch 4 + Exch $R6 + Exch 3 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro AddHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL _ISDDE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Push "${_VALOPEN}" + Push "${_VALICON}" + Push "${_DISPNAME}" + Push "${_ISPROTOCOL}" + Push "${_ISDDE}" + Call AddHandlerValues + !verbose pop +!macroend + +!macro un.AddHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL _ISDDE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Push "${_VALOPEN}" + Push "${_VALICON}" + Push "${_DISPNAME}" + Push "${_ISPROTOCOL}" + Push "${_ISDDE}" + Call un.AddHandlerValues + !verbose pop +!macroend + +!macro un.AddHandlerValues + !ifndef un.AddHandlerValues + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro AddHandlerValues + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Writes common registry values for a handler that DOES NOT use DDE using SHCTX. + * + * @param _KEY + * The key name in relation to the HKCR root. SOFTWARE\Classes is + * prefixed to this value when using SHCTX. + * @param _VALOPEN + * The path and args to launch the application. + * @param _VALICON + * The path to the binary that contains the icon group for the default icon + * followed by a comma and either the icon group's resource index or the icon + * group's resource id prefixed with a minus sign + * @param _DISPNAME + * The display name for the handler. If emtpy no value will be set. + * @param _ISPROTOCOL + * Sets protocol handler specific registry values when "true". + * Deletes protocol handler specific registry values when "delete". + * Otherwise doesn't touch handler specific registry values. + * + * $R3 = storage for SOFTWARE\Classes + * $R4 = string value of the current registry key path. + * $R5 = _KEY + * $R6 = _VALOPEN + * $R7 = _VALICON + * $R8 = _DISPNAME + * $R9 = _ISPROTOCOL + */ +!macro AddDisabledDDEHandlerValues + + !ifndef ${_MOZFUNC_UN}AddDisabledDDEHandlerValues + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}AddDisabledDDEHandlerValues "!insertmacro ${_MOZFUNC_UN}AddDisabledDDEHandlerValuesCall" + + Function ${_MOZFUNC_UN}AddDisabledDDEHandlerValues + Exch $R9 ; _ISPROTOCOL + Exch 1 + Exch $R8 ; FriendlyTypeName + Exch 2 + Exch $R7 ; icon index + Exch 3 + Exch $R6 ; shell\open\command + Exch 4 + Exch $R5 ; reg key + Push $R4 ; + Push $R3 ; base reg class + + StrCpy $R3 "SOFTWARE\Classes" + StrCmp "$R8" "" +6 +1 + ReadRegStr $R4 SHCTX "$R5" "FriendlyTypeName" + + StrCmp "$R4" "" +1 +3 + WriteRegStr SHCTX "$R3\$R5" "" "$R8" + WriteRegStr SHCTX "$R3\$R5" "FriendlyTypeName" "$R8" + + StrCmp "$R9" "true" +1 +2 + WriteRegStr SHCTX "$R3\$R5" "URL Protocol" "" + StrCmp "$R9" "delete" +1 +2 + DeleteRegValue SHCTX "$R3\$R5" "URL Protocol" + StrCpy $R4 "" + ReadRegDWord $R4 SHCTX "$R3\$R5" "EditFlags" + StrCmp $R4 "" +1 +3 ; Only add EditFlags if a value doesn't exist + DeleteRegValue SHCTX "$R3\$R5" "EditFlags" + WriteRegDWord SHCTX "$R3\$R5" "EditFlags" 0x00000002 + + StrCmp "$R7" "" +2 +1 + WriteRegStr SHCTX "$R3\$R5\DefaultIcon" "" "$R7" + + ; Main command handler for the app + WriteRegStr SHCTX "$R3\$R5\shell" "" "open" + WriteRegStr SHCTX "$R3\$R5\shell\open\command" "" "$R6" + + ; Drop support for DDE (bug 491947), and remove old dde entries if + ; they exist. + ; + ; Note, changes in SHCTX should propegate to hkey classes root when + ; current user or local machine entries are written. Windows will also + ; attempt to propegate entries when a handler is used. CR entries are a + ; combination of LM and CU, with CU taking priority. + ; + ; To disable dde, an empty shell/ddeexec key must be created in current + ; user or local machine. Unfortunately, settings have various different + ; behaviors depending on the windows version. The following code attempts + ; to address these differences. + ; + ; IE does not configure ddeexec, so issues with left over ddeexec keys + ; in LM are reduced. We configure an empty ddeexec key with an empty default + ; string in CU to be sure. + ; + DeleteRegKey SHCTX "SOFTWARE\Classes\$R5\shell\open\ddeexec" + WriteRegStr SHCTX "SOFTWARE\Classes\$R5\shell\open\ddeexec" "" "" + + ClearErrors + + Pop $R3 + Pop $R4 + Exch $R5 + Exch 4 + Exch $R6 + Exch 3 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro AddDisabledDDEHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Push "${_VALOPEN}" + Push "${_VALICON}" + Push "${_DISPNAME}" + Push "${_ISPROTOCOL}" + Call AddDisabledDDEHandlerValues + !verbose pop +!macroend + +!macro un.AddDisabledDDEHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Push "${_VALOPEN}" + Push "${_VALICON}" + Push "${_DISPNAME}" + Push "${_ISPROTOCOL}" + Call un.AddDisabledDDEHandlerValues + !verbose pop +!macroend + +!macro un.AddDisabledDDEHandlerValues + !ifndef un.AddDisabledDDEHandlerValues + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro AddDisabledDDEHandlerValues + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + + +################################################################################ +# Macros for handling DLL registration + +!macro RegisterDLL DLL + + ; The x64 regsvr32.exe registers x86 DLL's properly so just use it + ; when installing on an x64 systems even when installing an x86 application. + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + ${DisableX64FSRedirection} + ExecWait '"$SYSDIR\regsvr32.exe" /s "${DLL}"' + ${EnableX64FSRedirection} + ${Else} + RegDLL "${DLL}" + ${EndIf} + +!macroend + +!macro UnregisterDLL DLL + + ; The x64 regsvr32.exe registers x86 DLL's properly so just use it + ; when installing on an x64 systems even when installing an x86 application. + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + ${DisableX64FSRedirection} + ExecWait '"$SYSDIR\regsvr32.exe" /s /u "${DLL}"' + ${EnableX64FSRedirection} + ${Else} + UnRegDLL "${DLL}" + ${EndIf} + +!macroend + +!define RegisterDLL "!insertmacro RegisterDLL" +!define UnregisterDLL "!insertmacro UnregisterDLL" + + +################################################################################ +# Macros for retrieving special folders + +/** + * These macro get special folder paths directly, without depending on + * SetShellVarContext. + * + * Usage: + * ${GetProgramsFolder} $0 + * ${GetLocalAppDataFolder} $0 + * ${GetCommonAppDataFolder} $0 + * + */ +!macro GetSpecialFolder _ID _RESULT + ; This system call gets the directory path. The arguments are: + ; A null ptr for hwnd + ; t.s puts the output string on the NSIS stack + ; id indicates which dir to get + ; false for fCreate (i.e. Do not create the folder if it doesn't exist) + System::Call "Shell32::SHGetSpecialFolderPathW(p 0, t.s, i ${_ID}, i 0)" + Pop ${_RESULT} +!macroend + +!define CSIDL_PROGRAMS 0x0002 +!define CSIDL_LOCAL_APPDATA 0x001c +!define CSIDL_COMMON_APPDATA 0x0023 + +; Current User's Start Menu Programs +!define GetProgramsFolder "!insertmacro GetSpecialFolder ${CSIDL_PROGRAMS}" + +; Current User's Local App Data (e.g. C:\Users\<user>\AppData\Local) +!define GetLocalAppDataFolder "!insertmacro GetSpecialFolder ${CSIDL_LOCAL_APPDATA}" + +; Common App Data (e.g. C:\ProgramData) +!define GetCommonAppDataFolder "!insertmacro GetSpecialFolder ${CSIDL_COMMON_APPDATA}" + +################################################################################ +# Macros for retrieving existing install paths + +/** + * Finds a second installation of the application so we can make informed + * decisions about registry operations. This uses SHCTX to determine the + * registry hive so you must call SetShellVarContext first. + * + * @param _KEY + * The registry subkey (typically this will be Software\Mozilla). + * @return _RESULT + * false if a second install isn't found, path to the main exe if a + * second install is found. + * + * $R3 = stores the long path to $INSTDIR + * $R4 = counter for the outer loop's EnumRegKey + * $R5 = return value from ReadRegStr and RemoveQuotesFromPath + * $R6 = return value from GetParent + * $R7 = return value from the loop's EnumRegKey + * $R8 = storage for _KEY + * $R9 = _KEY and _RESULT + */ +!macro GetSecondInstallPath + + !ifndef ${_MOZFUNC_UN}GetSecondInstallPath + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}GetSecondInstallPath "!insertmacro ${_MOZFUNC_UN}GetSecondInstallPathCall" + + Function ${_MOZFUNC_UN}GetSecondInstallPath + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R3 + + StrCpy $R4 0 ; set the counter for the loop to 0 + StrCpy $R8 "$R9" ; Registry key path to search + StrCpy $R9 "false" ; default return value + + loop: + EnumRegKey $R7 SHCTX $R8 $R4 + StrCmp $R7 "" end +1 ; if empty there are no more keys to enumerate + IntOp $R4 $R4 + 1 ; increment the loop's counter + ClearErrors + ReadRegStr $R5 SHCTX "$R8\$R7\bin" "PathToExe" + IfErrors loop + + ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R5" $R5 + + IfFileExists "$R5" +1 loop + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ${${_MOZFUNC_UN}GetParent} "$R5" $R6 + StrCmp "$R6" "$R3" loop +1 + StrCmp "$R6\${FileMainEXE}" "$R5" +1 loop + StrCpy $R9 "$R5" + + end: + ClearErrors + + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro GetSecondInstallPathCall _KEY _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call GetSecondInstallPath + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.GetSecondInstallPathCall _KEY _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call un.GetSecondInstallPath + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.GetSecondInstallPath + !ifndef un.GetSecondInstallPath + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro GetSecondInstallPath + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Finds an existing installation path for the application based on the + * application's executable name so we can default to using this path for the + * install. If there is zero or more than one installation of the application + * then we default to the default installation path. This uses SHCTX to + * determine the registry hive to read from so you must call SetShellVarContext + * first. + * + * @param _KEY + * The registry subkey (typically this will be Software\Mozilla\App Name). + * @return _RESULT + * false if a single install location for this app name isn't found, + * path to the install directory if a single install location is found. + * + * $R5 = counter for the loop's EnumRegKey + * $R6 = return value from EnumRegKey + * $R7 = return value from ReadRegStr + * $R8 = storage for _KEY + * $R9 = _KEY and _RESULT + */ +!macro GetSingleInstallPath + + !ifndef ${_MOZFUNC_UN}GetSingleInstallPath + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}GetSingleInstallPath "!insertmacro ${_MOZFUNC_UN}GetSingleInstallPathCall" + + Function ${_MOZFUNC_UN}GetSingleInstallPath + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + + StrCpy $R8 $R9 + StrCpy $R9 "false" + StrCpy $R5 0 ; set the counter for the loop to 0 + + loop: + ClearErrors + EnumRegKey $R6 SHCTX $R8 $R5 + IfErrors cleanup + StrCmp $R6 "" cleanup +1 ; if empty there are no more keys to enumerate + IntOp $R5 $R5 + 1 ; increment the loop's counter + ClearErrors + ReadRegStr $R7 SHCTX "$R8\$R6\Main" "PathToExe" + IfErrors loop + ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R7" $R7 + GetFullPathName $R7 "$R7" + IfErrors loop + + StrCmp "$R9" "false" +1 +3 + StrCpy $R9 "$R7" + GoTo Loop + + StrCpy $R9 "false" + + cleanup: + StrCmp $R9 "false" end +1 + ${${_MOZFUNC_UN}GetLongPath} "$R9" $R9 + ${${_MOZFUNC_UN}GetParent} "$R9" $R9 + + end: + ClearErrors + + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro GetSingleInstallPathCall _KEY _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call GetSingleInstallPath + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.GetSingleInstallPathCall _KEY _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call un.GetSingleInstallPath + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.GetSingleInstallPath + !ifndef un.GetSingleInstallPath + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro GetSingleInstallPath + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Find the first existing installation for the application. + * This is similar to GetSingleInstallPath, except that it always returns the + * first path it finds, instead of an error when more than one path exists. + * + * The shell context and the registry view should already have been set. + * + * @param _KEY + * The registry subkey (typically Software\Mozilla\App Name). + * @return _RESULT + * path to the install directory of the first location found, or + * the string "false" if no existing installation was found. + * + * $R5 = counter for the loop's EnumRegKey + * $R6 = return value from EnumRegKey + * $R7 = return value from ReadRegStr + * $R8 = storage for _KEY + * $R9 = _KEY and _RESULT + */ +!macro GetFirstInstallPath + !ifndef ${_MOZFUNC_UN}GetFirstInstallPath + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}GetFirstInstallPath "!insertmacro ${_MOZFUNC_UN}__GetFirstInstallPathCall" + + Function ${_MOZFUNC_UN}__GetFirstInstallPath + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + + StrCpy $R8 $R9 + StrCpy $R9 "false" + StrCpy $R5 0 + + ${Do} + ClearErrors + EnumRegKey $R6 SHCTX $R8 $R5 + ${If} ${Errors} + ${OrIf} $R6 == "" + ${Break} + ${EndIf} + + IntOp $R5 $R5 + 1 + + ReadRegStr $R7 SHCTX "$R8\$R6\Main" "PathToExe" + ${If} ${Errors} + ${Continue} + ${EndIf} + + ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R7" $R7 + GetFullPathName $R7 "$R7" + ${If} ${Errors} + ${Continue} + ${EndIf} + + StrCpy $R9 "$R7" + ${Break} + ${Loop} + + ${If} $R9 != "false" + ${${_MOZFUNC_UN}GetLongPath} "$R9" $R9 + ${${_MOZFUNC_UN}GetParent} "$R9" $R9 + ${EndIf} + + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro __GetFirstInstallPathCall _KEY _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call __GetFirstInstallPath + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.__GetFirstInstallPathCall _KEY _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call un.__GetFirstInstallPath + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.__GetFirstInstallPath + !ifndef un.__GetFirstInstallPath + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro __GetFirstInstallPath + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + + +################################################################################ +# Macros for working with the file system + +/** + * Attempts to delete a file if it exists. This will fail if the file is in use. + * + * @param _FILE + * The path to the file that is to be deleted. + */ +!macro DeleteFile _FILE + ${If} ${FileExists} "${_FILE}" + Delete "${_FILE}" + ${EndIf} +!macroend +!define DeleteFile "!insertmacro DeleteFile" + +/** + * Removes a directory if it exists and is empty. + * + * @param _DIR + * The path to the directory that is to be removed. + */ +!macro RemoveDir _DIR + ${If} ${FileExists} "${_DIR}" + RmDir "${_DIR}" + ${EndIf} +!macroend +!define RemoveDir "!insertmacro RemoveDir" + +/** + * Checks whether it is possible to create and delete a directory and a file in + * the install directory. Creation and deletion of files and directories are + * checked since a user may have rights for one and not the other. If creation + * and deletion of a file and a directory are successful this macro will return + * true... if not, this it return false. + * + * @return _RESULT + * true if files and directories can be created and deleted in the + * install directory otherwise false. + * + * $R8 = temporary filename in the installation directory returned from + * GetTempFileName. + * $R9 = _RESULT + */ +!macro CanWriteToInstallDir + + !ifndef ${_MOZFUNC_UN}CanWriteToInstallDir + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}CanWriteToInstallDir "!insertmacro ${_MOZFUNC_UN}CanWriteToInstallDirCall" + + Function ${_MOZFUNC_UN}CanWriteToInstallDir + Push $R9 + Push $R8 + + StrCpy $R9 "true" + + ; IfFileExists returns false for $INSTDIR when $INSTDIR is the root of a + ; UNC path so always try to create $INSTDIR + CreateDirectory "$INSTDIR\" + GetTempFileName $R8 "$INSTDIR\" + + ${Unless} ${FileExists} $R8 ; Can files be created? + StrCpy $R9 "false" + Goto done + ${EndUnless} + + Delete $R8 + ${If} ${FileExists} $R8 ; Can files be deleted? + StrCpy $R9 "false" + Goto done + ${EndIf} + + CreateDirectory $R8 + ${Unless} ${FileExists} $R8 ; Can directories be created? + StrCpy $R9 "false" + Goto done + ${EndUnless} + + RmDir $R8 + ${If} ${FileExists} $R8 ; Can directories be deleted? + StrCpy $R9 "false" + Goto done + ${EndIf} + + done: + + RmDir "$INSTDIR\" ; Only remove $INSTDIR if it is empty + ClearErrors + + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CanWriteToInstallDirCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call CanWriteToInstallDir + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.CanWriteToInstallDirCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.CanWriteToInstallDir + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.CanWriteToInstallDir + !ifndef un.CanWriteToInstallDir + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro CanWriteToInstallDir + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Checks whether there is sufficient free space available for the installation + * directory using GetDiskFreeSpaceExW which respects disk quotas. This macro + * will calculate the size of all sections that are selected, compare that with + * the free space available, and if there is sufficient free space it will + * return true... if not, it will return false. + * + * @return _RESULT + * "true" if there is sufficient free space otherwise "false". + * + * $R5 = return value from SectionGetSize + * $R6 = return value from SectionGetFlags + * return value from an 'and' comparison of SectionGetFlags (1=selected) + * return value for lpFreeBytesAvailable from GetDiskFreeSpaceExW + * return value for System::Int64Op $R6 / 1024 + * return value for System::Int64Op $R6 > $R8 + * $R7 = the counter for enumerating the sections + * the temporary file name for the directory created under $INSTDIR passed + * to GetDiskFreeSpaceExW. + * $R8 = sum in KB of all selected sections + * $R9 = _RESULT + */ +!macro CheckDiskSpace + + !ifndef ${_MOZFUNC_UN}CheckDiskSpace + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}CheckDiskSpace "!insertmacro ${_MOZFUNC_UN}CheckDiskSpaceCall" + + Function ${_MOZFUNC_UN}CheckDiskSpace + Push $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + + ClearErrors + + StrCpy $R9 "true" ; default return value + StrCpy $R8 "0" ; sum in KB of all selected sections + StrCpy $R7 "0" ; counter for enumerating sections + + ; Enumerate the sections and sum up the sizes of the sections that are + ; selected. + SectionGetFlags $R7 $R6 + IfErrors +7 +1 + IntOp $R6 ${SF_SELECTED} & $R6 + IntCmp $R6 0 +3 +1 +1 + SectionGetSize $R7 $R5 + IntOp $R8 $R8 + $R5 + IntOp $R7 $R7 + 1 + GoTo -7 + + ; The directory passed to GetDiskFreeSpaceExW must exist for the call to + ; succeed. Since the CanWriteToInstallDir macro is called prior to this + ; macro the call to CreateDirectory will always succeed. + + ; IfFileExists returns false for $INSTDIR when $INSTDIR is the root of a + ; UNC path so always try to create $INSTDIR + CreateDirectory "$INSTDIR\" + GetTempFileName $R7 "$INSTDIR\" + Delete "$R7" + CreateDirectory "$R7" + + System::Call 'kernel32::GetDiskFreeSpaceExW(w, *l, *l, *l) i(R7, .R6, ., .) .' + + ; Convert to KB for comparison with $R8 which is in KB + System::Int64Op $R6 / 1024 + Pop $R6 + + System::Int64Op $R6 > $R8 + Pop $R6 + + IntCmp $R6 1 end +1 +1 + StrCpy $R9 "false" + + end: + RmDir "$R7" + RmDir "$INSTDIR\" ; Only remove $INSTDIR if it is empty + + ClearErrors + + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CheckDiskSpaceCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call CheckDiskSpace + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.CheckDiskSpaceCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.CheckDiskSpace + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.CheckDiskSpace + !ifndef un.CheckDiskSpace + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro CheckDiskSpace + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** +* Returns the path found within a passed in string. The path is quoted or not +* with the exception of an unquoted non 8dot3 path without arguments that is +* also not a DefaultIcon path, is a 8dot3 path or not, has command line +* arguments, or is a registry DefaultIcon path (e.g. <path to binary>,# where # +* is the icon's resuorce id). The string does not need to be a valid path or +* exist. It is up to the caller to pass in a string of one of the forms noted +* above and to verify existence if necessary. +* +* Examples: +* In: C:\PROGRA~1\MOZILL~1\FIREFOX.EXE -flag "%1" +* In: C:\PROGRA~1\MOZILL~1\FIREFOX.EXE,0 +* In: C:\PROGRA~1\MOZILL~1\FIREFOX.EXE +* In: "C:\PROGRA~1\MOZILL~1\FIREFOX.EXE" +* In: "C:\PROGRA~1\MOZILL~1\FIREFOX.EXE" -flag "%1" +* Out: C:\PROGRA~1\MOZILL~1\FIREFOX.EXE +* +* In: "C:\Program Files\Mozilla Firefox\firefox.exe" -flag "%1" +* In: C:\Program Files\Mozilla Firefox\firefox.exe,0 +* In: "C:\Program Files\Mozilla Firefox\firefox.exe" +* Out: C:\Program Files\Mozilla Firefox\firefox.exe +* +* @param _IN_PATH +* The string containing the path. +* @param _OUT_PATH +* The register to store the path to. +* +* $R7 = counter for the outer loop's EnumRegKey +* $R8 = return value from ReadRegStr +* $R9 = _IN_PATH and _OUT_PATH +*/ +!macro GetPathFromString + + !ifndef ${_MOZFUNC_UN}GetPathFromString + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}GetPathFromString "!insertmacro ${_MOZFUNC_UN}GetPathFromStringCall" + + Function ${_MOZFUNC_UN}GetPathFromString + Exch $R9 + Push $R8 + Push $R7 + + StrCpy $R7 0 ; Set the counter to 0. + + ; Handle quoted paths with arguments. + StrCpy $R8 $R9 1 ; Copy the first char. + StrCmp $R8 '"' +2 +1 ; Is it a "? + StrCmp $R8 "'" +1 +9 ; Is it a '? + StrCpy $R9 $R9 "" 1 ; Remove the first char. + IntOp $R7 $R7 + 1 ; Increment the counter. + StrCpy $R8 $R9 1 $R7 ; Starting from the counter copy the next char. + StrCmp $R8 "" end +1 ; Are there no more chars? + StrCmp $R8 '"' +2 +1 ; Is it a " char? + StrCmp $R8 "'" +1 -4 ; Is it a ' char? + StrCpy $R9 $R9 $R7 ; Copy chars up to the counter. + GoTo end + + ; Handle DefaultIcon paths. DefaultIcon paths are not quoted and end with + ; a , and a number. + IntOp $R7 $R7 - 1 ; Decrement the counter. + StrCpy $R8 $R9 1 $R7 ; Copy one char from the end minus the counter. + StrCmp $R8 '' +4 +1 ; Are there no more chars? + StrCmp $R8 ',' +1 -3 ; Is it a , char? + StrCpy $R9 $R9 $R7 ; Copy chars up to the end minus the counter. + GoTo end + + ; Handle unquoted paths with arguments. An unquoted path with arguments + ; must be an 8dot3 path. + StrCpy $R7 -1 ; Set the counter to -1 so it will start at 0. + IntOp $R7 $R7 + 1 ; Increment the counter. + StrCpy $R8 $R9 1 $R7 ; Starting from the counter copy the next char. + StrCmp $R8 "" end +1 ; Are there no more chars? + StrCmp $R8 " " +1 -3 ; Is it a space char? + StrCpy $R9 $R9 $R7 ; Copy chars up to the counter. + + end: + ClearErrors + + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro GetPathFromStringCall _IN_PATH _OUT_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_IN_PATH}" + Call GetPathFromString + Pop ${_OUT_PATH} + !verbose pop +!macroend + +!macro un.GetPathFromStringCall _IN_PATH _OUT_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_IN_PATH}" + Call un.GetPathFromString + Pop ${_OUT_PATH} + !verbose pop +!macroend + +!macro un.GetPathFromString + !ifndef un.GetPathFromString + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro GetPathFromString + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Removes the quotes from each end of a string if present. + * + * @param _IN_PATH + * The string containing the path. + * @param _OUT_PATH + * The register to store the long path. + * + * $R7 = storage for single character comparison + * $R8 = storage for _IN_PATH + * $R9 = _IN_PATH and _OUT_PATH + */ +!macro RemoveQuotesFromPath + + !ifndef ${_MOZFUNC_UN}RemoveQuotesFromPath + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}RemoveQuotesFromPath "!insertmacro ${_MOZFUNC_UN}RemoveQuotesFromPathCall" + + Function ${_MOZFUNC_UN}RemoveQuotesFromPath + Exch $R9 + Push $R8 + Push $R7 + + StrCpy $R7 "$R9" 1 + StrCmp $R7 "$\"" +1 +2 + StrCpy $R9 "$R9" "" 1 + + StrCpy $R7 "$R9" "" -1 + StrCmp $R7 "$\"" +1 +2 + StrCpy $R9 "$R9" -1 + + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro RemoveQuotesFromPathCall _IN_PATH _OUT_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_IN_PATH}" + Call RemoveQuotesFromPath + Pop ${_OUT_PATH} + !verbose pop +!macroend + +!macro un.RemoveQuotesFromPathCall _IN_PATH _OUT_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_IN_PATH}" + Call un.RemoveQuotesFromPath + Pop ${_OUT_PATH} + !verbose pop +!macroend + +!macro un.RemoveQuotesFromPath + !ifndef un.RemoveQuotesFromPath + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro RemoveQuotesFromPath + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Returns the long path for an existing file or directory. GetLongPathNameW + * may not be available on Win95 if Microsoft Layer for Unicode is not + * installed and GetFullPathName only returns a long path for the last file or + * directory that doesn't end with a \ in the path that it is passed. If the + * path does not exist on the file system this will return an empty string. To + * provide a consistent result trailing back-slashes are always removed. + * + * Note: 1024 used by GetLongPathNameW is the maximum NSIS string length. + * + * @param _IN_PATH + * The string containing the path. + * @param _OUT_PATH + * The register to store the long path. + * + * $R4 = counter value when the previous \ was found + * $R5 = directory or file name found during loop + * $R6 = return value from GetLongPathNameW and loop counter + * $R7 = long path from GetLongPathNameW and single char from path for comparison + * $R8 = storage for _IN_PATH + * $R9 = _IN_PATH _OUT_PATH + */ +!macro GetLongPath + + !ifndef ${_MOZFUNC_UN}GetLongPath + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}GetLongPath "!insertmacro ${_MOZFUNC_UN}GetLongPathCall" + + Function ${_MOZFUNC_UN}GetLongPath + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + + ClearErrors + + GetFullPathName $R8 "$R9" + IfErrors end_GetLongPath +1 ; If the path doesn't exist return an empty string. + + ; Make the drive letter uppercase. + StrCpy $R9 "$R8" 1 ; Copy the first char. + StrCpy $R8 "$R8" "" 1 ; Copy everything after the first char. + ; Convert the first char to uppercase. + System::Call "User32::CharUpper(w R9 R9)i" + StrCpy $R8 "$R9$R8" ; Copy the uppercase char and the rest of the chars. + + ; Do it the hard way. + StrCpy $R4 0 ; Stores the position in the string of the last \ found. + StrCpy $R6 -1 ; Set the counter to -1 so it will start at 0. + + loop_GetLongPath: + IntOp $R6 $R6 + 1 ; Increment the counter. + StrCpy $R7 $R8 1 $R6 ; Starting from the counter copy the next char. + StrCmp $R7 "" +2 +1 ; Are there no more chars? + StrCmp $R7 "\" +1 -3 ; Is it a \? + + ; Copy chars starting from the previously found \ to the counter. + StrCpy $R5 $R8 $R6 $R4 + + ; If this is the first \ found we want to swap R9 with R5 so a \ will + ; be appended to the drive letter and colon (e.g. C: will become C:\). + StrCmp $R4 0 +1 +3 + StrCpy $R9 $R5 + StrCpy $R5 "" + + GetFullPathName $R9 "$R9\$R5" + + StrCmp $R7 "" end_GetLongPath +1 ; Are there no more chars? + + ; Store the counter for the current \ and prefix it for StrCpy operations. + StrCpy $R4 "+$R6" + IntOp $R6 $R6 + 1 ; Increment the counter so we skip over the \. + StrCpy $R8 $R8 "" $R6 ; Copy chars starting from the counter to the end. + StrCpy $R6 -1 ; Reset the counter to -1 so it will start over at 0. + GoTo loop_GetLongPath + + end_GetLongPath: + ; If there is a trailing slash remove it + StrCmp $R9 "" +4 +1 + StrCpy $R8 "$R9" "" -1 + StrCmp $R8 "\" +1 +2 + StrCpy $R9 "$R9" -1 + + ClearErrors + + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro GetLongPathCall _IN_PATH _OUT_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_IN_PATH}" + Call GetLongPath + Pop ${_OUT_PATH} + !verbose pop +!macroend + +!macro un.GetLongPathCall _IN_PATH _OUT_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_IN_PATH}" + Call un.GetLongPath + Pop ${_OUT_PATH} + !verbose pop +!macroend + +!macro un.GetLongPath + !ifndef un.GetLongPath + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro GetLongPath + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + + +################################################################################ +# Macros for cleaning up the registry and file system + +/** + * Removes registry keys that reference this install location and for paths that + * no longer exist. This uses SHCTX to determine the registry hive so you must + * call SetShellVarContext first. + * + * @param _KEY + * The registry subkey (typically this will be Software\Mozilla). + * + * $0 = loop counter + * $1 = temporary value used for string searches + * $R0 = on x64 systems set to 'false' at the beginning of the macro when + * enumerating the x86 registry view and set to 'true' when enumerating + * the x64 registry view. + * $R1 = stores the long path to $INSTDIR + * $R2 = return value from the stack from the GetParent and GetLongPath macros + * $R3 = return value from the outer loop's EnumRegKey and ESR string + * $R4 = return value from the inner loop's EnumRegKey + * $R5 = return value from ReadRegStr + * $R6 = counter for the outer loop's EnumRegKey + * $R7 = counter for the inner loop's EnumRegKey + * $R8 = return value from the stack from the RemoveQuotesFromPath macro + * $R9 = _KEY + */ +!macro RegCleanMain + + !ifndef ${_MOZFUNC_UN}RegCleanMain + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}RegCleanMain "!insertmacro ${_MOZFUNC_UN}RegCleanMainCall" + + Function ${_MOZFUNC_UN}RegCleanMain + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + Push $R2 + Push $R1 + Push $R0 + Push $0 + Push $1 + + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R1 + StrCpy $R6 0 ; set the counter for the outer loop to 0 + + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + StrCpy $R0 "false" + ; Set the registry to the 32 bit registry for 64 bit installations or to + ; the 64 bit registry for 32 bit installations at the beginning so it can + ; easily be set back to the correct registry view when finished. + !ifdef HAVE_64BIT_BUILD + SetRegView 32 + !else + SetRegView 64 + !endif + ${EndIf} + + outerloop: + EnumRegKey $R3 SHCTX $R9 $R6 + StrCmp $R3 "" end +1 ; if empty there are no more keys to enumerate + IntOp $R6 $R6 + 1 ; increment the outer loop's counter + ClearErrors + ReadRegStr $R5 SHCTX "$R9\$R3\bin" "PathToExe" + IfErrors 0 outercontinue + StrCpy $R7 0 ; set the counter for the inner loop to 0 + + innerloop: + EnumRegKey $R4 SHCTX "$R9\$R3" $R7 + StrCmp $R4 "" outerloop +1 ; if empty there are no more keys to enumerate + IntOp $R7 $R7 + 1 ; increment the inner loop's counter + ClearErrors + ReadRegStr $R5 SHCTX "$R9\$R3\$R4\Main" "PathToExe" + IfErrors innerloop + + ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R5" $R8 + ${${_MOZFUNC_UN}GetParent} "$R8" $R2 + ${${_MOZFUNC_UN}GetLongPath} "$R2" $R2 + IfFileExists "$R2" +1 innerloop + StrCmp "$R2" "$R1" +1 innerloop + + ClearErrors + DeleteRegKey SHCTX "$R9\$R3\$R4" + IfErrors innerloop + IntOp $R7 $R7 - 1 ; decrement the inner loop's counter when the key is deleted successfully. + ClearErrors + DeleteRegKey /ifempty SHCTX "$R9\$R3" + IfErrors innerloop outerdecrement + + outercontinue: + ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R5" $R8 + ${${_MOZFUNC_UN}GetParent} "$R8" $R2 + ${${_MOZFUNC_UN}GetLongPath} "$R2" $R2 + IfFileExists "$R2" +1 outerloop + StrCmp "$R2" "$R1" +1 outerloop + + ClearErrors + DeleteRegKey SHCTX "$R9\$R3" + IfErrors outerloop + + outerdecrement: + IntOp $R6 $R6 - 1 ; decrement the outer loop's counter when the key is deleted successfully. + GoTo outerloop + + end: + ; Check if _KEY\${BrandFullNameInternal} refers to a key that's been + ; removed, either just now by this function or earlier by something else, + ; and if so either update it to a key that does exist or remove it if we + ; can't find anything to update it to. + ; We'll run this check twice, once looking for non-ESR keys and then again + ; looking specifically for the separate ESR keys. + StrCpy $R3 "" + ${For} $0 0 1 + ClearErrors + ReadRegStr $R5 SHCTX "$R9\${BrandFullNameInternal}$R3" "CurrentVersion" + ${IfNot} ${Errors} + ReadRegStr $R5 SHCTX "$R9\${BrandFullNameInternal}\$R5" "" + ${If} ${Errors} + ; Key doesn't exist, update or remove CurrentVersion and default. + StrCpy $R5 "" + StrCpy $R6 0 + EnumRegKey $R4 SHCTX "$R9\${BrandFullNameInternal}" $R6 + ${While} $R4 != "" + ClearErrors + ${WordFind} "$R4" "esr" "E#" $1 + ${If} $R3 == "" + ; The key we're looking to update is a non-ESR, so we need to + ; select only another non-ESR to update it with. + ${If} ${Errors} + StrCpy $R5 "$R4" + ${Break} + ${EndIf} + ${Else} + ; The key we're looking to update is an ESR, so we need to + ; select only another ESR to update it with. + ${IfNot} ${Errors} + StrCpy $R5 "$R4" + ${Break} + ${EndIf} + ${EndIf} + + IntOp $R6 $R6 + 1 + EnumRegKey $R4 SHCTX "$R9\${BrandFullNameInternal}" $R6 + ${EndWhile} + + ${If} $R5 == "" + ; We didn't find an install to update the key with, so delete the + ; CurrentVersion value and the entire key if it has no subkeys. + DeleteRegValue SHCTX "$R9\${BrandFullNameInternal}$R3" "CurrentVersion" + DeleteRegValue SHCTX "$R9\${BrandFullNameInternal}$R3" "" + DeleteRegKey /ifempty SHCTX "$R9\${BrandFullNameInternal}$R3" + ${Else} + ; We do have another still-existing install, so update the key to + ; that version. + WriteRegStr SHCTX "$R9\${BrandFullNameInternal}$R3" \ + "CurrentVersion" "$R5" + ${WordFind} "$R5" " " "+1{" $R5 + WriteRegStr SHCTX "$R9\${BrandFullNameInternal}$R3" "" "$R5" + ${EndIf} + ${EndIf} + ; Else, the key referenced in CurrentVersion still exists, + ; so there's nothing to update or remove. + ${EndIf} + + ; Set up for the second iteration of the loop, where we'll be looking + ; for the separate ESR keys. + StrCpy $R3 " ESR" + ${Next} + + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + ${If} "$R0" == "false" + ; Set the registry to the correct view. + !ifdef HAVE_64BIT_BUILD + SetRegView 64 + !else + SetRegView 32 + !endif + + StrCpy $R6 0 ; set the counter for the outer loop to 0 + StrCpy $R0 "true" + GoTo outerloop + ${EndIf} + ${EndIf} + + ClearErrors + + Pop $1 + Pop $0 + Pop $R0 + Pop $R1 + Pop $R2 + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro RegCleanMainCall _KEY + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call RegCleanMain + !verbose pop +!macroend + +!macro un.RegCleanMainCall _KEY + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call un.RegCleanMain + !verbose pop +!macroend + +!macro un.RegCleanMain + !ifndef un.RegCleanMain + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro RegCleanMain + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + + +/** + * Removes registry keys that reference this install location and for paths that + * no longer exist. + * + * @param _KEY ($R1) + * The registry subkey + * (typically this will be Software\Mozilla\${AppName}). + */ +!macro RegCleanPrefs + !ifndef ${_MOZFUNC_UN}RegCleanPrefs + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}RegCleanPrefs "!insertmacro ${_MOZFUNC_UN}RegCleanPrefsCall" + + Function ${_MOZFUNC_UN}RegCleanPrefs + Exch $R1 ; get _KEY from the stack + + ; These prefs are always stored in the native registry. + SetRegView 64 + + ; Delete the installer prefs key for this installation, if one exists. + DeleteRegKey HKCU "$R1\Installer\$AppUserModelID" + + ; If there aren't any more installer prefs keys, delete the parent key. + DeleteRegKey /ifempty HKCU "$R1\Installer" + + SetRegView lastused + + Pop $R1 ; restore the previous $R1 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro RegCleanPrefsCall _KEY + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call RegCleanPrefs + !verbose pop +!macroend + +!macro un.RegCleanPrefsCall _KEY + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_KEY}" + Call un.RegCleanPrefs + !verbose pop +!macroend + +!macro un.RegCleanPrefs + !ifndef un.RegCleanPrefs + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro RegCleanPrefs + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Removes all registry keys from \Software\Windows\CurrentVersion\Uninstall + * that reference this install location in both the 32 bit and 64 bit registry + * view. This macro uses SHCTX to determine the registry hive so you must call + * SetShellVarContext first. + * + * $R3 = on x64 systems set to 'false' at the beginning of the macro when + * enumerating the x86 registry view and set to 'true' when enumerating + * the x64 registry view. + * $R4 = stores the long path to $INSTDIR + * $R5 = return value from ReadRegStr + * $R6 = string for the base reg key (e.g. Software\Microsoft\Windows\CurrentVersion\Uninstall) + * $R7 = return value from EnumRegKey + * $R8 = counter for EnumRegKey + * $R9 = return value from the stack from the RemoveQuotesFromPath and GetLongPath macros + */ +!macro RegCleanUninstall + + !ifndef ${_MOZFUNC_UN}RegCleanUninstall + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}RegCleanUninstall "!insertmacro ${_MOZFUNC_UN}RegCleanUninstallCall" + + Function ${_MOZFUNC_UN}RegCleanUninstall + Push $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R4 + StrCpy $R6 "Software\Microsoft\Windows\CurrentVersion\Uninstall" + StrCpy $R7 "" + StrCpy $R8 0 + + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + StrCpy $R3 "false" + ; Set the registry to the 32 bit registry for 64 bit installations or to + ; the 64 bit registry for 32 bit installations at the beginning so it can + ; easily be set back to the correct registry view when finished. + !ifdef HAVE_64BIT_BUILD + SetRegView 32 + !else + SetRegView 64 + !endif + ${EndIf} + + loop: + EnumRegKey $R7 SHCTX $R6 $R8 + StrCmp $R7 "" end +1 + IntOp $R8 $R8 + 1 ; Increment the counter + ClearErrors + ReadRegStr $R5 SHCTX "$R6\$R7" "InstallLocation" + IfErrors loop + ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R5" $R9 + + ; Detect when the path is just a drive letter without a trailing + ; backslash (e.g., "C:"), and add a backslash. If we don't, the Win32 + ; calls in GetLongPath will interpret that syntax as a shorthand + ; for the working directory, because that's the DOS 2.0 convention, + ; and will return the path to that directory instead of just the drive. + ; Back here, we would then successfully match that with our $INSTDIR, + ; and end up deleting a registry key that isn't really ours. + StrLen $R5 "$R9" + ${If} $R5 == 2 + StrCpy $R5 "$R9" 1 1 + ${If} "$R5" == ":" + StrCpy $R9 "$R9\" + ${EndIf} + ${EndIf} + + ${${_MOZFUNC_UN}GetLongPath} "$R9" $R9 + StrCmp "$R9" "$R4" +1 loop + ClearErrors + DeleteRegKey SHCTX "$R6\$R7" + IfErrors loop +1 + IntOp $R8 $R8 - 1 ; Decrement the counter on successful deletion + GoTo loop + + end: + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + ${If} "$R3" == "false" + ; Set the registry to the correct view. + !ifdef HAVE_64BIT_BUILD + SetRegView 64 + !else + SetRegView 32 + !endif + + StrCpy $R7 "" + StrCpy $R8 0 + StrCpy $R3 "true" + GoTo loop + ${EndIf} + ${EndIf} + + ClearErrors + + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Pop $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro RegCleanUninstallCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call RegCleanUninstall + !verbose pop +!macroend + +!macro un.RegCleanUninstallCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.RegCleanUninstall + !verbose pop +!macroend + +!macro un.RegCleanUninstall + !ifndef un.RegCleanUninstall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro RegCleanUninstall + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Removes an application specific handler registry key under Software\Classes + * for both HKCU and HKLM when its open command refers to this install + * location or the install location doesn't exist. + * + * @param _HANDLER_NAME + * The registry name for the handler. + * + * $R7 = stores the long path to the $INSTDIR + * $R8 = stores the path to the open command's parent directory + * $R9 = _HANDLER_NAME + */ +!macro RegCleanAppHandler + + !ifndef ${_MOZFUNC_UN}RegCleanAppHandler + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}GetPathFromString + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}RegCleanAppHandler "!insertmacro ${_MOZFUNC_UN}RegCleanAppHandlerCall" + + Function ${_MOZFUNC_UN}RegCleanAppHandler + Exch $R9 + Push $R8 + Push $R7 + + ClearErrors + ReadRegStr $R8 HKCU "Software\Classes\$R9\shell\open\command" "" + IfErrors next +1 + ${${_MOZFUNC_UN}GetPathFromString} "$R8" $R8 + ${${_MOZFUNC_UN}GetParent} "$R8" $R8 + IfFileExists "$R8" +3 +1 + DeleteRegKey HKCU "Software\Classes\$R9" + GoTo next + + ${${_MOZFUNC_UN}GetLongPath} "$R8" $R8 + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R7 + StrCmp "$R7" "$R8" +1 next + DeleteRegKey HKCU "Software\Classes\$R9" + + next: + ReadRegStr $R8 HKLM "Software\Classes\$R9\shell\open\command" "" + IfErrors end + ${${_MOZFUNC_UN}GetPathFromString} "$R8" $R8 + ${${_MOZFUNC_UN}GetParent} "$R8" $R8 + IfFileExists "$R8" +3 +1 + DeleteRegKey HKLM "Software\Classes\$R9" + GoTo end + + ${${_MOZFUNC_UN}GetLongPath} "$R8" $R8 + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R7 + StrCmp "$R7" "$R8" +1 end + DeleteRegKey HKLM "Software\Classes\$R9" + + end: + + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro RegCleanAppHandlerCall _HANDLER_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_HANDLER_NAME}" + Call RegCleanAppHandler + !verbose pop +!macroend + +!macro un.RegCleanAppHandlerCall _HANDLER_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_HANDLER_NAME}" + Call un.RegCleanAppHandler + !verbose pop +!macroend + +!macro un.RegCleanAppHandler + !ifndef un.RegCleanAppHandler + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro RegCleanAppHandler + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Cleans up the registry for a protocol handler when its open command + * refers to this install location. For HKCU the registry key is deleted + * and for HKLM the values set by the application are deleted. + * + * @param _HANDLER_NAME + * The registry name for the handler. + * + * $R7 = stores the long path to $INSTDIR + * $R8 = stores the the long path to the open command's parent directory + * $R9 = _HANDLER_NAME + */ +!macro un.RegCleanProtocolHandler + + !ifndef un.RegCleanProtocolHandler + !insertmacro un.GetLongPath + !insertmacro un.GetParent + !insertmacro un.GetPathFromString + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define un.RegCleanProtocolHandler "!insertmacro un.RegCleanProtocolHandlerCall" + + Function un.RegCleanProtocolHandler + Exch $R9 + Push $R8 + Push $R7 + + ReadRegStr $R8 HKCU "Software\Classes\$R9\shell\open\command" "" + ${un.GetLongPath} "$INSTDIR" $R7 + + StrCmp "$R8" "" next +1 + ${un.GetPathFromString} "$R8" $R8 + ${un.GetParent} "$R8" $R8 + ${un.GetLongPath} "$R8" $R8 + StrCmp "$R7" "$R8" +1 next + DeleteRegKey HKCU "Software\Classes\$R9" + + next: + ReadRegStr $R8 HKLM "Software\Classes\$R9\shell\open\command" "" + StrCmp "$R8" "" end +1 + ${un.GetLongPath} "$INSTDIR" $R7 + ${un.GetPathFromString} "$R8" $R8 + ${un.GetParent} "$R8" $R8 + ${un.GetLongPath} "$R8" $R8 + StrCmp "$R7" "$R8" +1 end + DeleteRegValue HKLM "Software\Classes\$R9\DefaultIcon" "" + DeleteRegValue HKLM "Software\Classes\$R9\shell\open" "" + DeleteRegValue HKLM "Software\Classes\$R9\shell\open\command" "" + DeleteRegValue HKLM "Software\Classes\$R9\shell\ddeexec" "" + DeleteRegValue HKLM "Software\Classes\$R9\shell\ddeexec\Application" "" + DeleteRegValue HKLM "Software\Classes\$R9\shell\ddeexec\Topic" "" + + end: + + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro un.RegCleanProtocolHandlerCall _HANDLER_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_HANDLER_NAME}" + Call un.RegCleanProtocolHandler + !verbose pop +!macroend + +/** + * Cleans up the registry for a file handler when the passed in value equals + * the default value for the file handler. For HKCU the registry key is deleted + * and for HKLM the default value is deleted. + * + * @param _HANDLER_NAME + * The registry name for the handler. + * @param _DEFAULT_VALUE + * The value to check for against the handler's default value. + * + * $R6 = stores the long path to $INSTDIR + * $R7 = _DEFAULT_VALUE + * $R9 = _HANDLER_NAME + */ +!macro RegCleanFileHandler + + !ifndef ${_MOZFUNC_UN}RegCleanFileHandler + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}GetPathFromString + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}RegCleanFileHandler "!insertmacro ${_MOZFUNC_UN}RegCleanFileHandlerCall" + + Function ${_MOZFUNC_UN}RegCleanFileHandler + Exch $R9 + Exch 1 + Exch $R8 + Push $R7 + + DeleteRegValue HKCU "Software\Classes\$R9\OpenWithProgids" $R8 + EnumRegValue $R7 HKCU "Software\Classes\$R9\OpenWithProgids" 0 + StrCmp "$R7" "" +1 +2 + DeleteRegKey HKCU "Software\Classes\$R9\OpenWithProgids" + ReadRegStr $R7 HKCU "Software\Classes\$R9" "" + StrCmp "$R7" "$R8" +1 +2 + DeleteRegKey HKCU "Software\Classes\$R9" + + DeleteRegValue HKLM "Software\Classes\$R9\OpenWithProgids" $R8 + EnumRegValue $R7 HKLM "Software\Classes\$R9\OpenWithProgids" 0 + StrCmp "$R7" "" +1 +2 + DeleteRegKey HKLM "Software\Classes\$R9\OpenWithProgids" + ReadRegStr $R7 HKLM "Software\Classes\$R9" "" + StrCmp "$R7" "$R8" +1 +2 + DeleteRegValue HKLM "Software\Classes\$R9" "" + + ClearErrors + + Pop $R7 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro RegCleanFileHandlerCall _HANDLER_NAME _DEFAULT_VALUE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_DEFAULT_VALUE}" + Push "${_HANDLER_NAME}" + Call RegCleanFileHandler + !verbose pop +!macroend + +!macro un.RegCleanFileHandlerCall _HANDLER_NAME _DEFAULT_VALUE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_DEFAULT_VALUE}" + Push "${_HANDLER_NAME}" + Call un.RegCleanFileHandler + !verbose pop +!macroend + +!macro un.RegCleanFileHandler + !ifndef un.RegCleanFileHandler + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro RegCleanFileHandler + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Checks if a handler's open command points to this installation directory. + * Uses SHCTX to determine the registry hive (e.g. HKLM or HKCU) to check. + * + * @param _HANDLER_NAME + * The registry name for the handler. + * @param _RESULT + * true if it is the handler's open command points to this + * installation directory and false if it does not. + * + * $R7 = stores the value of the open command and the path macros return values + * $R8 = stores the handler's registry key name + * $R9 = _DEFAULT_VALUE and _RESULT + */ +!macro IsHandlerForInstallDir + + !ifndef ${_MOZFUNC_UN}IsHandlerForInstallDir + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}GetPathFromString + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}IsHandlerForInstallDir "!insertmacro ${_MOZFUNC_UN}IsHandlerForInstallDirCall" + + Function ${_MOZFUNC_UN}IsHandlerForInstallDir + Exch $R9 + Push $R8 + Push $R7 + + StrCpy $R8 "$R9" + StrCpy $R9 "false" + ReadRegStr $R7 SHCTX "Software\Classes\$R8\shell\open\command" "" + + ${If} $R7 != "" + ${GetPathFromString} "$R7" $R7 + ${GetParent} "$R7" $R7 + ${GetLongPath} "$R7" $R7 + ${If} $R7 == $INSTDIR + StrCpy $R9 "true" + ${EndIf} + ${EndIf} + + ClearErrors + + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro IsHandlerForInstallDirCall _HANDLER_NAME _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_HANDLER_NAME}" + Call IsHandlerForInstallDir + Pop "${_RESULT}" + !verbose pop +!macroend + +!macro un.IsHandlerForInstallDirCall _HANDLER_NAME _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_HANDLER_NAME}" + Call un.IsHandlerForInstallDir + Pop "${_RESULT}" + !verbose pop +!macroend + +!macro un.IsHandlerForInstallDir + !ifndef un.IsHandlerForInstallDir + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro IsHandlerForInstallDir + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Removes the application's VirtualStore directory if present when the + * installation directory is a sub-directory of the program files directory. + * + * $R3 = Local App Data dir + * $R4 = $PROGRAMFILES/$PROGRAMFILES64 for CleanVirtualStore_Internal + * $R5 = various path values. + * $R6 = length of the long path to $PROGRAMFILES32 or $PROGRAMFILES64 + * $R7 = long path to $PROGRAMFILES32 or $PROGRAMFILES64 + * $R8 = length of the long path to $INSTDIR + * $R9 = long path to $INSTDIR + */ +!macro CleanVirtualStore + + !ifndef ${_MOZFUNC_UN}CleanVirtualStore + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}CleanVirtualStore "!insertmacro ${_MOZFUNC_UN}CleanVirtualStoreCall" + + Function ${_MOZFUNC_UN}CleanVirtualStore + Push $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R9 + ${If} "$R9" != "" + StrLen $R8 "$R9" + + StrCpy $R4 $PROGRAMFILES32 + Call ${_MOZFUNC_UN}CleanVirtualStore_Internal + + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + StrCpy $R4 $PROGRAMFILES64 + Call ${_MOZFUNC_UN}CleanVirtualStore_Internal + ${EndIf} + + ${EndIf} + + ClearErrors + + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Pop $R9 + FunctionEnd + + Function ${_MOZFUNC_UN}CleanVirtualStore_Internal + ${${_MOZFUNC_UN}GetLongPath} "" $R7 + ${If} "$R7" != "" + StrLen $R6 "$R7" + ${If} $R8 < $R6 + ; Copy from the start of $INSTDIR the length of $PROGRAMFILES64 + StrCpy $R5 "$R9" $R6 + ${If} "$R5" == "$R7" + ; Remove the drive letter and colon from the $INSTDIR long path + StrCpy $R5 "$R9" "" 2 + ${GetLocalAppDataFolder} $R3 + StrCpy $R5 "$R3\VirtualStore$R5" + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ${If} "$R5" != "" + ${AndIf} ${FileExists} "$R5" + RmDir /r "$R5" + ${EndIf} + ${EndIf} + ${EndIf} + ${EndIf} + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CleanVirtualStoreCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call CleanVirtualStore + !verbose pop +!macroend + +!macro un.CleanVirtualStoreCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.CleanVirtualStore + !verbose pop +!macroend + +!macro un.CleanVirtualStore + !ifndef un.CleanVirtualStore + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro CleanVirtualStore + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Cleans up some old logs in the Maintenance Service directory. + * + * @param _OLD_REL_PATH + * The relative path to the profile directory from Local AppData. + * Calculated for the old update directory not based on a hash. + * + * $R7 = _OLD_REL_PATH + * $R1 = taskBar ID hash located in registry at SOFTWARE\_OLD_REL_PATH\TaskBarIDs + * $R6 = long path to $INSTDIR + */ +!macro CleanMaintenanceServiceLogs + + !ifndef ${_MOZFUNC_UN}CleanMaintenanceServiceLogs + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetCommonDirectory + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}CleanMaintenanceServiceLogs "!insertmacro ${_MOZFUNC_UN}CleanMaintenanceServiceLogsCall" + + Function ${_MOZFUNC_UN}CleanMaintenanceServiceLogs + Exch $R7 + Push $R6 + Push $R1 + + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R6 + + ${If} $R7 != "" ; _OLD_REL_PATH was passed + ${AndIf} $R6 != "" ; We have the install dir path + ; Get the taskbar ID hash for this installation path + ReadRegStr $R1 HKLM "SOFTWARE\$R7\TaskBarIDs" $R6 + ${If} $R1 == "" + ReadRegStr $R1 HKCU "SOFTWARE\$R7\TaskBarIDs" $R6 + ${EndIf} + + ${If} $R1 != "" + ; Remove the secure log files that our updater may have created + ; inside the maintenance service path. There are several files named + ; with the install hash and an extension indicating the kind of file. + ; so use a wildcard to delete them all. + Delete "$PROGRAMFILES32\Mozilla Maintenance Service\UpdateLogs\$R1.*" + + ; If the UpdateLogs directory is now empty, then delete it. + ; The Maintenance Service uninstaller should do this, but it may not + ; be up to date enough because of bug 1665193, so doing this here as + ; well lets us make sure it really happens. + RmDir "$PROGRAMFILES32\Mozilla Maintenance Service\UpdateLogs" + ${EndIf} + ${EndIf} + + ClearErrors + + Pop $R1 + Pop $R6 + Exch $R7 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CleanMaintenanceServiceLogsCall _OLD_REL_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_OLD_REL_PATH}" + Call CleanMaintenanceServiceLogs + !verbose pop +!macroend + +!macro un.CleanMaintenanceServiceLogsCall _OLD_REL_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_OLD_REL_PATH}" + Call un.CleanMaintenanceServiceLogs + !verbose pop +!macroend + +!macro un.CleanMaintenanceServiceLogs + !ifndef un.CleanMaintenanceServiceLogs + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro CleanMaintenanceServiceLogs + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Removes relevant shortcuts from a given shortcuts log. + * + * $R3 = return value from ShellLink::GetShortCutWorkingDirectory + * $R4 = counter for appending to Shortcut for enumerating the ini file entries + * $R5 = return value from ShellLink::GetShortCutTarget + * $R6 = the long path to the Start Menu Programs directory (e.g. $SMPROGRAMS) + * $R7 = the return value from ReadINIStr for the relative path to the applications + * directory under the Start Menu Programs directory and the long path to this + * directory + * $R8 = the return value from ReadINIStr for enumerating shortcuts + * $R9 = [in] long path to the shortcuts ini file + */ +!macro DeleteShortcutsFromLog + !ifndef ${_MOZFUNC_UN}DeleteShortcutsFromLog + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}GetCommonDirectory + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}DeleteShortcutsFromLog "!insertmacro ${_MOZFUNC_UN}DeleteShortcutsFromLogCall" + + Function ${_MOZFUNC_UN}DeleteShortcutsFromLog + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + + ${If} ${FileExists} "$R9" + ; Delete Start Menu shortcuts for this application + StrCpy $R4 -1 + ${Do} + IntOp $R4 $R4 + 1 ; Increment the counter + ClearErrors + ReadINIStr $R8 "$R9" "STARTMENU" "Shortcut$R4" + ${If} ${Errors} + ${ExitDo} + ${EndIf} + + ${If} ${FileExists} "$SMPROGRAMS\$R8" + ShellLink::GetShortCutTarget "$SMPROGRAMS\$R8" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ; Shortcuts created outside of the installer may + ; have metadata set on them such that GetShortCutTarget + ; returns the wrong path on 64-bit systems. + ; GetShortCutWorkingDirectory is not _quite_ as good + ; of a test (because it may be unset), but it's not + ; subject to this bug, and is therefore the next best thing. + ; https://phabricator.services.mozilla.com/D138197 + ; contains some additional background on this. + ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8" + Pop $R3 + ${If} "$INSTDIR\${FileMainEXE}" == "$R5" + ${OrIf} "$INSTDIR" == "$R3" + Delete "$SMPROGRAMS\$R8" + ${EndIf} + ${EndIf} + ${Loop} + ; There might also be a shortcut with a different name created by a + ; previous version of the installer. + ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk" + ShellLink::GetShortCutTarget "$SMPROGRAMS\${BrandFullName}.lnk" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8" + Pop $R3 + ${If} "$INSTDIR\${FileMainEXE}" == "$R5" + ${OrIf} "$INSTDIR" == "$R3" + Delete "$SMPROGRAMS\${BrandFullName}.lnk" + ${EndIf} + ${EndIf} + + ; Delete Quick Launch shortcuts for this application + StrCpy $R4 -1 + ${Do} + IntOp $R4 $R4 + 1 ; Increment the counter + ClearErrors + ReadINIStr $R8 "$R9" "QUICKLAUNCH" "Shortcut$R4" + ${If} ${Errors} + ${ExitDo} + ${EndIf} + + ${If} ${FileExists} "$QUICKLAUNCH\$R8" + ShellLink::GetShortCutTarget "$QUICKLAUNCH\$R8" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8" + Pop $R3 + ${If} "$INSTDIR\${FileMainEXE}" == "$R5" + ${OrIf} "$INSTDIR" == "$R3" + Delete "$QUICKLAUNCH\$R8" + ${EndIf} + ${EndIf} + ${Loop} + ; There might also be a shortcut with a different name created by a + ; previous version of the installer. + ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk" + ShellLink::GetShortCutTarget "$QUICKLAUNCH\${BrandFullName}.lnk" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8" + Pop $R3 + ${If} "$INSTDIR\${FileMainEXE}" == "$R5" + ${OrIf} "$INSTDIR" == "$R3" + Delete "$QUICKLAUNCH\${BrandFullName}.lnk" + ${EndIf} + ${EndIf} + + ; Delete Desktop shortcuts for this application + StrCpy $R4 -1 + ${Do} + IntOp $R4 $R4 + 1 ; Increment the counter + ClearErrors + ReadINIStr $R8 "$R9" "DESKTOP" "Shortcut$R4" + ${If} ${Errors} + ${ExitDo} + ${EndIf} + + ${If} ${FileExists} "$DESKTOP\$R8" + ShellLink::GetShortCutTarget "$DESKTOP\$R8" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8" + Pop $R3 + ${If} "$INSTDIR\${FileMainEXE}" == "$R5" + ${OrIf} "$INSTDIR" == "$R3" + Delete "$DESKTOP\$R8" + ${EndIf} + ${EndIf} + ${Loop} + ; There might also be a shortcut with a different name created by a + ; previous version of the installer. + ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk" + ShellLink::GetShortCutTarget "$DESKTOP\${BrandFullName}.lnk" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ; We don't bother checking working directory here because we know + ; these shortcuts were only created by the installer, and thus + ; not subject to the bug described above. + ${If} "$INSTDIR\${FileMainEXE}" == "$R5" + Delete "$DESKTOP\${BrandFullName}.lnk" + ${EndIf} + ${EndIf} + + ${${_MOZFUNC_UN}GetLongPath} "$SMPROGRAMS" $R6 + + ; Delete Start Menu Programs shortcuts for this application + ClearErrors + ReadINIStr $R7 "$R9" "SMPROGRAMS" "RelativePathToDir" + ${${_MOZFUNC_UN}GetLongPath} "$R6\$R7" $R7 + ${Unless} "$R7" == "" + StrCpy $R4 -1 + ${Do} + IntOp $R4 $R4 + 1 ; Increment the counter + ClearErrors + ReadINIStr $R8 "$R9" "SMPROGRAMS" "Shortcut$R4" + ${If} ${Errors} + ${ExitDo} + ${EndIf} + + ${If} ${FileExists} "$R7\$R8" + ShellLink::GetShortCutTarget "$R7\$R8" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ${If} "$INSTDIR\${FileMainEXE}" == "$R5" + Delete "$R7\$R8" + ${EndIf} + ${EndIf} + ${Loop} + + ; Delete Start Menu Programs directories for this application + ${Do} + ClearErrors + ${If} "$R6" == "$R7" + ${ExitDo} + ${EndIf} + RmDir "$R7" + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${${_MOZFUNC_UN}GetParent} "$R7" $R7 + ${Loop} + ${EndUnless} + ${EndIf} + + ClearErrors + + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro DeleteShortcutsFromLogCall _SHORTCUTS_LOG + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push ${_SHORTCUTS_LOG} + Call DeleteShortcutsFromLog + !verbose pop +!macroend + +!macro un.DeleteShortcutsFromLogCall _SHORTCUTS_LOG + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push ${_SHORTCUTS_LOG} + Call un.DeleteShortcutsFromLog + !verbose pop +!macroend + +!macro un.DeleteShortcutsFromLog + !ifndef un.DeleteShortcutsFromLog + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro DeleteShortcutsFromLog + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Deletes shortcuts and Start Menu directories under Programs as specified by + * the shortcuts log ini files and on Windows 7 unpins TaskBar and Start Menu + * shortcuts. The shortcuts will not be deleted if the shortcut target isn't for + * this install location which is determined by the shortcut having a target of + * $INSTDIR\${FileMainEXE} _or_ the working directory of the shortcut being + * equal to $INSTDIR. The context (All Users or Current User) of the + * $DESKTOP and $SMPROGRAMS constants depends on the + * SetShellVarContext setting and must be set by the caller of this macro. There + * is no All Users context for $QUICKLAUNCH but this will not cause a problem + * since the macro will just continue past the $QUICKLAUNCH shortcut deletion + * section on subsequent calls. + * + * The meat of this is farmed out to DeleteShortcutsFromLog to facilitate + * the processing of multiple shortcuts logs. + * + * The ini file sections must have the following format (the order of the + * sections in the ini file is not important): + * [SMPROGRAMS] -- this section is optional and largely irrelevant these days + * ; RelativePath is the directory relative from the Start Menu + * ; Programs directory. + * RelativePath=Mozilla App + * ; Shortcut1 is the first shortcut, Shortcut2 is the second shortcut, and so + * ; on. There must not be a break in the sequence of the numbers. + * Shortcut1=Mozilla App.lnk + * Shortcut2=Mozilla App (Safe Mode).lnk + * [DESKTOP] + * ; Shortcut1 is the first shortcut, Shortcut2 is the second shortcut, and so + * ; on. There must not be a break in the sequence of the numbers. + * Shortcut1=Mozilla App.lnk + * Shortcut2=Mozilla App (Safe Mode).lnk + * [QUICKLAUNCH] + * ; Shortcut1 is the first shortcut, Shortcut2 is the second shortcut, and so + * ; on. There must not be a break in the sequence of the numbers for the + * ; suffix. + * Shortcut1=Mozilla App.lnk + * Shortcut2=Mozilla App (Safe Mode).lnk + * [STARTMENU] + * ; Shortcut1 is the first shortcut, Shortcut2 is the second shortcut, and so + * ; on. There must not be a break in the sequence of the numbers for the + * ; suffix. + * Shortcut1=Mozilla App.lnk + * Shortcut2=Mozilla App (Safe Mode).lnk + * + * $R4 = return value from ShellLink::GetShortCutWorkingDirectory + * $R5 = return value from ShellLink::GetShortCutTarget and + * ApplicationID::UninstallPinnedItem + * $R6 = find handle + * $R7 = path to the $QUICKLAUNCH\User Pinned directory + * $R8 = return filename from FindFirst / FindNext + * $R9 = long path to the shortcut log ini file and path to the ProgramData + * directory that the application stores additional shortcut logs in + * (Typically c:\ProgramData\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38) + */ +!macro DeleteShortcuts + + !ifndef ${_MOZFUNC_UN}DeleteShortcuts + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}DeleteShortcutsFromLog + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}DeleteShortcuts "!insertmacro ${_MOZFUNC_UN}DeleteShortcutsCall" + + Function ${_MOZFUNC_UN}DeleteShortcuts + Push $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + + ; We call ApplicationID::UninstallPinnedItem once per shortcut here + ; (and explicitly not in DeleteShortcutsFromLog). Calling it again later + ; would remove the association of side by side installations. + ; Since shortcuts that are pinned can later be removed without removing + ; the pinned shortcut unpin the pinned shortcuts for the application's + ; main exe using the pinned shortcuts themselves. + StrCpy $R7 "$QUICKLAUNCH\User Pinned" + + ${If} ${FileExists} "$R7\TaskBar" + ; Delete TaskBar pinned shortcuts for the application's main exe + FindFirst $R6 $R8 "$R7\TaskBar\*.lnk" + ${Do} + ${If} ${FileExists} "$R7\TaskBar\$R8" + ShellLink::GetShortCutTarget "$R7\TaskBar\$R8" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8" + Pop $R4 + ${If} "$R5" == "$INSTDIR\${FileMainEXE}" + ${OrIf} "$R4" == "$INSTDIR" + ApplicationID::UninstallPinnedItem "$R7\TaskBar\$R8" + Pop $R5 + ${EndIf} + ${EndIf} + ClearErrors + FindNext $R6 $R8 + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${Loop} + FindClose $R6 + ${EndIf} + + ${If} ${FileExists} "$R7\StartMenu" + ; Delete Start Menu pinned shortcuts for the application's main exe + FindFirst $R6 $R8 "$R7\StartMenu\*.lnk" + ${Do} + ${If} ${FileExists} "$R7\StartMenu\$R8" + ShellLink::GetShortCutTarget "$R7\StartMenu\$R8" + Pop $R5 + ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5 + ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8" + Pop $R4 + ${If} "$R5" == "$INSTDIR\${FileMainEXE}" + ${OrIf} "$R4" == "$INSTDIR" + ApplicationID::UninstallPinnedItem "$R7\StartMenu\$R8" + Pop $R5 + ${EndIf} + ${EndIf} + ClearErrors + FindNext $R6 $R8 + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${Loop} + FindClose $R6 + ${EndIf} + + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" $R9 + ${${_MOZFUNC_UN}DeleteShortcutsFromLog} $R9 + + ${${_MOZFUNC_UN}GetCommonDirectory} $R9 + ; Shortcut logs created by an application are in a different directory, + ; and named after both the application and the user SID, eg: + ; Firefox_S-1-5-21-1004336348-1177238915-682003330-512_shortcuts.ini + FindFirst $R6 $R8 "$R9\*_shortcuts.ini" + ${DoUntil} ${Errors} + ${If} ${FileExists} "$R9\$R8" + ${${_MOZFUNC_UN}DeleteShortcutsFromLog} "$R9\$R8" + ${EndIf} + ClearErrors + FindNext $R6 $R8 + ${Loop} + + ClearErrors + + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Pop $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro DeleteShortcutsCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call DeleteShortcuts + !verbose pop +!macroend + +!macro un.DeleteShortcutsCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.DeleteShortcuts + !verbose pop +!macroend + +!macro un.DeleteShortcuts + !ifndef un.DeleteShortcuts + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro DeleteShortcuts + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + + +################################################################################ +# Macros for parsing and updating the uninstall.log + +/** + * Updates the uninstall.log with new files added by software update. + * + * When modifying this macro be aware that LineFind uses all registers except + * $R0-$R3 and TextCompareNoDetails uses all registers except $R0-$R9 so be + * cautious. Callers of this macro are not affected. + */ +!macro UpdateUninstallLog + + !ifndef UpdateUninstallLog + !insertmacro FileJoin + !insertmacro LineFind + !insertmacro TextCompareNoDetails + !insertmacro TrimNewLines + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define UpdateUninstallLog "!insertmacro UpdateUninstallLogCall" + + Function UpdateUninstallLog + Push $R3 + Push $R2 + Push $R1 + Push $R0 + + ClearErrors + + GetFullPathName $R3 "$INSTDIR\uninstall" + ${If} ${FileExists} "$R3\uninstall.update" + ${LineFind} "$R3\uninstall.update" "" "1:-1" "CleanupUpdateLog" + + GetTempFileName $R2 "$R3" + FileOpen $R1 "$R2" w + ${TextCompareNoDetails} "$R3\uninstall.update" "$R3\uninstall.log" "SlowDiff" "CreateUpdateDiff" + FileClose $R1 + + IfErrors +2 0 + ${FileJoin} "$R3\uninstall.log" "$R2" "$R3\uninstall.log" + + ${DeleteFile} "$R2" + ${EndIf} + + ClearErrors + + Pop $R0 + Pop $R1 + Pop $R2 + Pop $R3 + FunctionEnd + + ; This callback MUST use labels vs. relative line numbers. + Function CleanupUpdateLog + StrCpy $R2 "$R9" 12 + StrCmp "$R2" "EXECUTE ADD " +1 skip + StrCpy $R9 "$R9" "" 12 + + Push $R6 + Push $R5 + Push $R4 + StrCpy $R4 "" ; Initialize to an empty string. + StrCpy $R6 -1 ; Set the counter to -1 so it will start at 0. + + loop: + IntOp $R6 $R6 + 1 ; Increment the counter. + StrCpy $R5 $R9 1 $R6 ; Starting from the counter copy the next char. + StrCmp $R5 "" copy ; Are there no more chars? + StrCmp $R5 "/" +1 +2 ; Is the char a /? + StrCpy $R5 "\" ; Replace the char with a \. + + StrCpy $R4 "$R4$R5" + GoTo loop + + copy: + StrCpy $R9 "File: \$R4" + Pop $R6 + Pop $R5 + Pop $R4 + GoTo end + + skip: + StrCpy $0 "SkipWrite" + + end: + Push $0 + FunctionEnd + + Function CreateUpdateDiff + ${TrimNewLines} "$9" $9 + ${If} $9 != "" + FileWrite $R1 "$9$\r$\n" + ${EndIf} + + Push 0 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro UpdateUninstallLogCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call UpdateUninstallLog + !verbose pop +!macroend + +/** + * Copies files from a source directory to a destination directory with logging + * to the uninstall.log. If any destination files are in use a reboot will be + * necessary to complete the installation and the reboot flag (see IfRebootFlag + * in the NSIS documentation). + * + * @param _PATH_TO_SOURCE + * Source path to copy the files from. This must not end with a \. + * + * @param _PATH_TO_DESTINATION + * Destination path to copy the files to. This must not end with a \. + * + * @param _PREFIX_ERROR_CREATEDIR + * Prefix for the directory creation error message. The directory path + * will be inserted below this string. + * + * @param _SUFFIX_ERROR_CREATEDIR + * Suffix for the directory creation error message. The directory path + * will be inserted above this string. + * + * $0 = destination file's parent directory used in the create_dir label + * $R0 = copied value from $R6 (e.g. _PATH_TO_SOURCE) + * $R1 = copied value from $R7 (e.g. _PATH_TO_DESTINATION) + * $R2 = string length of the path to source + * $R3 = relative path from the path to source + * $R4 = copied value from $R8 (e.g. _PREFIX_ERROR_CREATEDIR) + * $R5 = copied value from $R9 (e.g. _SUFFIX_ERROR_CREATEDIR) + * note: the LocateNoDetails macro uses these registers so we copy the values + * to other registers. + * $R6 = initially _PATH_TO_SOURCE and then set to "size" ($R6="" if directory, + * $R6="0" if file with /S=)"path\name" in callback + * $R7 = initially _PATH_TO_DESTINATION and then set to "name" in callback + * $R8 = initially _PREFIX_ERROR_CREATEDIR and then set to "path" in callback + * $R9 = initially _SUFFIX_ERROR_CREATEDIR and then set to "path\name" in + * callback + */ +!macro CopyFilesFromDir + + !ifndef CopyFilesFromDir + !insertmacro LocateNoDetails + !insertmacro OnEndCommon + !insertmacro WordReplace + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define CopyFilesFromDir "!insertmacro CopyFilesFromDirCall" + + Function CopyFilesFromDir + Exch $R9 + Exch 1 + Exch $R8 + Exch 2 + Exch $R7 + Exch 3 + Exch $R6 + Push $R5 + Push $R4 + Push $R3 + Push $R2 + Push $R1 + Push $R0 + Push $0 + + StrCpy $R0 "$R6" + StrCpy $R1 "$R7" + StrCpy $R4 "$R8" + StrCpy $R5 "$R9" + + StrLen $R2 "$R0" + + ${LocateNoDetails} "$R0" "/L=FD" "CopyFileCallback" + + Pop $0 + Pop $R0 + Pop $R1 + Pop $R2 + Pop $R3 + Pop $R4 + Pop $R5 + Exch $R6 + Exch 3 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + Function CopyFileCallback + StrCpy $R3 $R8 "" $R2 ; $R3 always begins with a \. + + retry: + ClearErrors + StrCmp $R6 "" +1 copy_file + IfFileExists "$R1$R3\$R7" end +1 + StrCpy $0 "$R1$R3\$R7" + + create_dir: + ClearErrors + CreateDirectory "$0" + IfFileExists "$0" +1 err_create_dir ; protect against looping. + ${LogMsg} "Created Directory: $0" + StrCmp $R6 "" end copy_file + + err_create_dir: + ${LogMsg} "** ERROR Creating Directory: $0 **" + MessageBox MB_RETRYCANCEL|MB_ICONQUESTION "$R4$\r$\n$\r$\n$0$\r$\n$\r$\n$R5" IDRETRY retry + ${OnEndCommon} + Quit + + copy_file: + StrCpy $0 "$R1$R3" + StrCmp "$0" "$INSTDIR" +2 +1 + IfFileExists "$0" +1 create_dir + + ClearErrors + ${DeleteFile} "$R1$R3\$R7" + IfErrors +1 dest_clear + ClearErrors + Rename "$R1$R3\$R7" "$R1$R3\$R7.moz-delete" + IfErrors +1 reboot_delete + + ; file will replace destination file on reboot + Rename "$R9" "$R9.moz-upgrade" + CopyFiles /SILENT "$R9.moz-upgrade" "$R1$R3" + Rename /REBOOTOK "$R1$R3\$R7.moz-upgrade" "$R1$R3\$R7" + ${LogMsg} "Copied File: $R1$R3\$R7.moz-upgrade" + ${LogMsg} "Delayed Install File (Reboot Required): $R1$R3\$R7" + GoTo log_uninstall + + ; file will be deleted on reboot + reboot_delete: + CopyFiles /SILENT $R9 "$R1$R3" + Delete /REBOOTOK "$R1$R3\$R7.moz-delete" + ${LogMsg} "Installed File: $R1$R3\$R7" + ${LogMsg} "Delayed Delete File (Reboot Required): $R1$R3\$R7.moz-delete" + GoTo log_uninstall + + ; destination file doesn't exist - coast is clear + dest_clear: + CopyFiles /SILENT $R9 "$R1$R3" + ${LogMsg} "Installed File: $R1$R3\$R7" + + log_uninstall: + ; If the file is installed into the installation directory remove the + ; installation directory's path from the file path when writing to the + ; uninstall.log so it will be a relative path. This allows the same + ; helper.exe to be used with zip builds if we supply an uninstall.log. + ${WordReplace} "$R1$R3\$R7" "$INSTDIR" "" "+" $R3 + ${LogUninstall} "File: $R3" + + end: + Push 0 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CopyFilesFromDirCall _PATH_TO_SOURCE _PATH_TO_DESTINATION \ + _PREFIX_ERROR_CREATEDIR _SUFFIX_ERROR_CREATEDIR + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PATH_TO_SOURCE}" + Push "${_PATH_TO_DESTINATION}" + Push "${_PREFIX_ERROR_CREATEDIR}" + Push "${_SUFFIX_ERROR_CREATEDIR}" + Call CopyFilesFromDir + !verbose pop +!macroend + +/** + * Parses the uninstall.log on install to first remove a previous installation's + * files and then their directories if empty prior to installing. + * + * When modifying this macro be aware that LineFind uses all registers except + * $R0-$R3 so be cautious. Callers of this macro are not affected. + */ +!macro OnInstallUninstall + + !ifndef OnInstallUninstall + !insertmacro GetParent + !insertmacro LineFind + !insertmacro TrimNewLines + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define OnInstallUninstall "!insertmacro OnInstallUninstallCall" + + Function OnInstallUninstall + Push $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + Push $R2 + Push $R1 + Push $R0 + Push $TmpVal + + IfFileExists "$INSTDIR\uninstall\uninstall.log" +1 end + + ${LogHeader} "Removing Previous Installation" + + ; Copy the uninstall log file to a temporary file + GetTempFileName $TmpVal + CopyFiles /SILENT /FILESONLY "$INSTDIR\uninstall\uninstall.log" "$TmpVal" + + ; Delete files + ${LineFind} "$TmpVal" "/NUL" "1:-1" "RemoveFilesCallback" + + ; Remove empty directories + ${LineFind} "$TmpVal" "/NUL" "1:-1" "RemoveDirsCallback" + + ; Delete the temporary uninstall log file + Delete /REBOOTOK "$TmpVal" + + ; Delete the uninstall log file + Delete "$INSTDIR\uninstall\uninstall.log" + + end: + ClearErrors + + Pop $TmpVal + Pop $R0 + Pop $R1 + Pop $R2 + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Pop $R9 + FunctionEnd + + Function RemoveFilesCallback + ${TrimNewLines} "$R9" $R9 + StrCpy $R1 "$R9" 5 ; Copy the first five chars + + StrCmp "$R1" "File:" +1 end + StrCpy $R9 "$R9" "" 6 ; Copy string starting after the 6th char + StrCpy $R0 "$R9" 1 ; Copy the first char + + StrCmp "$R0" "\" +1 end ; If this isn't a relative path goto end + StrCmp "$R9" "\install.log" end +1 ; Skip the install.log + StrCmp "$R9" "\MapiProxy_InUse.dll" end +1 ; Skip the MapiProxy_InUse.dll + StrCmp "$R9" "\mozMapi32_InUse.dll" end +1 ; Skip the mozMapi32_InUse.dll + + StrCpy $R1 "$INSTDIR$R9" ; Copy the install dir path and suffix it with the string + IfFileExists "$R1" +1 end + + ClearErrors + Delete "$R1" + ${Unless} ${Errors} + ${LogMsg} "Deleted File: $R1" + Goto end + ${EndUnless} + + ClearErrors + Rename "$R1" "$R1.moz-delete" + ${Unless} ${Errors} + Delete /REBOOTOK "$R1.moz-delete" + ${LogMsg} "Delayed Delete File (Reboot Required): $R1.moz-delete" + GoTo end + ${EndUnless} + + ; Check if the file exists in the source. If it does the new file will + ; replace the existing file when the system is rebooted. If it doesn't + ; the file will be deleted when the system is rebooted. + ${Unless} ${FileExists} "$EXEDIR\core$R9" + ${AndUnless} ${FileExists} "$EXEDIR\optional$R9" + Delete /REBOOTOK "$R1" + ${LogMsg} "Delayed Delete File (Reboot Required): $R1" + ${EndUnless} + + end: + ClearErrors + + Push 0 + FunctionEnd + + ; Using locate will leave file handles open to some of the directories + ; which will prevent the deletion of these directories. This parses the + ; uninstall.log and uses the file entries to find / remove empty + ; directories. + Function RemoveDirsCallback + ${TrimNewLines} "$R9" $R9 + StrCpy $R0 "$R9" 5 ; Copy the first five chars + StrCmp "$R0" "File:" +1 end + + StrCpy $R9 "$R9" "" 6 ; Copy string starting after the 6th char + StrCpy $R0 "$R9" 1 ; Copy the first char + + StrCpy $R1 "$INSTDIR$R9" ; Copy the install dir path and suffix it with the string + StrCmp "$R0" "\" loop end ; If this isn't a relative path goto end + + loop: + ${GetParent} "$R1" $R1 ; Get the parent directory for the path + StrCmp "$R1" "$INSTDIR" end +1 ; If the directory is the install dir goto end + + IfFileExists "$R1" +1 loop ; Only try to remove the dir if it exists + ClearErrors + RmDir "$R1" ; Remove the dir + IfErrors end +1 ; If we fail there is no use trying to remove its parent dir + ${LogMsg} "Deleted Directory: $R1" + GoTo loop + + end: + ClearErrors + + Push 0 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro OnInstallUninstallCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call OnInstallUninstall + !verbose pop +!macroend + +/** + * Parses the precomplete file to remove an installation's files and + * directories. + * + * @param _CALLBACK + * The function address of a callback function for progress or "false" + * if there is no callback function. + * + * $R3 = false if all files were deleted or moved to the tobedeleted directory. + * true if file(s) could not be moved to the tobedeleted directory. + * $R4 = Path to temporary precomplete file. + * $R5 = File handle for the temporary precomplete file. + * $R6 = String returned from FileRead. + * $R7 = First seven characters of the string returned from FileRead. + * $R8 = Temporary file path used to rename files that are in use. + * $R9 = _CALLBACK + */ +!macro RemovePrecompleteEntries + + !ifndef ${_MOZFUNC_UN}RemovePrecompleteEntries + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetParent + !insertmacro ${_MOZFUNC_UN_TMP}TrimNewLines + !insertmacro ${_MOZFUNC_UN_TMP}WordReplace + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}RemovePrecompleteEntries "!insertmacro ${_MOZFUNC_UN}RemovePrecompleteEntriesCall" + + Function ${_MOZFUNC_UN}RemovePrecompleteEntries + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + + ${If} ${FileExists} "$INSTDIR\precomplete" + StrCpy $R3 "false" + + RmDir /r "$INSTDIR\${TO_BE_DELETED}" + CreateDirectory "$INSTDIR\${TO_BE_DELETED}" + GetTempFileName $R4 "$INSTDIR\${TO_BE_DELETED}" + Delete "$R4" + Rename "$INSTDIR\precomplete" "$R4" + + ClearErrors + ; Rename and then remove files + FileOpen $R5 "$R4" r + ${Do} + FileRead $R5 $R6 + ${If} ${Errors} + ${Break} + ${EndIf} + + ${${_MOZFUNC_UN}TrimNewLines} "$R6" $R6 + ; Replace all occurrences of "/" with "\". + ${${_MOZFUNC_UN}WordReplace} "$R6" "/" "\" "+" $R6 + + ; Copy the first 7 chars + StrCpy $R7 "$R6" 7 + ${If} "$R7" == "remove " + ; Copy the string starting after the 8th char + StrCpy $R6 "$R6" "" 8 + ; Copy all but the last char to remove the double quote. + StrCpy $R6 "$R6" -1 + ${If} ${FileExists} "$INSTDIR\$R6" + ${Unless} "$R9" == "false" + Call $R9 + ${EndUnless} + + ClearErrors + Delete "$INSTDIR\$R6" + ${If} ${Errors} + GetTempFileName $R8 "$INSTDIR\${TO_BE_DELETED}" + Delete "$R8" + ClearErrors + Rename "$INSTDIR\$R6" "$R8" + ${Unless} ${Errors} + Delete /REBOOTOK "$R8" + + ClearErrors + ${EndUnless} +!ifdef __UNINSTALL__ + ${If} ${Errors} + Delete /REBOOTOK "$INSTDIR\$R6" + StrCpy $R3 "true" + ClearErrors + ${EndIf} +!endif + ${EndIf} + ${EndIf} + ${ElseIf} "$R7" == "rmdir $\"" + ; Copy the string starting after the 7th char. + StrCpy $R6 "$R6" "" 7 + ; Copy all but the last two chars to remove the slash and the double quote. + StrCpy $R6 "$R6" -2 + ${If} ${FileExists} "$INSTDIR\$R6" + ; Ignore directory removal errors + RmDir "$INSTDIR\$R6" + ClearErrors + ${EndIf} + ${EndIf} + ${Loop} + FileClose $R5 + + ; Delete the temporary precomplete file + Delete /REBOOTOK "$R4" + + RmDir /r /REBOOTOK "$INSTDIR\${TO_BE_DELETED}" + + ${If} ${RebootFlag} + ${AndIf} "$R3" == "false" + ; Clear the reboot flag if all files were deleted or moved to the + ; tobedeleted directory. + SetRebootFlag false + ${EndIf} + ${EndIf} + + ClearErrors + + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro RemovePrecompleteEntriesCall _CALLBACK + !verbose push + Push "${_CALLBACK}" + !verbose ${_MOZFUNC_VERBOSE} + Call RemovePrecompleteEntries + !verbose pop +!macroend + +!macro un.RemovePrecompleteEntriesCall _CALLBACK + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_CALLBACK}" + Call un.RemovePrecompleteEntries + !verbose pop +!macroend + +!macro un.RemovePrecompleteEntries + !ifndef un.RemovePrecompleteEntries + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro RemovePrecompleteEntries + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Parses the uninstall.log to unregister dll's, remove files, and remove + * empty directories for this installation. + * + * When modifying this macro be aware that LineFind uses all registers except + * $R0-$R3 so be cautious. Callers of this macro are not affected. + */ +!macro un.ParseUninstallLog + + !ifndef un.ParseUninstallLog + !insertmacro un.GetParent + !insertmacro un.LineFind + !insertmacro un.TrimNewLines + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define un.ParseUninstallLog "!insertmacro un.ParseUninstallLogCall" + + Function un.ParseUninstallLog + Push $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + Push $R2 + Push $R1 + Push $R0 + Push $TmpVal + + IfFileExists "$INSTDIR\uninstall\uninstall.log" +1 end + + ; Copy the uninstall log file to a temporary file + GetTempFileName $TmpVal + CopyFiles /SILENT /FILESONLY "$INSTDIR\uninstall\uninstall.log" "$TmpVal" + + ; Unregister DLL's + ${un.LineFind} "$TmpVal" "/NUL" "1:-1" "un.UnRegDLLsCallback" + + ; Delete files + ${un.LineFind} "$TmpVal" "/NUL" "1:-1" "un.RemoveFilesCallback" + + ; Remove empty directories + ${un.LineFind} "$TmpVal" "/NUL" "1:-1" "un.RemoveDirsCallback" + + ; Delete the temporary uninstall log file + Delete /REBOOTOK "$TmpVal" + + end: + + Pop $TmpVal + Pop $R0 + Pop $R1 + Pop $R2 + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Pop $R9 + FunctionEnd + + Function un.RemoveFilesCallback + ${un.TrimNewLines} "$R9" $R9 + StrCpy $R1 "$R9" 5 + + StrCmp "$R1" "File:" +1 end + StrCpy $R9 "$R9" "" 6 + StrCpy $R0 "$R9" 1 + + StrCpy $R1 "$INSTDIR$R9" + StrCmp "$R0" "\" +2 +1 + StrCpy $R1 "$R9" + + IfFileExists "$R1" +1 end + Delete "$R1" + IfErrors +1 end + ClearErrors + Rename "$R1" "$R1.moz-delete" + IfErrors +1 +3 + Delete /REBOOTOK "$R1" + GoTo end + + Delete /REBOOTOK "$R1.moz-delete" + + end: + ClearErrors + + Push 0 + FunctionEnd + + Function un.UnRegDLLsCallback + ${un.TrimNewLines} "$R9" $R9 + StrCpy $R1 "$R9" 7 + + StrCmp $R1 "DLLReg:" +1 end + StrCpy $R9 "$R9" "" 8 + StrCpy $R0 "$R9" 1 + + StrCpy $R1 "$INSTDIR$R9" + StrCmp $R0 "\" +2 +1 + StrCpy $R1 "$R9" + + ${UnregisterDLL} $R1 + + end: + ClearErrors + + Push 0 + FunctionEnd + + ; Using locate will leave file handles open to some of the directories + ; which will prevent the deletion of these directories. This parses the + ; uninstall.log and uses the file entries to find / remove empty + ; directories. + Function un.RemoveDirsCallback + ${un.TrimNewLines} "$R9" $R9 + StrCpy $R0 "$R9" 5 ; Copy the first five chars + StrCmp "$R0" "File:" +1 end + + StrCpy $R9 "$R9" "" 6 ; Copy string starting after the 6th char + StrCpy $R0 "$R9" 1 ; Copy the first char + + StrCpy $R1 "$INSTDIR$R9" ; Copy the install dir path and suffix it with the string + StrCmp "$R0" "\" loop ; If this is a relative path goto the loop + StrCpy $R1 "$R9" ; Already a full path so copy the string + + loop: + ${un.GetParent} "$R1" $R1 ; Get the parent directory for the path + StrCmp "$R1" "$INSTDIR" end ; If the directory is the install dir goto end + + ; We only try to remove empty directories but the Desktop, StartMenu, and + ; QuickLaunch directories can be empty so guard against removing them. + SetShellVarContext all ; Set context to all users + StrCmp "$R1" "$DESKTOP" end ; All users desktop + StrCmp "$R1" "$STARTMENU" end ; All users start menu + + SetShellVarContext current ; Set context to all users + StrCmp "$R1" "$DESKTOP" end ; Current user desktop + StrCmp "$R1" "$STARTMENU" end ; Current user start menu + StrCmp "$R1" "$QUICKLAUNCH" end ; Current user quick launch + + IfFileExists "$R1" +1 +3 ; Only try to remove the dir if it exists + ClearErrors + RmDir "$R1" ; Remove the dir + IfErrors end ; If we fail there is no use trying to remove its parent dir + + StrCmp "$R0" "\" loop end ; Only loop when the path is relative to the install dir + + end: + ClearErrors + + Push 0 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro un.ParseUninstallLogCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.ParseUninstallLog + !verbose pop +!macroend + +/** + * Finds a valid Start Menu shortcut in the uninstall log and returns the + * relative path from the Start Menu's Programs directory to the shortcut's + * directory. + * + * When modifying this macro be aware that LineFind uses all registers except + * $R0-$R3 so be cautious. Callers of this macro are not affected. + * + * @return _REL_PATH_TO_DIR + * The relative path to the application's Start Menu directory from the + * Start Menu's Programs directory. + */ +!macro FindSMProgramsDir + + !ifndef FindSMProgramsDir + !insertmacro GetParent + !insertmacro LineFind + !insertmacro TrimNewLines + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define FindSMProgramsDir "!insertmacro FindSMProgramsDirCall" + + Function FindSMProgramsDir + Exch $R3 + Push $R2 + Push $R1 + Push $R0 + + StrCpy $R3 "" + ${If} ${FileExists} "$INSTDIR\uninstall\uninstall.log" + ${LineFind} "$INSTDIR\uninstall\uninstall.log" "/NUL" "1:-1" "FindSMProgramsDirRelPath" + ${EndIf} + ClearErrors + + Pop $R0 + Pop $R1 + Pop $R2 + Exch $R3 + FunctionEnd + + ; This callback MUST use labels vs. relative line numbers. + Function FindSMProgramsDirRelPath + Push 0 + ${TrimNewLines} "$R9" $R9 + StrCpy $R4 "$R9" 5 + + StrCmp "$R4" "File:" +1 end_FindSMProgramsDirRelPath + StrCpy $R9 "$R9" "" 6 + StrCpy $R4 "$R9" 1 + + StrCmp "$R4" "\" end_FindSMProgramsDirRelPath +1 + + SetShellVarContext all + ${GetLongPath} "$SMPROGRAMS" $R4 + StrLen $R2 "$R4" + StrCpy $R1 "$R9" $R2 + StrCmp "$R1" "$R4" +1 end_FindSMProgramsDirRelPath + IfFileExists "$R9" +1 end_FindSMProgramsDirRelPath + ShellLink::GetShortCutTarget "$R9" + Pop $R0 + StrCmp "$INSTDIR\${FileMainEXE}" "$R0" +1 end_FindSMProgramsDirRelPath + ${GetParent} "$R9" $R3 + IntOp $R2 $R2 + 1 + StrCpy $R3 "$R3" "" $R2 + + Pop $R4 ; Remove the previously pushed 0 from the stack and + push "StopLineFind" ; push StopLineFind to stop finding more lines. + + end_FindSMProgramsDirRelPath: + ClearErrors + + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro FindSMProgramsDirCall _REL_PATH_TO_DIR + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call FindSMProgramsDir + Pop ${_REL_PATH_TO_DIR} + !verbose pop +!macroend + + +################################################################################ +# Macros for custom branding + +/** + * Sets BrandFullName and / or BrandShortName to values provided in the specified + * ini file and defaults to BrandShortName and BrandFullName as defined in + * branding.nsi when the associated ini file entry is not specified. + * + * ini file format: + * [Branding] + * BrandFullName=Custom Full Name + * BrandShortName=Custom Short Name + * + * @param _PATH_TO_INI + * Path to the ini file. + * + * $R6 = return value from ReadINIStr + * $R7 = stores BrandShortName + * $R8 = stores BrandFullName + * $R9 = _PATH_TO_INI + */ +!macro SetBrandNameVars + + !ifndef ${_MOZFUNC_UN}SetBrandNameVars + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}WordReplace + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + ; Prevent declaring vars twice when the SetBrandNameVars macro is + ; inserted into both the installer and uninstaller. + !ifndef SetBrandNameVars + Var BrandFullName + Var BrandFullNameDA + Var BrandShortName + Var BrandProductName + !endif + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}SetBrandNameVars "!insertmacro ${_MOZFUNC_UN}SetBrandNameVarsCall" + + Function ${_MOZFUNC_UN}SetBrandNameVars + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + + StrCpy $R8 "${BrandFullName}" + StrCpy $R7 "${BrandShortName}" + StrCpy $R6 "${BrandProductName}" + + IfFileExists "$R9" +1 finish + + ClearErrors + ReadINIStr $R5 $R9 "Branding" "BrandFullName" + IfErrors +2 +1 + StrCpy $R8 "$R5" + + ClearErrors + ReadINIStr $R5 $R9 "Branding" "BrandShortName" + IfErrors +2 +1 + StrCpy $R7 "$R5" + + ClearErrors + ReadINIStr $R5 $R9 "Branding" "BrandProductName" + IfErrors +2 +1 + StrCpy $R6 "$R5" + + finish: + StrCpy $BrandFullName "$R8" + ${${_MOZFUNC_UN}WordReplace} "$R8" "&" "&&" "+" $R8 + StrCpy $BrandFullNameDA "$R8" + StrCpy $BrandShortName "$R7" + StrCpy $BrandProductName "$R6" + + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro SetBrandNameVarsCall _PATH_TO_INI + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PATH_TO_INI}" + Call SetBrandNameVars + !verbose pop +!macroend + +!macro un.SetBrandNameVarsCall _PATH_TO_INI + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PATH_TO_INI}" + Call un.SetBrandNameVars + !verbose pop +!macroend + +!macro un.SetBrandNameVars + !ifndef un.SetBrandNameVars + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro SetBrandNameVars + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Replaces the wizard's header image with the one specified. + * + * @param _PATH_TO_IMAGE + * Fully qualified path to the bitmap to use for the header image. + * + * $R8 = hwnd for the control returned from GetDlgItem. + * $R9 = _PATH_TO_IMAGE + */ +!macro ChangeMUIHeaderImage + + !ifndef ${_MOZFUNC_UN}ChangeMUIHeaderImage + Var hHeaderBitmap + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}ChangeMUIHeaderImage "!insertmacro ${_MOZFUNC_UN}ChangeMUIHeaderImageCall" + + Function ${_MOZFUNC_UN}ChangeMUIHeaderImage + Exch $R9 + Push $R8 + + GetDlgItem $R8 $HWNDPARENT 1046 + ${SetStretchedImageOLE} $R8 "$R9" $hHeaderBitmap + ; There is no way to specify a show function for a custom page so hide + ; and then show the control to force the bitmap to redraw. + ShowWindow $R8 ${SW_HIDE} + ShowWindow $R8 ${SW_SHOW} + + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro ChangeMUIHeaderImageCall _PATH_TO_IMAGE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PATH_TO_IMAGE}" + Call ChangeMUIHeaderImage + !verbose pop +!macroend + +!macro un.ChangeMUIHeaderImageCall _PATH_TO_IMAGE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PATH_TO_IMAGE}" + Call un.ChangeMUIHeaderImage + !verbose pop +!macroend + +!macro un.ChangeMUIHeaderImage + !ifndef un.ChangeMUIHeaderImage + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro ChangeMUIHeaderImage + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Replaces the sidebar image on the wizard's welcome and finish pages. + * + * @param _PATH_TO_IMAGE + * Fully qualified path to the bitmap to use for the header image. + * + * $R8 = hwnd for the bitmap control + * $R9 = _PATH_TO_IMAGE + */ +!macro ChangeMUISidebarImage + + !ifndef ${_MOZFUNC_UN}ChangeMUISidebarImage + Var hSidebarBitmap + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}ChangeMUISidebarImage "!insertmacro ${_MOZFUNC_UN}ChangeMUISidebarImageCall" + + Function ${_MOZFUNC_UN}ChangeMUISidebarImage + Exch $R9 + Push $R8 + + ; Make sure we're not about to leak an existing handle. + ${If} $hSidebarBitmap <> 0 + System::Call "gdi32::DeleteObject(p $hSidebarBitmap)" + StrCpy $hSidebarBitmap 0 + ${EndIf} + ; The controls on the welcome and finish pages aren't in the dialog + ; template, they're always created manually from the INI file, so we need + ; to query it to find the right HWND. + ReadINIStr $R8 "$PLUGINSDIR\ioSpecial.ini" "Field 1" "HWND" + ${SetStretchedImageOLE} $R8 "$R9" $hSidebarBitmap + + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro ChangeMUISidebarImageCall _PATH_TO_IMAGE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PATH_TO_IMAGE}" + Call ChangeMUISidebarImage + !verbose pop +!macroend + +!macro un.ChangeMUISidebarImageCall _PATH_TO_IMAGE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PATH_TO_IMAGE}" + Call un.ChangeMUISidebarImage + !verbose pop +!macroend + +!macro un.ChangeMUISidebarImage + !ifndef un.ChangeMUISidebarImage + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro ChangeMUISidebarImage + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +################################################################################ +# User interface callback helper defines and macros + +/* Install type defines */ +!ifndef INSTALLTYPE_BASIC + !define INSTALLTYPE_BASIC 1 +!endif + +!ifndef INSTALLTYPE_CUSTOM + !define INSTALLTYPE_CUSTOM 2 +!endif + +/** + * Checks whether to display the current page (e.g. if not performing a custom + * install don't display the custom pages). + */ +!macro CheckCustomCommon + + !ifndef CheckCustomCommon + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define CheckCustomCommon "!insertmacro CheckCustomCommonCall" + + Function CheckCustomCommon + + ; Abort if not a custom install + IntCmp $InstallType ${INSTALLTYPE_CUSTOM} +2 +1 +1 + Abort + + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CheckCustomCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call CheckCustomCommon + !verbose pop +!macroend + +/** + * Unloads dll's and releases references when the installer and uninstaller + * exit. + */ +!macro OnEndCommon + + !ifndef ${_MOZFUNC_UN}OnEndCommon + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}UnloadUAC + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}OnEndCommon "!insertmacro ${_MOZFUNC_UN}OnEndCommonCall" + + Function ${_MOZFUNC_UN}OnEndCommon + + ${${_MOZFUNC_UN}UnloadUAC} + StrCmp $hHeaderBitmap "" +3 +1 + System::Call "gdi32::DeleteObject(i s)" $hHeaderBitmap + StrCpy $hHeaderBitmap "" + ; If ChangeMUISidebarImage was called, then we also need to clean up the + ; GDI bitmap handle that it would have created. + !ifdef ${_MOZFUNC_UN}ChangeMUISidebarImage + StrCmp $hSidebarBitmap "" +3 +1 + System::Call "gdi32::DeleteObject(i s)" $hSidebarBitmap + StrCpy $hSidebarBitmap "" + !endif + + System::Free 0 + + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro OnEndCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call OnEndCommon + !verbose pop +!macroend + +!macro un.OnEndCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.OnEndCommon + !verbose pop +!macroend + +!macro un.OnEndCommon + !ifndef un.OnEndCommon + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro OnEndCommon + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Reads a flag option from the command line and sets a variable with its state, + * if the option is present on the command line. + * + * @param FULL_COMMAND_LINE + * The entire installer command line, such as from ${GetParameters} + * @param OPTION + * Name of the option to look for + * @param OUTPUT + * Variable/register to write the output to. Will be set to "0" if the + * option was present with the value "false", will be set to "1" if the + * option was present with another value, and will be untouched if the + * option was not on the command line at all. + */ +!macro InstallGetOption FULL_COMMAND_LINE OPTION OUTPUT + Push $0 + ClearErrors + ${GetOptions} ${FULL_COMMAND_LINE} "/${OPTION}" $0 + ${IfNot} ${Errors} + ; Any valid command-line option triggers a silent installation. + SetSilent silent + + ${If} $0 == "=false" + StrCpy ${OUTPUT} "0" + ${Else} + StrCpy ${OUTPUT} "1" + ${EndIf} + ${EndIf} + Pop $0 +!macroend +!define InstallGetOption "!insertmacro InstallGetOption" + +/** + * Called from the installer's .onInit function not to be confused with the + * uninstaller's .onInit or the uninstaller's un.onInit functions. + * + * @param _WARN_UNSUPPORTED_MSG + * Message displayed when the Windows version is not supported. + * + * $R4 = keeps track of whether a custom install path was specified on either + * the command line or in an INI file + * $R5 = return value from the GetSize macro + * $R6 = general string values, return value from GetTempFileName, return + * value from the GetSize macro + * $R7 = full path to the configuration ini file + * $R8 = used for OS Version and Service Pack detection and the return value + * from the GetParameters macro + * $R9 = _WARN_UNSUPPORTED_MSG + */ +!macro InstallOnInitCommon + + !ifndef InstallOnInitCommon + !insertmacro ElevateUAC + !insertmacro GetOptions + !insertmacro GetParameters + !insertmacro GetSize + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define InstallOnInitCommon "!insertmacro InstallOnInitCommonCall" + + Function InstallOnInitCommon + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + + ; Don't install on systems that don't support SSE2. The parameter value of + ; 10 is for PF_XMMI64_INSTRUCTIONS_AVAILABLE which will check whether the + ; SSE2 instruction set is available. + System::Call "kernel32::IsProcessorFeaturePresent(i 10)i .R8" + ${If} "$R8" == "0" + MessageBox MB_OK|MB_ICONSTOP "$R9" + ; Nothing initialized so no need to call OnEndCommon + Quit + ${EndIf} + + ; Windows 8.1/Server 2012 R2 and lower are not supported. + ${Unless} ${AtLeastWin10} + MessageBox MB_OK|MB_ICONSTOP "$R9" + ; Nothing initialized so no need to call OnEndCommon + Quit + ${EndUnless} + + !ifdef HAVE_64BIT_BUILD + SetRegView 64 + !endif + + StrCpy $R4 0 ; will be set to 1 if a custom install path is set + + ${GetParameters} $R8 + ${If} $R8 != "" + ; Default install type + StrCpy $InstallType ${INSTALLTYPE_BASIC} + + ${Unless} ${Silent} + ; NSIS should check for /S for us, but we've had issues with it such + ; as bug 506867 in the past, so we'll check for it ourselves also. + ClearErrors + ${GetOptions} "$R8" "/S" $R7 + ${Unless} ${Errors} + SetSilent silent + ${Else} + ; NSIS dropped support for the deprecated -ms argument, but we don't + ; want to break backcompat, so we'll check for it here too. + ClearErrors + ${GetOptions} "$R8" "-ms" $R7 + ${Unless} ${Errors} + SetSilent silent + ${EndUnless} + ${EndUnless} + ${EndUnless} + + ; Support for specifying an installation configuration file. + ClearErrors + ${GetOptions} "$R8" "/INI=" $R7 + ${Unless} ${Errors} + ; The configuration file must also exist + ${If} ${FileExists} "$R7" + ; Any valid command-line option triggers a silent installation. + SetSilent silent + + ReadINIStr $R8 $R7 "Install" "InstallDirectoryName" + ${If} $R8 != "" + StrCpy $R4 1 + !ifdef HAVE_64BIT_BUILD + StrCpy $INSTDIR "$PROGRAMFILES64\$R8" + !else + StrCpy $INSTDIR "$PROGRAMFILES32\$R8" + !endif + ${Else} + ReadINIStr $R8 $R7 "Install" "InstallDirectoryPath" + ${If} $R8 != "" + StrCpy $R4 1 + StrCpy $INSTDIR "$R8" + ${EndIf} + ${EndIf} + + ReadINIStr $R8 $R7 "Install" "DesktopShortcut" + ${If} $R8 == "false" + StrCpy $AddDesktopSC "0" + ${Else} + StrCpy $AddDesktopSC "1" + ${EndIf} + + ReadINIStr $R8 $R7 "Install" "StartMenuShortcuts" + ${If} $R8 == "false" + StrCpy $AddStartMenuSC "0" + ${Else} + StrCpy $AddStartMenuSC "1" + ${EndIf} + + ; We still accept the plural version for backwards compatibility, + ; but the singular version takes priority. + ClearErrors + ReadINIStr $R8 $R7 "Install" "StartMenuShortcut" + ${If} $R8 == "false" + StrCpy $AddStartMenuSC "0" + ${ElseIfNot} ${Errors} + StrCpy $AddStartMenuSC "1" + ${EndIf} + + !ifdef MOZ_PRIVATE_BROWSING + ReadINIStr $R8 $R7 "Install" "PrivateBrowsingShortcut" + ${If} $R8 == "false" + StrCpy $AddPrivateBrowsingSC "0" + ${ElseIfNot} ${Errors} + StrCpy $AddPrivateBrowsingSC "1" + ${EndIf} + !endif + + ReadINIStr $R8 $R7 "Install" "TaskbarShortcut" + ${If} $R8 == "false" + StrCpy $AddTaskbarSC "0" + ${Else} + StrCpy $AddTaskbarSC "1" + ${EndIf} + + ReadINIStr $R8 $R7 "Install" "MaintenanceService" + ${If} $R8 == "false" + StrCpy $InstallMaintenanceService "0" + ${Else} + StrCpy $InstallMaintenanceService "1" + ${EndIf} + + ReadINIStr $R8 $R7 "Install" "RegisterDefaultAgent" + ${If} $R8 == "false" + StrCpy $RegisterDefaultAgent "0" + ${Else} + StrCpy $RegisterDefaultAgent "1" + ${EndIf} + + !ifdef MOZ_OPTIONAL_EXTENSIONS + ReadINIStr $R8 $R7 "Install" "OptionalExtensions" + ${If} $R8 == "false" + StrCpy $InstallOptionalExtensions "0" + ${Else} + StrCpy $InstallOptionalExtensions "1" + ${EndIf} + !endif + + !ifndef NO_STARTMENU_DIR + ReadINIStr $R8 $R7 "Install" "StartMenuDirectoryName" + ${If} $R8 != "" + StrCpy $StartMenuDir "$R8" + ${EndIf} + !endif + ${EndIf} + ${EndUnless} + + ; Check for individual command line parameters after evaluating the INI + ; file, because these should override the INI entires. + ${GetParameters} $R8 + ${GetOptions} $R8 "/InstallDirectoryName=" $R7 + ${If} $R7 != "" + StrCpy $R4 1 + !ifdef HAVE_64BIT_BUILD + StrCpy $INSTDIR "$PROGRAMFILES64\$R7" + !else + StrCpy $INSTDIR "$PROGRAMFILES32\$R7" + !endif + ${Else} + ${GetOptions} $R8 "/InstallDirectoryPath=" $R7 + ${If} $R7 != "" + StrCpy $R4 1 + StrCpy $INSTDIR "$R7" + ${EndIf} + ${EndIf} + + ${InstallGetOption} $R8 "DesktopShortcut" $AddDesktopSC + ${InstallGetOption} $R8 "StartMenuShortcuts" $AddStartMenuSC + ; We still accept the plural version for backwards compatibility, + ; but the singular version takes priority. + ${InstallGetOption} $R8 "StartMenuShortcut" $AddStartMenuSC + !ifdef MOZ_PRIVATE_BROWSING + ${InstallGetOption} $R8 "PrivateBrowsingShortcut" $AddPrivateBrowsingSC + !endif + ${InstallGetOption} $R8 "TaskbarShortcut" $AddTaskbarSC + ${InstallGetOption} $R8 "MaintenanceService" $InstallMaintenanceService + ${InstallGetOption} $R8 "RegisterDefaultAgent" $RegisterDefaultAgent + !ifdef MOZ_OPTIONAL_EXTENSIONS + ${InstallGetOption} $R8 "OptionalExtensions" $InstallOptionalExtensions + !endif + + ; Installing the service always requires elevated privileges. + ${If} $InstallMaintenanceService == "1" + ${ElevateUAC} + ${EndIf} + ${EndIf} + + ${If} $R4 == 1 + ; Any valid command-line option triggers a silent installation. + SetSilent silent + + ; Quit if we are unable to create the installation directory or we are + ; unable to write to a file in the installation directory. + ClearErrors + ${If} ${FileExists} "$INSTDIR" + GetTempFileName $R6 "$INSTDIR" + FileOpen $R5 "$R6" w + FileWrite $R5 "Write Access Test" + FileClose $R5 + Delete $R6 + ${If} ${Errors} + ; Attempt to elevate and then try again. + ${ElevateUAC} + GetTempFileName $R6 "$INSTDIR" + FileOpen $R5 "$R6" w + FileWrite $R5 "Write Access Test" + FileClose $R5 + Delete $R6 + ${If} ${Errors} + ; Nothing initialized so no need to call OnEndCommon + Quit + ${EndIf} + ${EndIf} + ${Else} + CreateDirectory "$INSTDIR" + ${If} ${Errors} + ; Attempt to elevate and then try again. + ${ElevateUAC} + CreateDirectory "$INSTDIR" + ${If} ${Errors} + ; Nothing initialized so no need to call OnEndCommon + Quit + ${EndIf} + ${EndIf} + ${EndIf} + ${Else} + ; If we weren't given a custom path parameter, then try to elevate now. + ; We'll check the user's permission level later on to determine the + ; default install path (which will be the real install path for /S). + ; If an INI file is used, we try to elevate down that path when needed. + ${ElevateUAC} + ${EndIf} + + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro InstallOnInitCommonCall _WARN_UNSUPPORTED_MSG + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_WARN_UNSUPPORTED_MSG}" + Call InstallOnInitCommon + !verbose pop +!macroend + +/** + * Called from the uninstaller's .onInit function not to be confused with the + * installer's .onInit or the uninstaller's un.onInit functions. + */ +!macro UninstallOnInitCommon + + !ifndef UninstallOnInitCommon + !insertmacro ElevateUAC + !insertmacro GetLongPath + !insertmacro GetOptions + !insertmacro GetParameters + !insertmacro GetParent + !insertmacro UnloadUAC + !insertmacro UpdateShortcutAppModelIDs + !insertmacro UpdateUninstallLog + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define UninstallOnInitCommon "!insertmacro UninstallOnInitCommonCall" + + Function UninstallOnInitCommon + ; Prevents breaking apps that don't use SetBrandNameVars + !ifdef SetBrandNameVars + ${SetBrandNameVars} "$EXEDIR\distribution\setup.ini" + !endif + + ; Prevent launching the application when a reboot is required and this + ; executable is the main application executable + IfFileExists "$EXEDIR\${FileMainEXE}.moz-upgrade" +1 +4 + MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2 + Reboot + Quit ; Nothing initialized so no need to call OnEndCommon + + ${GetParent} "$EXEDIR" $INSTDIR + ${GetLongPath} "$INSTDIR" $INSTDIR + IfFileExists "$INSTDIR\${FileMainEXE}" +2 +1 + Quit ; Nothing initialized so no need to call OnEndCommon + +!ifmacrodef InitHashAppModelId + ; setup the application model id registration value + !ifdef AppName + ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs" + !endif +!endif + + ; Prevents breaking apps that don't use SetBrandNameVars + !ifdef SetBrandNameVars + ${SetBrandNameVars} "$INSTDIR\distribution\setup.ini" + !endif + + ; Application update uses a directory named tobedeleted in the $INSTDIR to + ; delete files on OS reboot when they are in use. Try to delete this + ; directory if it exists. + ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}" + RmDir /r "$INSTDIR\${TO_BE_DELETED}" + ${EndIf} + + ; Prevent all operations (e.g. set as default, postupdate, etc.) when a + ; reboot is required and the executable launched is helper.exe + IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +4 + MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2 + Reboot + Quit ; Nothing initialized so no need to call OnEndCommon + + !ifdef HAVE_64BIT_BUILD + SetRegView 64 + !endif + + ${GetParameters} $R0 + + ${Unless} ${Silent} + ; Manually check for /S in the command line due to Bug 506867 + ClearErrors + ${GetOptions} "$R0" "/S" $R2 + ${Unless} ${Errors} + SetSilent silent + ${Else} + ; Support for the deprecated -ms command line argument. + ClearErrors + ${GetOptions} "$R0" "-ms" $R2 + ${Unless} ${Errors} + SetSilent silent + ${EndUnless} + ${EndUnless} + ${EndUnless} + + StrCmp "$R0" "" continue +1 + + ; Require elevation if the user can elevate + hideshortcuts: + ClearErrors + ${GetOptions} "$R0" "/HideShortcuts" $R2 + IfErrors showshortcuts +1 +!ifndef NONADMIN_ELEVATE + ${ElevateUAC} +!endif + ${HideShortcuts} + GoTo finish + + ; Require elevation if the user can elevate + showshortcuts: + ClearErrors + ${GetOptions} "$R0" "/ShowShortcuts" $R2 + IfErrors defaultappuser +1 +!ifndef NONADMIN_ELEVATE + ${ElevateUAC} +!endif + ${ShowShortcuts} + GoTo finish + + ; Require elevation if the the StartMenuInternet registry keys require + ; updating and the user can elevate + defaultappuser: + ClearErrors + ${GetOptions} "$R0" "/SetAsDefaultAppUser" $R2 + IfErrors defaultappglobal +1 + ${SetAsDefaultAppUser} + GoTo finish + + ; Require elevation if the user can elevate + defaultappglobal: + ClearErrors + ${GetOptions} "$R0" "/SetAsDefaultAppGlobal" $R2 + IfErrors postupdate +1 + ${ElevateUAC} + ${SetAsDefaultAppGlobal} + GoTo finish + + ; Do not attempt to elevate. The application launching this executable is + ; responsible for elevation if it is required. + postupdate: + ${WordReplace} "$R0" "$\"" "" "+" $R0 + ClearErrors + ${GetOptions} "$R0" "/PostUpdate" $R2 + IfErrors continue +1 + ; If the uninstall.log does not exist don't perform post update + ; operations. This prevents updating the registry for zip builds. + IfFileExists "$EXEDIR\uninstall.log" +2 +1 + Quit ; Nothing initialized so no need to call OnEndCommon + ${PostUpdate} + ClearErrors + ${GetOptions} "$R0" "/UninstallLog=" $R2 + IfErrors updateuninstalllog +1 + StrCmp "$R2" "" finish +1 + GetFullPathName $R3 "$R2" + IfFileExists "$R3" +1 finish + Delete "$INSTDIR\uninstall\*wizard*" + Delete "$INSTDIR\uninstall\uninstall.log" + CopyFiles /SILENT /FILESONLY "$R3" "$INSTDIR\uninstall\" + ${GetParent} "$R3" $R4 + Delete "$R3" + RmDir "$R4" + GoTo finish + + ; Do not attempt to elevate. The application launching this executable is + ; responsible for elevation if it is required. + updateuninstalllog: + ${UpdateUninstallLog} + + finish: + ${UnloadUAC} + ${RefreshShellIcons} + Quit ; Nothing initialized so no need to call OnEndCommon + + continue: + + ; If the uninstall.log does not exist don't perform uninstall + ; operations. This prevents running the uninstaller for zip builds. + IfFileExists "$INSTDIR\uninstall\uninstall.log" +2 +1 + Quit ; Nothing initialized so no need to call OnEndCommon + + ; When silent, try to avoid elevation if we have a chance to succeed. We + ; can succeed when we can write to (hence delete from) the install + ; directory and when we can clean up all registry entries. Now, the + ; installer when elevated writes privileged registry entries for the use + ; of the Maintenance Service, even when the service is not and will not be + ; installed. (In fact, even when a service installed in the future will + ; never update the installation, for example due to not being in a + ; privileged location.) In practice this means we can only truly silently + ; remove an unelevated install: an elevated installer writing to an + ; unprivileged install directory will still write privileged registry + ; entries, requiring an elevated uninstaller to completely clean up. + ; + ; This avoids a wrinkle, whereby an uninstaller which runs unelevated will + ; never itself launch the Maintenance Service uninstaller, because it will + ; fail to remove its own service registration (removing the relevant + ; registry key would require elevation). Therefore the check for the + ; service being unused will fail, which will prevent running the service + ; uninstaller. That's both subtle and possibly leaves the service + ; registration hanging around, which might be a security risk. + ; + ; That is why we look for a privileged service registration for this + ; installation when deciding to elevate, and elevate unconditionally if we + ; find one, regardless of the result of the write check that would avoid + ; elevation. + + ; The reason for requiring elevation, or "" for not required. + StrCpy $R4 "" + + ${IfNot} ${Silent} + ; In normal operation, require elevation if the user can elevate so that + ; we are most likely to succeed. + StrCpy $R4 "not silent" + ${EndIf} + + GetTempFileName $R6 "$INSTDIR" + FileOpen $R5 "$R6" w + FileWrite $R5 "Write Access Test" + FileClose $R5 + Delete $R6 + ${If} ${Errors} + StrCpy $R4 "write" + ${EndIf} + + !ifdef MOZ_MAINTENANCE_SERVICE + ; We don't necessarily have $MaintCertKey, so use temporary registers. + ServicesHelper::PathToUniqueRegistryPath "$INSTDIR" + Pop $R5 + + ${If} $R5 != "" + ; Always use the 64bit registry for certs on 64bit systems. + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + SetRegView 64 + ${EndIf} + + EnumRegKey $R6 HKLM $R5 0 + ClearErrors + + ${If} ${RunningX64} + ${OrIf} ${IsNativeARM64} + SetRegView lastused + ${EndIf} + + ${IfNot} "$R6" == "" + StrCpy $R4 "mms" + ${EndIf} + ${EndIf} + !endif + + ${If} "$R4" != "" + ; In the future, we might not try to elevate to remain truly silent. Or + ; we might add a command line arguments to specify behaviour. One + ; reason to not do that immediately is that we have no great way to + ; signal that we exited without taking action. + ${ElevateUAC} + ${EndIf} + + ; Now we've elevated, try the write access test again. + ClearErrors + GetTempFileName $R6 "$INSTDIR" + FileOpen $R5 "$R6" w + FileWrite $R5 "Write Access Test" + FileClose $R5 + Delete $R6 + ${If} ${Errors} + ; Nothing initialized so no need to call OnEndCommon + Quit + ${EndIf} + + !ifdef MOZ_MAINTENANCE_SERVICE + ; And verify that if we need to, we're going to clean up the registry + ; correctly. + ${If} "$R4" == "mms" + WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test" + ${If} ${Errors} + ; Nothing initialized so no need to call OnEndCommon + Quit + ${Endif} + DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" + ${EndIf} + !endif + + ; If we made it this far then this installer is being used as an uninstaller. + WriteUninstaller "$EXEDIR\uninstaller.exe" + + ${If} ${Silent} + StrCpy $R1 "$\"$EXEDIR\uninstaller.exe$\" /S" + ${Else} + StrCpy $R1 "$\"$EXEDIR\uninstaller.exe$\"" + ${EndIf} + + ; When the uninstaller is launched it copies itself to the temp directory + ; so it won't be in use so it can delete itself. + ExecWait $R1 + ${DeleteFile} "$EXEDIR\uninstaller.exe" + ${UnloadUAC} + SetErrorLevel 0 + Quit ; Nothing initialized so no need to call OnEndCommon + + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro UninstallOnInitCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call UninstallOnInitCommon + !verbose pop +!macroend + +/** + * Called from the uninstaller's un.onInit function not to be confused with the + * installer's .onInit or the uninstaller's .onInit functions. + */ +!macro un.UninstallUnOnInitCommon + + !ifndef un.UninstallUnOnInitCommon + !insertmacro un.GetLongPath + !insertmacro un.GetParent + !insertmacro un.SetBrandNameVars + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define un.UninstallUnOnInitCommon "!insertmacro un.UninstallUnOnInitCommonCall" + + Function un.UninstallUnOnInitCommon + ${un.GetParent} "$INSTDIR" $INSTDIR + ${un.GetLongPath} "$INSTDIR" $INSTDIR + ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}" + Abort + ${EndUnless} + + !ifdef HAVE_64BIT_BUILD + SetRegView 64 + !endif + + ; Prevents breaking apps that don't use SetBrandNameVars + !ifdef un.SetBrandNameVars + ${un.SetBrandNameVars} "$INSTDIR\distribution\setup.ini" + !endif + + ; Initialize $hHeaderBitmap to prevent redundant changing of the bitmap if + ; the user clicks the back button + StrCpy $hHeaderBitmap "" + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro un.UninstallUnOnInitCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.UninstallUnOnInitCommon + !verbose pop +!macroend + +/** + * Called from the MUI leaveOptions function to set the value of $INSTDIR. + */ +!macro LeaveOptionsCommon + + !ifndef LeaveOptionsCommon + !insertmacro CanWriteToInstallDir + !insertmacro GetLongPath + +!ifndef NO_INSTDIR_FROM_REG + !insertmacro GetSingleInstallPath +!endif + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define LeaveOptionsCommon "!insertmacro LeaveOptionsCommonCall" + + Function LeaveOptionsCommon + Push $R9 + + StrCpy $R9 "false" + +!ifndef NO_INSTDIR_FROM_REG + SetShellVarContext all ; Set SHCTX to HKLM + + ${If} ${IsNativeAMD64} + ${OrIf} ${IsNativeARM64} + SetRegView 64 + ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9 + SetRegView lastused + ${EndIf} + + StrCmp "$R9" "false" +1 finish_get_install_dir + + SetRegView 32 + ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9 + SetRegView lastused + + StrCmp "$R9" "false" +1 finish_get_install_dir + + SetShellVarContext current ; Set SHCTX to HKCU + ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9 + + finish_get_install_dir: + StrCmp "$R9" "false" +2 +1 + StrCpy $INSTDIR "$R9" +!endif + + ; If the user doesn't have write access to the installation directory set + ; the installation directory to a subdirectory of the user's local + ; application directory (e.g. non-roaming). + ${CanWriteToInstallDir} $R9 + ${If} "$R9" == "false" + ; NOTE: This SetShellVarContext isn't directly needed anymore, but to + ; leave the state consistent with earlier code I'm leaving it here. + SetShellVarContext all ; Set SHCTX to All Users + ${GetLocalAppDataFolder} $R9 + StrCpy $INSTDIR "$R9\${BrandFullName}\" + ${CanWriteToInstallDir} $R9 + ${EndIf} + + IfFileExists "$INSTDIR" +3 +1 + Pop $R9 + Return + + ; Always display the long path if the path already exists. + ${GetLongPath} "$INSTDIR" $INSTDIR + + ; The call to GetLongPath returns a long path without a trailing + ; back-slash. Append a \ to the path to prevent the directory + ; name from being appended when using the NSIS create new folder. + ; http://www.nullsoft.com/free/nsis/makensis.htm#InstallDir + StrCpy $INSTDIR "$INSTDIR\" + + Pop $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro LeaveOptionsCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call LeaveOptionsCommon + !verbose pop +!macroend + +/** + * Called from the MUI preDirectory function to verify there is enough disk + * space for the installation and the installation directory is writable. + * + * $R9 = returned value from CheckDiskSpace and CanWriteToInstallDir macros + */ +!macro PreDirectoryCommon + + !ifndef PreDirectoryCommon + !insertmacro CanWriteToInstallDir + !insertmacro CheckDiskSpace + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define PreDirectoryCommon "!insertmacro PreDirectoryCommonCall" + + Function PreDirectoryCommon + Push $R9 + + IntCmp $InstallType ${INSTALLTYPE_CUSTOM} end +1 +1 + ${CanWriteToInstallDir} $R9 + StrCmp "$R9" "false" end +1 + ${CheckDiskSpace} $R9 + StrCmp "$R9" "false" end +1 + Abort + + end: + + Pop $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro PreDirectoryCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call PreDirectoryCommon + !verbose pop +!macroend + +/** + * Called from the MUI leaveDirectory function + * + * @param _WARN_DISK_SPACE + * Message displayed when there isn't enough disk space to perform the + * installation. + * @param _WARN_WRITE_ACCESS + * Message displayed when the installer does not have write access to + * $INSTDIR. + * + * $R7 = returned value from CheckDiskSpace and CanWriteToInstallDir macros + * $R8 = _WARN_DISK_SPACE + * $R9 = _WARN_WRITE_ACCESS + */ +!macro LeaveDirectoryCommon + + !ifndef LeaveDirectoryCommon + !insertmacro CheckDiskSpace + !insertmacro CanWriteToInstallDir + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define LeaveDirectoryCommon "!insertmacro LeaveDirectoryCommonCall" + + Function LeaveDirectoryCommon + Exch $R9 + Exch 1 + Exch $R8 + Push $R7 + + ${CanWriteToInstallDir} $R7 + ${If} $R7 == "false" + MessageBox MB_OK|MB_ICONEXCLAMATION "$R9" + Abort + ${EndIf} + + ${CheckDiskSpace} $R7 + ${If} $R7 == "false" + MessageBox MB_OK|MB_ICONEXCLAMATION "$R8" + Abort + ${EndIf} + + Pop $R7 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro LeaveDirectoryCommonCall _WARN_DISK_SPACE _WARN_WRITE_ACCESS + !verbose push + Push "${_WARN_DISK_SPACE}" + Push "${_WARN_WRITE_ACCESS}" + !verbose ${_MOZFUNC_VERBOSE} + Call LeaveDirectoryCommon + !verbose pop +!macroend + + +################################################################################ +# Install Section common macros. + +/** + * Performs common cleanup operations prior to the actual installation. + * This macro should be called first when installation starts. + */ +!macro InstallStartCleanupCommon + + !ifndef InstallStartCleanupCommon + !insertmacro CleanVirtualStore + !insertmacro EndUninstallLog + !insertmacro OnInstallUninstall + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define InstallStartCleanupCommon "!insertmacro InstallStartCleanupCommonCall" + + Function InstallStartCleanupCommon + + ; Remove files not removed by parsing the uninstall.log + Delete "$INSTDIR\install_wizard.log" + Delete "$INSTDIR\install_status.log" + + RmDir /r "$INSTDIR\updates" + Delete "$INSTDIR\updates.xml" + Delete "$INSTDIR\active-update.xml" + + ; Remove files from the uninstall directory. + ${If} ${FileExists} "$INSTDIR\uninstall" + Delete "$INSTDIR\uninstall\*wizard*" + Delete "$INSTDIR\uninstall\uninstall.ini" + Delete "$INSTDIR\uninstall\cleanup.log" + Delete "$INSTDIR\uninstall\uninstall.update" + ${OnInstallUninstall} + ${EndIf} + + ; Since we write to the uninstall.log in this directory during the + ; installation create the directory if it doesn't already exist. + IfFileExists "$INSTDIR\uninstall" +2 +1 + CreateDirectory "$INSTDIR\uninstall" + + ; Application update uses a directory named tobedeleted in the $INSTDIR to + ; delete files on OS reboot when they are in use. Try to delete this + ; directory if it exists. + ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}" + RmDir /r "$INSTDIR\${TO_BE_DELETED}" + ${EndIf} + + ; Remove files that may be left behind by the application in the + ; VirtualStore directory. + ${CleanVirtualStore} + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro InstallStartCleanupCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call InstallStartCleanupCommon + !verbose pop +!macroend + +/** + * Performs common cleanup operations after the actual installation. + * This macro should be called last during the installation. + */ +!macro InstallEndCleanupCommon + + !ifndef InstallEndCleanupCommon + !insertmacro EndUninstallLog + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define InstallEndCleanupCommon "!insertmacro InstallEndCleanupCommonCall" + + Function InstallEndCleanupCommon + + ; Close the file handle to the uninstall.log + ${EndUninstallLog} + + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro InstallEndCleanupCommonCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call InstallEndCleanupCommon + !verbose pop +!macroend + + +################################################################################ +# UAC Related Macros + +/** + * Provides UAC elevation support (requires the UAC plugin). + * + * $0 = return values from calls to the UAC plugin (always uses $0) + * $R9 = return values from GetParameters and GetOptions macros + */ +!macro ElevateUAC + + !ifndef ${_MOZFUNC_UN}ElevateUAC + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetOptions + !insertmacro ${_MOZFUNC_UN_TMP}GetParameters + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}ElevateUAC "!insertmacro ${_MOZFUNC_UN}ElevateUACCall" + + Function ${_MOZFUNC_UN}ElevateUAC + Push $R9 + Push $0 + +!ifndef NONADMIN_ELEVATE + UAC::IsAdmin + ; If the user is not an admin already + ${If} "$0" != "1" + UAC::SupportsUAC + ; If the system supports UAC + ${If} "$0" == "1" + UAC::GetElevationType + ; If the user account has a split token + ${If} "$0" == "3" + UAC::RunElevated + UAC::Unload + ; Nothing besides UAC initialized so no need to call OnEndCommon + Quit + ${EndIf} + ${EndIf} + ${Else} + ${GetParameters} $R9 + ${If} $R9 != "" + ClearErrors + ${GetOptions} "$R9" "/UAC:" $0 + ; If the command line contains /UAC then we need to initialize + ; the UAC plugin to use UAC::ExecCodeSegment to execute code in + ; the non-elevated context. + ${Unless} ${Errors} + UAC::RunElevated + ${EndUnless} + ${EndIf} + ${EndIf} +!else + UAC::IsAdmin + ; If the user is not an admin already + ${If} "$0" != "1" + UAC::SupportsUAC + ; If the system supports UAC require that the user elevate + ${If} "$0" == "1" + UAC::GetElevationType + ; If the user account has a split token + ${If} "$0" == "3" + UAC::RunElevated + ${If} "$0" == "0" ; Was elevation successful + UAC::Unload + ; Nothing besides UAC initialized so no need to call OnEndCommon + Quit + ${EndIf} + ; Unload UAC since the elevation request was not successful and + ; install anyway. + UAC::Unload + ${EndIf} + ${Else} + ; Check if UAC is enabled. If the user has turned UAC on or off + ; without rebooting this value will be incorrect. This is an + ; edgecase that we have to live with when trying to allow + ; installing when the user doesn't have privileges such as a public + ; computer while trying to also achieve UAC elevation. When this + ; happens the user will be presented with the runas dialog if the + ; value is 1 and won't be presented with the UAC dialog when the + ; value is 0. + ReadRegDWord $R9 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableLUA" + ${If} "$R9" == "1" + ; This will display the UAC version of the runas dialog which + ; requires a password for an existing user account. + UAC::RunElevated + ${If} "$0" == "0" ; Was elevation successful + UAC::Unload + ; Nothing besides UAC initialized so no need to call OnEndCommon + Quit + ${EndIf} + ; Unload UAC since the elevation request was not successful and + ; install anyway. + UAC::Unload + ${EndIf} + ${EndIf} + ${Else} + ClearErrors + ${${_MOZFUNC_UN}GetParameters} $R9 + ${${_MOZFUNC_UN}GetOptions} "$R9" "/UAC:" $R9 + ; If the command line contains /UAC then we need to initialize the UAC + ; plugin to use UAC::ExecCodeSegment to execute code in the + ; non-elevated context. + ${Unless} ${Errors} + UAC::RunElevated + ${EndUnless} + ${EndIf} +!endif + + ClearErrors + + Pop $0 + Pop $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro ElevateUACCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call ElevateUAC + !verbose pop +!macroend + +!macro un.ElevateUACCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.ElevateUAC + !verbose pop +!macroend + +!macro un.ElevateUAC + !ifndef un.ElevateUAC + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro ElevateUAC + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Unloads the UAC plugin so the NSIS plugins can be removed when the installer + * and uninstaller exit. + * + * $R9 = return values from GetParameters and GetOptions macros + */ +!macro UnloadUAC + + !ifndef ${_MOZFUNC_UN}UnloadUAC + !define _MOZFUNC_UN_TMP_UnloadUAC ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP_UnloadUAC}GetOptions + !insertmacro ${_MOZFUNC_UN_TMP_UnloadUAC}GetParameters + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP_UnloadUAC} + !undef _MOZFUNC_UN_TMP_UnloadUAC + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}UnloadUAC "!insertmacro ${_MOZFUNC_UN}UnloadUACCall" + + Function ${_MOZFUNC_UN}UnloadUAC + Push $R9 + + ClearErrors + ${${_MOZFUNC_UN}GetParameters} $R9 + ${${_MOZFUNC_UN}GetOptions} "$R9" "/UAC:" $R9 + ; If the command line contains /UAC then we need to unload the UAC plugin + IfErrors +2 +1 + UAC::Unload + + ClearErrors + + Pop $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro UnloadUACCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call UnloadUAC + !verbose pop +!macroend + +!macro un.UnloadUACCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.UnloadUAC + !verbose pop +!macroend + +!macro un.UnloadUAC + !ifndef un.UnloadUAC + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro UnloadUAC + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + + +################################################################################ +# Macros for uninstall.log and install.log logging +# +# Since these are used by other macros they should be inserted first. All of +# these macros can be easily inserted using the _LoggingCommon macro. + +/** + * Adds all logging macros in the correct order in one fell swoop as well as + * the vars for the install.log and uninstall.log file handles. + */ +!macro _LoggingCommon + Var /GLOBAL fhInstallLog + Var /GLOBAL fhUninstallLog + !insertmacro StartInstallLog + !insertmacro EndInstallLog + !insertmacro StartUninstallLog + !insertmacro EndUninstallLog +!macroend +!define _LoggingCommon "!insertmacro _LoggingCommon" + +/** + * Creates a file named install.log in the install directory (e.g. $INSTDIR) + * and adds the installation started message to the install.log for this + * installation. This also adds the fhInstallLog and fhUninstallLog vars used + * for logging. + * + * $fhInstallLog = filehandle for $INSTDIR\install.log + * + * @param _APP_NAME + * Typically the BrandFullName + * @param _AB_CD + * The locale identifier + * @param _APP_VERSION + * The application version + * @param _GRE_VERSION + * The Gecko Runtime Engine version + * + * $R6 = _APP_NAME + * $R7 = _AB_CD + * $R8 = _APP_VERSION + * $R9 = _GRE_VERSION + */ +!macro StartInstallLog + + !ifndef StartInstallLog + !insertmacro GetTime + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define StartInstallLog "!insertmacro StartInstallLogCall" + + Function StartInstallLog + Exch $R9 + Exch 1 + Exch $R8 + Exch 2 + Exch $R7 + Exch 3 + Exch $R6 + Push $R5 + Push $R4 + Push $R3 + Push $R2 + Push $R1 + Push $R0 + Push $9 + + ${DeleteFile} "$INSTDIR\install.log" + FileOpen $fhInstallLog "$INSTDIR\install.log" w + FileWriteWord $fhInstallLog "65279" + + ${GetTime} "" "L" $9 $R0 $R1 $R2 $R3 $R4 $R5 + FileWriteUTF16LE $fhInstallLog "$R6 Installation Started: $R1-$R0-$9 $R3:$R4:$R5" + ${WriteLogSeparator} + + ${LogHeader} "Installation Details" + ${LogMsg} "Install Dir: $INSTDIR" + ${LogMsg} "Locale : $R7" + ${LogMsg} "App Version: $R8" + ${LogMsg} "GRE Version: $R9" + + ${If} ${IsWin10} + ${LogMsg} "OS Name : Windows 10" + ${ElseIf} ${AtLeastWin10} + ${LogMsg} "OS Name : Above Windows 10" + ${Else} + ${LogMsg} "OS Name : Unable to detect" + ${EndIf} + + ${LogMsg} "Target CPU : ${ARCH}" + + Pop $9 + Pop $R0 + Pop $R1 + Pop $R2 + Pop $R3 + Pop $R4 + Pop $R5 + Exch $R6 + Exch 3 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro StartInstallLogCall _APP_NAME _AB_CD _APP_VERSION _GRE_VERSION + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_APP_NAME}" + Push "${_AB_CD}" + Push "${_APP_VERSION}" + Push "${_GRE_VERSION}" + Call StartInstallLog + !verbose pop +!macroend + +/** + * Writes the installation finished message to the install.log and closes the + * file handles to the install.log and uninstall.log + * + * @param _APP_NAME + * + * $R9 = _APP_NAME + */ +!macro EndInstallLog + + !ifndef EndInstallLog + !insertmacro GetTime + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define EndInstallLog "!insertmacro EndInstallLogCall" + + Function EndInstallLog + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + Push $R4 + Push $R3 + Push $R2 + + ${WriteLogSeparator} + ${GetTime} "" "L" $R2 $R3 $R4 $R5 $R6 $R7 $R8 + FileWriteUTF16LE $fhInstallLog "$R9 Installation Finished: $R4-$R3-$R2 $R6:$R7:$R8$\r$\n" + FileClose $fhInstallLog + + Pop $R2 + Pop $R3 + Pop $R4 + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro EndInstallLogCall _APP_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_APP_NAME}" + Call EndInstallLog + !verbose pop +!macroend + +/** + * Opens the file handle to the uninstall.log. + * + * $fhUninstallLog = filehandle for $INSTDIR\uninstall\uninstall.log + */ +!macro StartUninstallLog + + !ifndef StartUninstallLog + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define StartUninstallLog "!insertmacro StartUninstallLogCall" + + Function StartUninstallLog + FileOpen $fhUninstallLog "$INSTDIR\uninstall\uninstall.log" w + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro StartUninstallLogCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call StartUninstallLog + !verbose pop +!macroend + +/** + * Closes the file handle to the uninstall.log. + */ +!macro EndUninstallLog + + !ifndef EndUninstallLog + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define EndUninstallLog "!insertmacro EndUninstallLogCall" + + Function EndUninstallLog + FileClose $fhUninstallLog + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro EndUninstallLogCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call EndUninstallLog + !verbose pop +!macroend + +/** + * Adds a section header to the human readable log. + * + * @param _HEADER + * The header text to write to the log. + */ +!macro LogHeader _HEADER + ${WriteLogSeparator} + FileWriteUTF16LE $fhInstallLog "${_HEADER}" + ${WriteLogSeparator} +!macroend +!define LogHeader "!insertmacro LogHeader" + +/** + * Adds a section message to the human readable log. + * + * @param _MSG + * The message text to write to the log. + */ +!macro LogMsg _MSG + FileWriteUTF16LE $fhInstallLog " ${_MSG}$\r$\n" +!macroend +!define LogMsg "!insertmacro LogMsg" + +/** + * Adds an uninstall entry to the uninstall log. + * + * @param _MSG + * The message text to write to the log. + */ +!macro LogUninstall _MSG + FileWrite $fhUninstallLog "${_MSG}$\r$\n" +!macroend +!define LogUninstall "!insertmacro LogUninstall" + +/** + * Adds a section divider to the human readable log. + */ +!macro WriteLogSeparator + FileWriteUTF16LE $fhInstallLog "$\r$\n----------------------------------------\ + ---------------------------------------$\r$\n" +!macroend +!define WriteLogSeparator "!insertmacro WriteLogSeparator" + + +################################################################################ +# Macros for managing the shortcuts log ini file + +/** + * Adds the most commonly used shortcut logging macros for the installer in one + * fell swoop. + */ +!macro _LoggingShortcutsCommon + !insertmacro LogDesktopShortcut + !insertmacro LogQuickLaunchShortcut + !insertmacro LogSMProgramsShortcut +!macroend +!define _LoggingShortcutsCommon "!insertmacro _LoggingShortcutsCommon" + +/** + * Creates the shortcuts log ini file with a UTF-16LE BOM if it doesn't exist. + */ +!macro initShortcutsLog + Push $R9 + + IfFileExists "$INSTDIR\uninstall\${SHORTCUTS_LOG}" +4 +1 + FileOpen $R9 "$INSTDIR\uninstall\${SHORTCUTS_LOG}" w + FileWriteWord $R9 "65279" + FileClose $R9 + + Pop $R9 +!macroend +!define initShortcutsLog "!insertmacro initShortcutsLog" + +/** + * Adds shortcut entries to the shortcuts log ini file. This macro is primarily + * a helper used by the LogDesktopShortcut, LogQuickLaunchShortcut, and + * LogSMProgramsShortcut macros but it can be used by other code if desired. If + * the value already exists the the value is not written to the file. + * + * @param _SECTION_NAME + * The section name to write to in the shortcut log ini file + * @param _FILE_NAME + * The shortcut's file name + * + * $R6 = return value from ReadIniStr for the shortcut file name + * $R7 = counter for supporting multiple shortcuts in the same location + * $R8 = _SECTION_NAME + * $R9 = _FILE_NAME + */ +!macro LogShortcut + + !ifndef LogShortcut + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define LogShortcut "!insertmacro LogShortcutCall" + + Function LogShortcut + Exch $R9 + Exch 1 + Exch $R8 + Push $R7 + Push $R6 + + ClearErrors + + !insertmacro initShortcutsLog + + StrCpy $R6 "" + StrCpy $R7 -1 + + StrCmp "$R6" "$R9" +5 +1 ; if the shortcut already exists don't add it + IntOp $R7 $R7 + 1 ; increment the counter + ReadIniStr $R6 "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "$R8" "Shortcut$R7" + IfErrors +1 -3 + WriteINIStr "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "$R8" "Shortcut$R7" "$R9" + + ClearErrors + + Pop $R6 + Pop $R7 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro LogShortcutCall _SECTION_NAME _FILE_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_SECTION_NAME}" + Push "${_FILE_NAME}" + Call LogShortcut + !verbose pop +!macroend + +/** + * Adds a Desktop shortcut entry to the shortcuts log ini file. + * + * @param _FILE_NAME + * The shortcut file name (e.g. shortcut.lnk) + */ +!macro LogDesktopShortcut + + !ifndef LogDesktopShortcut + !insertmacro LogShortcut + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define LogDesktopShortcut "!insertmacro LogDesktopShortcutCall" + + Function LogDesktopShortcut + Call LogShortcut + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro LogDesktopShortcutCall _FILE_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "DESKTOP" + Push "${_FILE_NAME}" + Call LogDesktopShortcut + !verbose pop +!macroend + +/** + * Adds a QuickLaunch shortcut entry to the shortcuts log ini file. + * + * @param _FILE_NAME + * The shortcut file name (e.g. shortcut.lnk) + */ +!macro LogQuickLaunchShortcut + + !ifndef LogQuickLaunchShortcut + !insertmacro LogShortcut + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define LogQuickLaunchShortcut "!insertmacro LogQuickLaunchShortcutCall" + + Function LogQuickLaunchShortcut + Call LogShortcut + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro LogQuickLaunchShortcutCall _FILE_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "QUICKLAUNCH" + Push "${_FILE_NAME}" + Call LogQuickLaunchShortcut + !verbose pop +!macroend + +/** + * Adds a Start Menu shortcut entry to the shortcuts log ini file. + * + * @param _FILE_NAME + * The shortcut file name (e.g. shortcut.lnk) + */ +!macro LogStartMenuShortcut + + !ifndef LogStartMenuShortcut + !insertmacro LogShortcut + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define LogStartMenuShortcut "!insertmacro LogStartMenuShortcutCall" + + Function LogStartMenuShortcut + Call LogShortcut + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro LogStartMenuShortcutCall _FILE_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "STARTMENU" + Push "${_FILE_NAME}" + Call LogStartMenuShortcut + !verbose pop +!macroend + +/** + * Adds a Start Menu Programs shortcut entry to the shortcuts log ini file. + * + * @param _FILE_NAME + * The shortcut file name (e.g. shortcut.lnk) + */ +!macro LogSMProgramsShortcut + + !ifndef LogSMProgramsShortcut + !insertmacro LogShortcut + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define LogSMProgramsShortcut "!insertmacro LogSMProgramsShortcutCall" + + Function LogSMProgramsShortcut + Call LogShortcut + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro LogSMProgramsShortcutCall _FILE_NAME + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "SMPROGRAMS" + Push "${_FILE_NAME}" + Call LogSMProgramsShortcut + !verbose pop +!macroend + +/** + * Adds the relative path from the Start Menu Programs directory for the + * application's Start Menu directory if it is different from the existing value + * to the shortcuts log ini file. + * + * @param _REL_PATH_TO_DIR + * The relative path from the Start Menu Programs directory to the + * program's directory. + * + * $R9 = _REL_PATH_TO_DIR + */ +!macro LogSMProgramsDirRelPath _REL_PATH_TO_DIR + Push $R9 + + !insertmacro initShortcutsLog + + ReadINIStr $R9 "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "SMPROGRAMS" "RelativePathToDir" + StrCmp "$R9" "${_REL_PATH_TO_DIR}" +2 +1 + WriteINIStr "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "SMPROGRAMS" "RelativePathToDir" "${_REL_PATH_TO_DIR}" + + Pop $R9 +!macroend +!define LogSMProgramsDirRelPath "!insertmacro LogSMProgramsDirRelPath" + +/** + * Copies the value for the relative path from the Start Menu programs directory + * (e.g. $SMPROGRAMS) to the Start Menu directory as it is stored in the + * shortcuts log ini file to the variable specified in the first parameter. + */ +!macro GetSMProgramsDirRelPath _VAR + ReadINIStr ${_VAR} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "SMPROGRAMS" \ + "RelativePathToDir" +!macroend +!define GetSMProgramsDirRelPath "!insertmacro GetSMProgramsDirRelPath" + +/** + * Copies the shortcuts log ini file path to the variable specified in the + * first parameter. + */ +!macro GetShortcutsLogPath _VAR + StrCpy ${_VAR} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" +!macroend +!define GetShortcutsLogPath "!insertmacro GetShortcutsLogPath" + + +################################################################################ +# Macros for managing specific Windows version features + +/** + * Sets the permitted layered service provider (LSP) categories + * for the application. Consumers should call this after an + * installation log section has completed since this macro will log the results + * to the installation log along with a header. + * + * !IMPORTANT - When calling this macro from an uninstaller do not specify a + * parameter. The paramter is hardcoded with 0x00000000 to remove + * the LSP category for the application when performing an + * uninstall. + * + * @param _LSP_CATEGORIES + * The permitted LSP categories for the application. When called by an + * uninstaller this will always be 0x00000000. + * + * $R5 = error code popped from the stack for the WSCSetApplicationCategory call + * $R6 = return value from the WSCSetApplicationCategory call + * $R7 = string length for the long path to the main application executable + * $R8 = long path to the main application executable + * $R9 = _LSP_CATEGORIES + */ +!macro SetAppLSPCategories + + !ifndef ${_MOZFUNC_UN}SetAppLSPCategories + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}SetAppLSPCategories "!insertmacro ${_MOZFUNC_UN}SetAppLSPCategoriesCall" + + Function ${_MOZFUNC_UN}SetAppLSPCategories + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR\${FileMainEXE}" $R8 + StrLen $R7 "$R8" + + ; Remove existing categories by setting the permitted categories to + ; 0x00000000 since new categories are ANDed with existing categories. If + ; the param value stored in $R9 is 0x00000000 then skip the removal since + ; the categories will be removed by the second call to + ; WSCSetApplicationCategory. + StrCmp "$R9" "0x00000000" +2 +1 + System::Call "Ws2_32::WSCSetApplicationCategory(w R8, i R7, w n, i 0,\ + i 0x00000000, i n, *i) i" + + ; Set the permitted LSP categories + System::Call "Ws2_32::WSCSetApplicationCategory(w R8, i R7, w n, i 0,\ + i R9, i n, *i .s) i.R6" + Pop $R5 + +!ifndef NO_LOG + ${LogHeader} "Setting Permitted LSP Categories" + StrCmp "$R6" 0 +3 +1 + ${LogMsg} "** ERROR Setting LSP Categories: $R5 **" + GoTo +2 + ${LogMsg} "Permitted LSP Categories: $R9" +!endif + + ClearErrors + + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro SetAppLSPCategoriesCall _LSP_CATEGORIES + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_LSP_CATEGORIES}" + Call SetAppLSPCategories + !verbose pop +!macroend + +!macro un.SetAppLSPCategoriesCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "0x00000000" + Call un.SetAppLSPCategories + !verbose pop +!macroend + +!macro un.SetAppLSPCategories + !ifndef un.SetAppLSPCategories + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro SetAppLSPCategories + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Checks if any pinned TaskBar lnk files point to the executable's path passed + * to the macro. + * + * @param _EXE_PATH + * The executable path + * @return _RESULT + * false if no pinned shotcuts were found for this install location. + * true if pinned shotcuts were found for this install location. + * + * $R5 = stores whether a TaskBar lnk file has been found for the executable + * $R6 = long path returned from GetShortCutTarget and GetLongPath + * $R7 = file name returned from FindFirst and FindNext + * $R8 = find handle for FindFirst and FindNext + * $R9 = _EXE_PATH and _RESULT + */ +!macro IsPinnedToTaskBar + + !ifndef IsPinnedToTaskBar + !insertmacro GetLongPath + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define IsPinnedToTaskBar "!insertmacro IsPinnedToTaskBarCall" + + Function IsPinnedToTaskBar + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + + StrCpy $R5 "false" + + ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar" + FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\TaskBar\*.lnk" + ${Do} + ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar\$R7" + ShellLink::GetShortCutTarget "$QUICKLAUNCH\User Pinned\TaskBar\$R7" + Pop $R6 + ${GetLongPath} "$R6" $R6 + ${If} "$R6" == "$R9" + StrCpy $R5 "true" + ${ExitDo} + ${EndIf} + ${EndIf} + ClearErrors + FindNext $R8 $R7 + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${Loop} + FindClose $R8 + ${EndIf} + + ClearErrors + + StrCpy $R9 $R5 + + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro IsPinnedToTaskBarCall _EXE_PATH _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_EXE_PATH}" + Call IsPinnedToTaskBar + Pop ${_RESULT} + !verbose pop +!macroend + +/** + * Checks if any pinned Start Menu lnk files point to the executable's path + * passed to the macro. + * + * @param _EXE_PATH + * The executable path + * @return _RESULT + * false if no pinned shotcuts were found for this install location. + * true if pinned shotcuts were found for this install location. + * + * $R5 = stores whether a Start Menu lnk file has been found for the executable + * $R6 = long path returned from GetShortCutTarget and GetLongPath + * $R7 = file name returned from FindFirst and FindNext + * $R8 = find handle for FindFirst and FindNext + * $R9 = _EXE_PATH and _RESULT + */ +!macro IsPinnedToStartMenu + + !ifndef IsPinnedToStartMenu + !insertmacro GetLongPath + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define IsPinnedToStartMenu "!insertmacro IsPinnedToStartMenuCall" + + Function IsPinnedToStartMenu + Exch $R9 + Push $R8 + Push $R7 + Push $R6 + Push $R5 + + StrCpy $R5 "false" + + ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu" + FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\StartMenu\*.lnk" + ${Do} + ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu\$R7" + ShellLink::GetShortCutTarget "$QUICKLAUNCH\User Pinned\StartMenu\$R7" + Pop $R6 + ${GetLongPath} "$R6" $R6 + ${If} "$R6" == "$R9" + StrCpy $R5 "true" + ${ExitDo} + ${EndIf} + ${EndIf} + ClearErrors + FindNext $R8 $R7 + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${Loop} + FindClose $R8 + ${EndIf} + + ClearErrors + + StrCpy $R9 $R5 + + Pop $R5 + Pop $R6 + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro IsPinnedToStartMenuCall _EXE_PATH _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_EXE_PATH}" + Call IsPinnedToStartMenu + Pop ${_RESULT} + !verbose pop +!macroend + +/** + * Gets the number of pinned shortcut lnk files pinned to the Task Bar. + * + * @return _RESULT + * number of pinned shortcut lnk files. + * + * $R7 = file name returned from FindFirst and FindNext + * $R8 = find handle for FindFirst and FindNext + * $R9 = _RESULT + */ +!macro PinnedToTaskBarLnkCount + + !ifndef PinnedToTaskBarLnkCount + !insertmacro GetLongPath + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define PinnedToTaskBarLnkCount "!insertmacro PinnedToTaskBarLnkCountCall" + + Function PinnedToTaskBarLnkCount + Push $R9 + Push $R8 + Push $R7 + + StrCpy $R9 0 + + ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar" + FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\TaskBar\*.lnk" + ${Do} + ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar\$R7" + IntOp $R9 $R9 + 1 + ${EndIf} + ClearErrors + FindNext $R8 $R7 + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${Loop} + FindClose $R8 + ${EndIf} + + ClearErrors + + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro PinnedToTaskBarLnkCountCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call PinnedToTaskBarLnkCount + Pop ${_RESULT} + !verbose pop +!macroend + +/** + * Gets the number of pinned shortcut lnk files pinned to the Start Menu. + * + * @return _RESULT + * number of pinned shortcut lnk files. + * + * $R7 = file name returned from FindFirst and FindNext + * $R8 = find handle for FindFirst and FindNext + * $R9 = _RESULT + */ +!macro PinnedToStartMenuLnkCount + + !ifndef PinnedToStartMenuLnkCount + !insertmacro GetLongPath + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define PinnedToStartMenuLnkCount "!insertmacro PinnedToStartMenuLnkCountCall" + + Function PinnedToStartMenuLnkCount + Push $R9 + Push $R8 + Push $R7 + + StrCpy $R9 0 + + ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu" + FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\StartMenu\*.lnk" + ${Do} + ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu\$R7" + IntOp $R9 $R9 + 1 + ${EndIf} + ClearErrors + FindNext $R8 $R7 + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${Loop} + FindClose $R8 + ${EndIf} + + ClearErrors + + Pop $R7 + Pop $R8 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro PinnedToStartMenuLnkCountCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call PinnedToStartMenuLnkCount + Pop ${_RESULT} + !verbose pop +!macroend + +/** + * Update Start Menu / TaskBar lnk files that point to the executable's path + * passed to the macro and all other shortcuts installed by the application with + * the current application user model ID. Requires ApplicationID. + * + * NOTE: this does not update Desktop shortcut application user model ID due to + * bug 633728. + * + * @param _EXE_PATH + * The main application executable path + * @param _APP_ID + * The application user model ID for the current install + * @return _RESULT + * false if no pinned shotcuts were found for this install location. + * true if pinned shotcuts were found for this install location. + */ +!macro UpdateShortcutAppModelIDs + + !ifndef UpdateShortcutAppModelIDs + !insertmacro GetLongPath + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define UpdateShortcutAppModelIDs "!insertmacro UpdateShortcutAppModelIDsCall" + + Function UpdateShortcutAppModelIDs + ; stack: path, appid + Exch $R9 ; stack: $R9, appid | $R9 = path + Exch 1 ; stack: appid, $R9 + Exch $R8 ; stack: $R8, $R9 | $R8 = appid + Push $R7 ; stack: $R7, $R8, $R9 + Push $R6 + Push $R5 + Push $R4 + Push $R3 ; stack: $R3, $R5, $R6, $R7, $R8, $R9 + Push $R2 + + ; $R9 = main application executable path + ; $R8 = appid + ; $R7 = path to the application's start menu programs directory + ; $R6 = path to the shortcut log ini file + ; $R5 = shortcut filename + ; $R4 = GetShortCutTarget result + + StrCpy $R3 "false" + + ; installed shortcuts + ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" $R6 + ${If} ${FileExists} "$R6" + ; Update the Start Menu shortcuts' App ID for this application + StrCpy $R2 -1 + ${Do} + IntOp $R2 $R2 + 1 ; Increment the counter + ClearErrors + ReadINIStr $R5 "$R6" "STARTMENU" "Shortcut$R2" + ${If} ${Errors} + ${ExitDo} + ${EndIf} + + ${If} ${FileExists} "$SMPROGRAMS\$R5" + ShellLink::GetShortCutTarget "$SMPROGRAMS\$$R5" + Pop $R4 + ${GetLongPath} "$R4" $R4 + ${If} "$R4" == "$R9" ; link path == install path + ApplicationID::Set "$SMPROGRAMS\$R5" "$R8" "true" + Pop $R4 + ${EndIf} + ${EndIf} + ${Loop} + + ; Update the Quick Launch shortcuts' App ID for this application + StrCpy $R2 -1 + ${Do} + IntOp $R2 $R2 + 1 ; Increment the counter + ClearErrors + ReadINIStr $R5 "$R6" "QUICKLAUNCH" "Shortcut$R2" + ${If} ${Errors} + ${ExitDo} + ${EndIf} + + ${If} ${FileExists} "$QUICKLAUNCH\$R5" + ShellLink::GetShortCutTarget "$QUICKLAUNCH\$R5" + Pop $R4 + ${GetLongPath} "$R4" $R4 + ${If} "$R4" == "$R9" ; link path == install path + ApplicationID::Set "$QUICKLAUNCH\$R5" "$R8" "true" + Pop $R4 + ${EndIf} + ${EndIf} + ${Loop} + + ; Update the Start Menu Programs shortcuts' App ID for this application + ClearErrors + ReadINIStr $R7 "$R6" "SMPROGRAMS" "RelativePathToDir" + ${Unless} ${Errors} + ${${_MOZFUNC_UN}GetLongPath} "$SMPROGRAMS\$R7" $R7 + ${Unless} "$R7" == "" + StrCpy $R2 -1 + ${Do} + IntOp $R2 $R2 + 1 ; Increment the counter + ClearErrors + ReadINIStr $R5 "$R6" "SMPROGRAMS" "Shortcut$R2" + ${If} ${Errors} + ${ExitDo} + ${EndIf} + + ${If} ${FileExists} "$R7\$R5" + ShellLink::GetShortCutTarget "$R7\$R5" + Pop $R4 + ${GetLongPath} "$R4" $R4 + ${If} "$R4" == "$R9" ; link path == install path + ApplicationID::Set "$R7\$R5" "$R8" "true" + Pop $R4 + ${EndIf} + ${EndIf} + ${Loop} + ${EndUnless} + ${EndUnless} + ${EndIf} + + StrCpy $R7 "$QUICKLAUNCH\User Pinned" + StrCpy $R3 "false" + + ; $R9 = main application executable path + ; $R8 = appid + ; $R7 = user pinned path + ; $R6 = find handle + ; $R5 = found filename + ; $R4 = GetShortCutTarget result + + ; TaskBar links + FindFirst $R6 $R5 "$R7\TaskBar\*.lnk" + ${Do} + ${If} ${FileExists} "$R7\TaskBar\$R5" + ShellLink::GetShortCutTarget "$R7\TaskBar\$R5" + Pop $R4 + ${If} "$R4" == "$R9" ; link path == install path + ApplicationID::Set "$R7\TaskBar\$R5" "$R8" "true" + Pop $R4 ; pop Set result off the stack + StrCpy $R3 "true" + ${EndIf} + ${EndIf} + ClearErrors + FindNext $R6 $R5 + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${Loop} + FindClose $R6 + + ; Start menu links + FindFirst $R6 $R5 "$R7\StartMenu\*.lnk" + ${Do} + ${If} ${FileExists} "$R7\StartMenu\$R5" + ShellLink::GetShortCutTarget "$R7\StartMenu\$R5" + Pop $R4 + ${If} "$R4" == "$R9" ; link path == install path + ApplicationID::Set "$R7\StartMenu\$R5" "$R8" "true" + Pop $R4 ; pop Set result off the stack + StrCpy $R3 "true" + ${EndIf} + ${EndIf} + ClearErrors + FindNext $R6 $R5 + ${If} ${Errors} + ${ExitDo} + ${EndIf} + ${Loop} + FindClose $R6 + + ClearErrors + + StrCpy $R9 $R3 + + Pop $R2 + Pop $R3 ; stack: $R4, $R5, $R6, $R7, $R8, $R9 + Pop $R4 ; stack: $R5, $R6, $R7, $R8, $R9 + Pop $R5 ; stack: $R6, $R7, $R8, $R9 + Pop $R6 ; stack: $R7, $R8, $R9 + Pop $R7 ; stack: $R8, $R9 + Exch $R8 ; stack: appid, $R9 | $R8 = old $R8 + Exch 1 ; stack: $R9, appid + Exch $R9 ; stack: path, appid | $R9 = old $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro UpdateShortcutAppModelIDsCall _EXE_PATH _APP_ID _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_APP_ID}" + Push "${_EXE_PATH}" + Call UpdateShortcutAppModelIDs + Pop ${_RESULT} + !verbose pop +!macroend + +!macro IsUserAdmin + ; Copied from: http://nsis.sourceforge.net/IsUserAdmin + Function IsUserAdmin + Push $R0 + Push $R1 + Push $R2 + + ClearErrors + UserInfo::GetName + IfErrors Win9x + Pop $R1 + UserInfo::GetAccountType + Pop $R2 + + StrCmp $R2 "Admin" 0 Continue + StrCpy $R0 "true" + Goto Done + + Continue: + + StrCmp $R2 "" Win9x + StrCpy $R0 "false" + Goto Done + + Win9x: + StrCpy $R0 "true" + + Done: + Pop $R2 + Pop $R1 + Exch $R0 + FunctionEnd +!macroend + +/** + * Retrieve if present or generate and store a 64 bit hash of an install path + * using the City Hash algorithm. On return the resulting id is saved in the + * $AppUserModelID variable declared by inserting this macro. InitHashAppModelId + * will attempt to load from HKLM/_REG_PATH first, then HKCU/_REG_PATH. If found + * in either it will return the hash it finds. If not found it will generate a + * new hash and attempt to store the hash in HKLM/_REG_PATH, then HKCU/_REG_PATH. + * Subsequent calls will then retreive the stored hash value. On any failure, + * $AppUserModelID will be set to an empty string. + * + * A second string that will be used for Private Browsing mode is also generated by + * taking the regular $AppUserModelID and appending ";PrivateBrowsingAUMID" to it. + * This is stored in $AppUserModelIDPrivate upon successful completion of this + * function. + * + * Registry format: root/_REG_PATH/"_EXE_PATH" = "hash" + * + * @param _EXE_PATH + * The main application executable path + * @param _REG_PATH + * The HKLM/HKCU agnostic registry path where the key hash should + * be stored. ex: "Software\Mozilla\Firefox\TaskBarIDs" + * @result (Var) $AppUserModelID and $AppUserModelIDPrivate contain the + * app model id and the private app model id, respectively. + */ +!macro InitHashAppModelId + !ifndef ${_MOZFUNC_UN}InitHashAppModelId + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !ifndef InitHashAppModelId + Var AppUserModelID + Var AppUserModelIDPrivate + !endif + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}InitHashAppModelId "!insertmacro ${_MOZFUNC_UN}InitHashAppModelIdCall" + + Function ${_MOZFUNC_UN}InitHashAppModelId + ; stack: apppath, regpath + Exch $R9 ; stack: $R9, regpath | $R9 = apppath + Exch 1 ; stack: regpath, $R9 + Exch $R8 ; stack: $R8, $R9 | $R8 = regpath + Push $R7 + + ${${_MOZFUNC_UN}GetLongPath} "$R9" $R9 + ; Always create a new AppUserModelID and overwrite the existing one + ; for the current installation path. + CityHash::GetCityHash64 "$R9" + Pop $AppUserModelID + ${If} $AppUserModelID == "error" + GoTo end + ${EndIf} + ClearErrors + WriteRegStr HKLM "$R8" "$R9" "$AppUserModelID" + ${If} ${Errors} + ClearErrors + WriteRegStr HKCU "$R8" "$R9" "$AppUserModelID" + ${If} ${Errors} + StrCpy $AppUserModelID "error" + ${EndIf} + ${EndIf} + + end: + ${If} "$AppUserModelID" == "error" + StrCpy $AppUserModelID "" + StrCpy $AppUserModelIDPrivate "" + ${Else} + StrCpy $AppUserModelIDPrivate "$AppUserModelID;PrivateBrowsingAUMID" + ${EndIf} + + ClearErrors + Pop $R7 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro InitHashAppModelIdCall _EXE_PATH _REG_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_REG_PATH}" + Push "${_EXE_PATH}" + Call InitHashAppModelId + !verbose pop +!macroend + +!macro un.InitHashAppModelIdCall _EXE_PATH _REG_PATH + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_REG_PATH}" + Push "${_EXE_PATH}" + Call un.InitHashAppModelId + !verbose pop +!macroend + +!macro un.InitHashAppModelId + !ifndef un.InitHashAppModelId + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro InitHashAppModelId + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Try to locate the default profile of this install from AppUserModelID + * using installs.ini. + * FIXME This could instead use the Install<AUMID> entries in profiles.ini? + * + * - `SetShellVarContext current` must be called before this macro so + * $APPDATA gets the current user's data. + * - InitHashAppModelId must have been called before this macro to set + * $AppUserModelID + * + * @result: Path of the profile directory (not checked for existence), + * or "" if not found, left on top of the stack. + */ +!macro FindInstallSpecificProfileMaybeUn _MOZFUNC_UN + Push $R0 + Push $0 + Push $1 + Push $2 + + StrCpy $R0 "" + ; Look for an install-specific profile, which might be listed as + ; either a relative or an absolute path (installs.ini doesn't say which). + ${If} ${FileExists} "$APPDATA\Mozilla\Firefox\installs.ini" + ClearErrors + ReadINIStr $1 "$APPDATA\Mozilla\Firefox\installs.ini" "$AppUserModelID" "Default" + ${IfNot} ${Errors} + ${${_MOZFUNC_UN}GetLongPath} "$APPDATA\Mozilla\Firefox\$1" $2 + ${If} ${FileExists} $2 + StrCpy $R0 $2 + ${Else} + ${${_MOZFUNC_UN}GetLongPath} "$1" $2 + ${If} ${FileExists} $2 + StrCpy $R0 $2 + ${EndIf} + ${EndIf} + ${EndIf} + ${EndIf} + + Pop $2 + Pop $1 + Pop $0 + Exch $R0 +!macroend +!define FindInstallSpecificProfile "!insertmacro FindInstallSpecificProfileMaybeUn ''" +!define un.FindInstallSpecificProfile "!insertmacro FindInstallSpecificProfileMaybeUn un." + +/** + * Copy the post-signing data, which was left alongside the installer + * by the self-extractor stub, into the global location for this data. + * + * If the post-signing data file doesn't exist, or is empty, "0" is + * pushed on the stack, and nothing is copied. + * Otherwise the first line of the post-signing data (including newline, + * if any) is pushed on the stack. + */ +!macro CopyPostSigningData + !ifndef CopyPostSigningData + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define CopyPostSigningData "Call CopyPostSigningData" + + Function CopyPostSigningData + Push $0 ; Stack: old $0 + Push $1 ; Stack: $1, old $0 + + ${LineRead} "$EXEDIR\postSigningData" "1" $0 + ${If} ${Errors} + ClearErrors + StrCpy $0 "0" + ${Else} + CopyFiles /SILENT "$EXEDIR\postSigningData" "$INSTDIR" + ${Endif} + + Pop $1 ; Stack: old $0 + Exch $0 ; Stack: postSigningData + FunctionEnd + + !verbose pop + !endif +!macroend + +################################################################################ +# Helpers for taskbar progress + +!ifndef CLSCTX_INPROC_SERVER + !define CLSCTX_INPROC_SERVER 1 +!endif + +!define CLSID_ITaskbarList {56fdf344-fd6d-11d0-958a-006097c9a090} +!define IID_ITaskbarList3 {ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf} +!define ITaskbarList3->SetProgressValue $ITaskbarList3->9 +!define ITaskbarList3->SetProgressState $ITaskbarList3->10 + +/** + * Creates a single uninitialized object of the ITaskbarList class with a + * reference to the ITaskbarList3 interface. This object can be used to set + * progress and state on the installer's taskbar icon using the helper macros + * in this section. + */ +!macro ITBL3Create + + !ifndef ${_MOZFUNC_UN}ITBL3Create + Var ITaskbarList3 + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}ITBL3Create "!insertmacro ${_MOZFUNC_UN}ITBL3CreateCall" + + Function ${_MOZFUNC_UN}ITBL3Create + ; Setting to 0 allows the helper macros to detect when the object was not + ; created. + StrCpy $ITaskbarList3 0 + ; Don't create when running silently. + ${Unless} ${Silent} + System::Call "ole32::CoCreateInstance(g '${CLSID_ITaskbarList}', \ + i 0, \ + i ${CLSCTX_INPROC_SERVER}, \ + g '${IID_ITaskbarList3}', \ + *i .s)" + Pop $ITaskbarList3 + ${EndUnless} + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro ITBL3CreateCall + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call ITBL3Create + !verbose pop +!macroend + +!macro un.ITBL3CreateCall _PATH_TO_IMAGE + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.ITBL3Create + !verbose pop +!macroend + +!macro un.ITBL3Create + !ifndef un.ITBL3Create + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro ITBL3Create + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Sets the percentage completed on the taskbar process icon progress indicator. + * + * @param _COMPLETED + * The proportion of the operation that has been completed in relation + * to _TOTAL. + * @param _TOTAL + * The value _COMPLETED will have when the operation has completed. + * + * $R8 = _COMPLETED + * $R9 = _TOTAL + */ +!macro ITBL3SetProgressValueCall _COMPLETED _TOTAL + Push ${_COMPLETED} + Push ${_TOTAL} + ${CallArtificialFunction} ITBL3SetProgressValue_ +!macroend + +!define ITBL3SetProgressValue "!insertmacro ITBL3SetProgressValueCall" +!define un.ITBL3SetProgressValue "!insertmacro ITBL3SetProgressValueCall" + +!macro ITBL3SetProgressValue_ + Exch $R9 + Exch 1 + Exch $R8 + ${If} $ITaskbarList3 <> 0 + System::Call "${ITaskbarList3->SetProgressValue}(i$HWNDPARENT, l$R8, l$R9)" + ${EndIf} + Exch $R8 + Exch 1 + Exch $R9 +!macroend + +; Normal state / no progress bar +!define TBPF_NOPROGRESS 0x00000000 +; Marquee style progress bar +!define TBPF_INDETERMINATE 0x00000001 +; Standard progress bar +!define TBPF_NORMAL 0x00000002 +; Red taskbar button to indicate an error occurred +!define TBPF_ERROR 0x00000004 +; Yellow taskbar button to indicate user attention (input) is required to +; resume progress +!define TBPF_PAUSED 0x00000008 + +/** + * Sets the state on the taskbar process icon progress indicator. + * + * @param _STATE + * The state to set on the taskbar icon progress indicator. Only one of + * the states defined above should be specified. + * + * $R9 = _STATE + */ +!macro ITBL3SetProgressStateCall _STATE + Push ${_STATE} + ${CallArtificialFunction} ITBL3SetProgressState_ +!macroend + +!define ITBL3SetProgressState "!insertmacro ITBL3SetProgressStateCall" +!define un.ITBL3SetProgressState "!insertmacro ITBL3SetProgressStateCall" + +!macro ITBL3SetProgressState_ + Exch $R9 + ${If} $ITaskbarList3 <> 0 + System::Call "${ITaskbarList3->SetProgressState}(i$HWNDPARENT, i$R9)" + ${EndIf} + Exch $R9 +!macroend + +################################################################################ +# Helpers for the new user interface + +!ifndef MAXDWORD + !define MAXDWORD 0xffffffff +!endif + +!ifndef DT_WORDBREAK + !define DT_WORDBREAK 0x0010 +!endif +!ifndef DT_SINGLELINE + !define DT_SINGLELINE 0x0020 +!endif +!ifndef DT_NOCLIP + !define DT_NOCLIP 0x0100 +!endif +!ifndef DT_CALCRECT + !define DT_CALCRECT 0x0400 +!endif +!ifndef DT_EDITCONTROL + !define DT_EDITCONTROL 0x2000 +!endif +!ifndef DT_RTLREADING + !define DT_RTLREADING 0x00020000 +!endif +!ifndef DT_NOFULLWIDTHCHARBREAK + !define DT_NOFULLWIDTHCHARBREAK 0x00080000 +!endif + +!define /ifndef GWL_STYLE -16 +!define /ifndef GWL_EXSTYLE -20 + +!ifndef WS_EX_NOINHERITLAYOUT + !define WS_EX_NOINHERITLAYOUT 0x00100000 +!endif + +!ifndef PBS_MARQUEE + !define PBS_MARQUEE 0x08 +!endif + +!ifndef PBM_SETRANGE32 + !define PBM_SETRANGE32 0x406 +!endif +!ifndef PBM_GETRANGE + !define PBM_GETRANGE 0x407 +!endif + +!ifndef SHACF_FILESYSTEM + !define SHACF_FILESYSTEM 1 +!endif + +!define MOZ_LOADTRANSPARENT ${LR_LOADFROMFILE}|${LR_LOADTRANSPARENT}|${LR_LOADMAP3DCOLORS} + +; Extend nsDialogs.nsh to support creating centered labels if it is already +; included +!ifmacrodef __NSD_DefineControl +!insertmacro __NSD_DefineControl LabelCenter +!define __NSD_LabelCenter_CLASS STATIC +!define __NSD_LabelCenter_STYLE ${DEFAULT_STYLES}|${SS_NOTIFY}|${SS_CENTER} +!define __NSD_LabelCenter_EXSTYLE ${WS_EX_TRANSPARENT} +!endif + +/** + * Draws an image file (BMP, GIF, or JPG) onto a bitmap control, with scaling. + * Adapted from https://stackoverflow.com/a/13405711/1508094 + * + * @param CONTROL bitmap control created by NSD_CreateBitmap + * @param IMAGE path to image file to draw to the bitmap + * @param HANDLE output bitmap handle which must be freed via NSD_FreeImage + * after nsDialogs::Show has been called + */ +!macro __SetStretchedImageOLE CONTROL IMAGE HANDLE + !ifndef IID_IPicture + !define IID_IPicture {7BF80980-BF32-101A-8BBB-00AA00300CAB} + !endif + !ifndef SRCCOPY + !define SRCCOPY 0xCC0020 + !endif + !ifndef HALFTONE + !define HALFTONE 4 + !endif + !ifndef IMAGE_BITMAP + !define IMAGE_BITMAP 0 + !endif + + Push $0 ; HANDLE + Push $1 ; memory DC + Push $2 ; IPicture created from IMAGE + Push $3 ; HBITMAP obtained from $2 + Push $4 ; BITMAPINFO obtained from $3 + Push $5 ; width of IMAGE + Push $6 ; height of IMAGE + Push $7 ; width of CONTROL + Push $8 ; height of CONTROL + Push $R0 ; CONTROL + + StrCpy $R0 ${CONTROL} ; in case ${CONTROL} is $0 + StrCpy $7 "" + StrCpy $8 "" + + System::Call '*(i, i, i, i) i.s' + Pop $0 + + ${If} $0 <> 0 + System::Call 'user32::GetClientRect(i R0, i r0)' + System::Call '*$0(i, i, i .s, i .s)' + System::Free $0 + Pop $7 + Pop $8 + ${EndIf} + + ${If} $7 > 0 + ${AndIf} $8 > 0 + System::Call 'oleaut32::OleLoadPicturePath(w"${IMAGE}",i0,i0,i0,g"${IID_IPicture}",*i.r2)i.r1' + ${If} $1 = 0 + System::Call 'user32::GetDC(i0)i.s' + System::Call 'gdi32::CreateCompatibleDC(iss)i.r1' + System::Call 'gdi32::CreateCompatibleBitmap(iss,ir7,ir8)i.r0' + System::Call 'user32::ReleaseDC(i0,is)' + System::Call '$2->3(*i.r3)i.r4' ; IPicture->get_Handle + ${If} $4 = 0 + System::Call 'gdi32::SetStretchBltMode(ir1,i${HALFTONE})' + System::Call '*(&i40,&i1024)i.r4' ; BITMAP / BITMAPINFO + System::Call 'gdi32::GetObject(ir3,i24,ir4)' + System::Call 'gdi32::SelectObject(ir1,ir0)i.s' + System::Call '*$4(i40,i.r5,i.r6,i0,i,i.s)' ; Grab size and bits-ptr AND init as BITMAPINFOHEADER + System::Call 'gdi32::GetDIBits(ir1,ir3,i0,i0,i0,ir4,i0)' ; init BITMAPINFOHEADER + System::Call 'gdi32::GetDIBits(ir1,ir3,i0,i0,i0,ir4,i0)' ; init BITMAPINFO + System::Call 'gdi32::StretchDIBits(ir1,i0,i0,ir7,ir8,i0,i0,ir5,ir6,is,ir4,i0,i${SRCCOPY})' + System::Call 'gdi32::SelectObject(ir1,is)' + System::Free $4 + SendMessage $R0 ${STM_SETIMAGE} ${IMAGE_BITMAP} $0 + ${EndIf} + System::Call 'gdi32::DeleteDC(ir1)' + System::Call '$2->2()' ; IPicture->release() + ${EndIf} + ${EndIf} + + Pop $R0 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Exch $0 + Pop ${HANDLE} +!macroend +!define SetStretchedImageOLE "!insertmacro __SetStretchedImageOLE" + +/** + * Display a task dialog box with custom strings and button labels. + * + * The task dialog is highly customizable. The specific style being used here + * is similar to a MessageBox except that the button text is customizable. + * MessageBox-style buttons are used instead of command link buttons; this can + * be made configurable if command buttons are needed. + * + * See https://msdn.microsoft.com/en-us/library/windows/desktop/bb760544.aspx + * for the TaskDialogIndirect function's documentation, and links to definitions + * of the TASKDIALOGCONFIG and TASKDIALOG_BUTTON structures it uses. + * + * @param INSTRUCTION Dialog header string; use empty string if unneeded + * @param CONTENT Secondary message string; use empty string if unneeded + * @param BUTTON1 Text for the first button, the one selected by default + * @param BUTTON2 Text for the second button + * + * @return One of the following values will be left on the stack: + * 1001 if the first button was clicked + * 1002 if the second button was clicked + * 2 (IDCANCEL) if the dialog was closed + * 0 on error + */ +!macro _ShowTaskDialog INSTRUCTION CONTENT BUTTON1 BUTTON2 + !ifndef SIZEOF_TASKDIALOGCONFIG_32BIT + !define SIZEOF_TASKDIALOGCONFIG_32BIT 96 + !endif + !ifndef TDF_ALLOW_DIALOG_CANCELLATION + !define TDF_ALLOW_DIALOG_CANCELLATION 0x0008 + !endif + !ifndef TDF_RTL_LAYOUT + !define TDF_RTL_LAYOUT 0x02000 + !endif + !ifndef TD_WARNING_ICON + !define TD_WARNING_ICON 0x0FFFF + !endif + + Push $0 ; return value + Push $1 ; TASKDIALOGCONFIG struct + Push $2 ; TASKDIALOG_BUTTON array + Push $3 ; dwFlags member of the TASKDIALOGCONFIG + + StrCpy $3 ${TDF_ALLOW_DIALOG_CANCELLATION} + !ifdef ${AB_CD}_rtl + IntOp $3 $3 | ${TDF_RTL_LAYOUT} + !endif + + ; Build an array of two TASKDIALOG_BUTTON structs + System::Call "*(i 1001, \ + w '${BUTTON1}', \ + i 1002, \ + w '${BUTTON2}' \ + ) p.r2" + ; Build a TASKDIALOGCONFIG struct + System::Call "*(i ${SIZEOF_TASKDIALOGCONFIG_32BIT}, \ + p $HWNDPARENT, \ + p 0, \ + i $3, \ + i 0, \ + w '$(INSTALLER_WIN_CAPTION)', \ + p ${TD_WARNING_ICON}, \ + w '${INSTRUCTION}', \ + w '${CONTENT}', \ + i 2, \ + p r2, \ + i 1001, \ + i 0, \ + p 0, \ + i 0, \ + p 0, \ + p 0, \ + p 0, \ + p 0, \ + p 0, \ + p 0, \ + p 0, \ + p 0, \ + i 0 \ + ) p.r1" + System::Call "comctl32::TaskDialogIndirect(p r1, *i 0 r0, p 0, p 0)" + System::Free $1 + System::Free $2 + + Pop $3 + Pop $2 + Pop $1 + Exch $0 +!macroend +!define ShowTaskDialog "!insertmacro _ShowTaskDialog" + +/** + * Removes a single style from a control. + * + * _HANDLE the handle of the control + * _STYLE the style to remove + */ +!macro _RemoveStyle _HANDLE _STYLE + Push $0 + + System::Call 'user32::GetWindowLongW(i ${_HANDLE}, i ${GWL_STYLE}) i .r0' + IntOp $0 $0 | ${_STYLE} + IntOp $0 $0 - ${_STYLE} + System::Call 'user32::SetWindowLongW(i ${_HANDLE}, i ${GWL_STYLE}, i r0)' + + Pop $0 +!macroend +!define RemoveStyle "!insertmacro _RemoveStyle" + +/** + * Adds a single extended style to a control. + * + * _HANDLE the handle of the control + * _EXSTYLE the extended style to add + */ +!macro _AddExStyle _HANDLE _EXSTYLE + Push $0 + + System::Call 'user32::GetWindowLongW(i ${_HANDLE}, i ${GWL_EXSTYLE}) i .r0' + IntOp $0 $0 | ${_EXSTYLE} + System::Call 'user32::SetWindowLongW(i ${_HANDLE}, i ${GWL_EXSTYLE}, i r0)' + + Pop $0 +!macroend +!define AddExStyle "!insertmacro _AddExStyle" + +/** + * Removes a single extended style from a control. + * + * _HANDLE the handle of the control + * _EXSTYLE the extended style to remove + */ +!macro _RemoveExStyle _HANDLE _EXSTYLE + Push $0 + + System::Call 'user32::GetWindowLongW(i ${_HANDLE}, i ${GWL_EXSTYLE}) i .r0' + IntOp $0 $0 | ${_EXSTYLE} + IntOp $0 $0 - ${_EXSTYLE} + System::Call 'user32::SetWindowLongW(i ${_HANDLE}, i ${GWL_EXSTYLE}, i r0)' + + Pop $0 +!macroend +!define RemoveExStyle "!insertmacro _RemoveExStyle" + +/** + * Set the necessary styles to configure the given window as right-to-left + * + * _HANDLE the handle of the control to configure + */ +!macro _MakeWindowRTL _HANDLE + !define /ifndef WS_EX_RIGHT 0x00001000 + !define /ifndef WS_EX_LEFT 0x00000000 + !define /ifndef WS_EX_RTLREADING 0x00002000 + !define /ifndef WS_EX_LTRREADING 0x00000000 + !define /ifndef WS_EX_LAYOUTRTL 0x00400000 + + ${AddExStyle} ${_HANDLE} ${WS_EX_LAYOUTRTL} + ${RemoveExStyle} ${_HANDLE} ${WS_EX_RTLREADING} + ${RemoveExStyle} ${_HANDLE} ${WS_EX_RIGHT} + ${AddExStyle} ${_HANDLE} ${WS_EX_LEFT}|${WS_EX_LTRREADING} +!macroend +!define MakeWindowRTL "!insertmacro _MakeWindowRTL" + +/** + * Gets the extent of the specified text in pixels for sizing a control. + * + * _TEXT the text to get the text extent for + * _FONT the font to use when getting the text extent + * _RES_WIDTH return value - control width for the text + * _RES_HEIGHT return value - control height for the text + */ +!macro GetTextExtentCall _TEXT _FONT _RES_WIDTH _RES_HEIGHT + Push "${_TEXT}" + Push "${_FONT}" + ${CallArtificialFunction} GetTextExtent_ + Pop ${_RES_WIDTH} + Pop ${_RES_HEIGHT} +!macroend + +!define GetTextExtent "!insertmacro GetTextExtentCall" +!define un.GetTextExtent "!insertmacro GetTextExtentCall" + +!macro GetTextExtent_ + Exch $0 ; font + Exch 1 + Exch $1 ; text + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + Push $7 + + ; Reuse the existing NSIS control which is used for BrandingText instead of + ; creating a new control. + GetDlgItem $2 $HWNDPARENT 1028 + + System::Call 'user32::GetDC(i r2) i .r3' + System::Call 'gdi32::SelectObject(i r3, i r0)' + + StrLen $4 "$1" + + System::Call '*(i, i) i .r5' + System::Call 'gdi32::GetTextExtentPoint32W(i r3, t$\"$1$\", i r4, i r5)' + System::Call '*$5(i .r6, i .r7)' + System::Free $5 + + System::Call 'user32::ReleaseDC(i r2, i r3)' + + StrCpy $1 $7 + StrCpy $0 $6 + + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Exch $1 ; return height + Exch 1 + Exch $0 ; return width +!macroend + +/** + * Gets the width and the height of a control in pixels. + * + * _HANDLE the handle of the control + * _RES_WIDTH return value - control width for the text + * _RES_HEIGHT return value - control height for the text + */ +!macro GetDlgItemWidthHeightCall _HANDLE _RES_WIDTH _RES_HEIGHT + Push "${_HANDLE}" + ${CallArtificialFunction} GetDlgItemWidthHeight_ + Pop ${_RES_WIDTH} + Pop ${_RES_HEIGHT} +!macroend + +!define GetDlgItemWidthHeight "!insertmacro GetDlgItemWidthHeightCall" +!define un.GetDlgItemWidthHeight "!insertmacro GetDlgItemWidthHeightCall" + +!macro GetDlgItemWidthHeight_ + Exch $0 ; handle for the control + Push $1 + Push $2 + + System::Call '*(i, i, i, i) i .r2' + ; The left and top values will always be 0 so the right and bottom values + ; will be the width and height. + System::Call 'user32::GetClientRect(i r0, i r2)' + System::Call '*$2(i, i, i .r0, i .r1)' + System::Free $2 + + Pop $2 + Exch $1 ; return height + Exch 1 + Exch $0 ; return width +!macroend + +/** + * Gets the number of pixels from the beginning of the dialog to the end of a + * control in a RTL friendly manner. + * + * _HANDLE the handle of the control + * _RES_PX return value - pixels from the beginning of the dialog to the end of + * the control + */ +!macro GetDlgItemEndPXCall _HANDLE _RES_PX + Push "${_HANDLE}" + ${CallArtificialFunction} GetDlgItemEndPX_ + Pop ${_RES_PX} +!macroend + +!define GetDlgItemEndPX "!insertmacro GetDlgItemEndPXCall" +!define un.GetDlgItemEndPX "!insertmacro GetDlgItemEndPXCall" + +!macro GetDlgItemEndPX_ + Exch $0 ; handle of the control + Push $1 + Push $2 + + ; #32770 is the dialog class + FindWindow $1 "#32770" "" $HWNDPARENT + System::Call '*(i, i, i, i) i .r2' + System::Call 'user32::GetWindowRect(i r0, i r2)' + System::Call 'user32::MapWindowPoints(i 0, i r1,i r2, i 2)' + System::Call '*$2(i, i, i .r0, i)' + System::Free $2 + + Pop $2 + Pop $1 + Exch $0 ; pixels from the beginning of the dialog to the end of the control +!macroend + +/** + * Gets the number of pixels from the top of a dialog to the bottom of a control + * + * _CONTROL the handle of the control + * _RES_PX return value - pixels from the top of the dialog to the bottom + * of the control + */ +!macro GetDlgItemBottomPXCall _CONTROL _RES_PX + Push "${_CONTROL}" + ${CallArtificialFunction} GetDlgItemBottomPX_ + Pop ${_RES_PX} +!macroend + +!define GetDlgItemBottomPX "!insertmacro GetDlgItemBottomPXCall" +!define un.GetDlgItemBottomPX "!insertmacro GetDlgItemBottomPXCall" + +!macro GetDlgItemBottomPX_ + Exch $0 ; handle of the control + Push $1 + Push $2 + + ; #32770 is the dialog class + FindWindow $1 "#32770" "" $HWNDPARENT + System::Call '*(i, i, i, i) i .r2' + System::Call 'user32::GetWindowRect(i r0, i r2)' + System::Call 'user32::MapWindowPoints(i 0, i r1, i r2, i 2)' + System::Call '*$2(i, i, i, i .r0)' + System::Free $2 + + Pop $2 + Pop $1 + Exch $0 ; pixels from the top of the dialog to the bottom of the control +!macroend + +/** + * Gets the width and height for sizing a control that has the specified text. + * The control's height and width will be dynamically determined for the maximum + * width specified. + * + * _TEXT the text + * _FONT the font to use when getting the width and height + * _MAX_WIDTH the maximum width for the control in pixels + * _RES_WIDTH return value - control width for the text in pixels. + * This might be larger than _MAX_WIDTH if that constraint couldn't + * be satisfied, e.g. a single word that couldn't be broken up is + * longer than _MAX_WIDTH by itself. + * _RES_HEIGHT return value - control height for the text in pixels + */ +!macro GetTextWidthHeight + + !ifndef ${_MOZFUNC_UN}GetTextWidthHeight + !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN} + !insertmacro ${_MOZFUNC_UN_TMP}WordFind + !undef _MOZFUNC_UN + !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP} + !undef _MOZFUNC_UN_TMP + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}GetTextWidthHeight "!insertmacro ${_MOZFUNC_UN}GetTextWidthHeightCall" + + Function ${_MOZFUNC_UN}GetTextWidthHeight + ; Stack contents after each instruction (top of the stack on the left): + ; _MAX_WIDTH _FONT _TEXT + Exch $0 ; $0 _FONT _TEXT + Exch 1 ; _FONT $0 _TEXT + Exch $1 ; $1 $0 _TEXT + Exch 2 ; _TEXT $0 $1 + Exch $2 ; $2 $0 $1 + ; That's all the parameters, now save our scratch registers. + Push $3 ; handle to a temporary control for drawing the text into + Push $4 ; DC handle + Push $5 ; string length of the text argument + Push $6 ; RECT struct to call DrawText with + Push $7 ; width returned from DrawText + Push $8 ; height returned from DrawText + Push $9 ; flags to pass to DrawText + + StrCpy $9 "${DT_NOCLIP}|${DT_CALCRECT}|${DT_WORDBREAK}" + !ifdef ${AB_CD}_rtl + StrCpy $9 "$9|${DT_RTLREADING}" + !endif + + ; Reuse the existing NSIS control which is used for BrandingText instead + ; of creating a new control. + GetDlgItem $3 $HWNDPARENT 1028 + + System::Call 'user32::GetDC(i r3) i .r4' + System::Call 'gdi32::SelectObject(i r4, i r1)' + + StrLen $5 "$2" ; text length + System::Call '*(i, i, i r0, i) i .r6' + + System::Call 'user32::DrawTextW(i r4, t $\"$2$\", i r5, i r6, i r9)' + System::Call '*$6(i, i, i .r7, i .r8)' + System::Free $6 + + System::Call 'user32::ReleaseDC(i r3, i r4)' + + StrCpy $1 $8 + StrCpy $0 $7 + + ; Restore the values that were in our scratch registers. + Pop $9 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + ; Restore our parameter registers and return our results. + ; Stack contents after each instruction (top of the stack on the left): + ; $2 $0 $1 + Pop $2 ; $0 $1 + Exch 1 ; $1 $0 + Exch $1 ; _RES_HEIGHT $0 + Exch 1 ; $0 _RES_HEIGHT + Exch $0 ; _RES_WIDTH _RES_HEIGHT + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro GetTextWidthHeightCall _TEXT _FONT _MAX_WIDTH _RES_WIDTH _RES_HEIGHT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_TEXT}" + Push "${_FONT}" + Push "${_MAX_WIDTH}" + Call GetTextWidthHeight + Pop ${_RES_WIDTH} + Pop ${_RES_HEIGHT} + !verbose pop +!macroend + +!macro un.GetTextWidthHeightCall _TEXT _FONT _MAX_WIDTH _RES_WIDTH _RES_HEIGHT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_TEXT}" + Push "${_FONT}" + Push "${_MAX_WIDTH}" + Call un.GetTextWidthHeight + Pop ${_RES_WIDTH} + Pop ${_RES_HEIGHT} + !verbose pop +!macroend + +!macro un.GetTextWidthHeight + !ifndef un.GetTextWidthHeight + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro GetTextWidthHeight + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + +/** + * Convert a number of dialog units to a number of pixels. + * + * _DU Number of dialog units to convert + * _AXIS Which axis you want to convert a value along, X or Y + * _RV Register or variable to return the number of pixels in + */ +!macro _DialogUnitsToPixels _DU _AXIS _RV + Push $0 + Push $1 + + ; The dialog units value might be a string ending with a 'u', + ; so convert it to a number. + IntOp $0 "${_DU}" + 0 + + !if ${_AXIS} == 'Y' + System::Call '*(i 0, i 0, i 0, i r0) i .r1' + System::Call 'user32::MapDialogRect(p $HWNDPARENT, p r1)' + System::Call '*$1(i, i, i, i . r0)' + !else if ${_AXIS} == 'X' + System::Call '*(i 0, i 0, i r0, i 0) i .r1' + System::Call 'user32::MapDialogRect(p $HWNDPARENT, p r1)' + System::Call '*$1(i, i, i . r0, i)' + !else + !error "Invalid axis ${_AXIS} passed to DialogUnitsToPixels; please use X or Y" + !endif + System::Free $1 + + Pop $1 + Exch $0 + Pop ${_RV} +!macroend +!define DialogUnitsToPixels "!insertmacro _DialogUnitsToPixels" + +/** + * Convert a given left coordinate for a dialog control to flip the control to + * the other side of the dialog if we're using an RTL locale. + * + * _LEFT_DU Number of dialog units to convert + * _WIDTH Width of the control in either pixels or DU's + * If the string has a 'u' on the end, it will be interpreted as + * dialog units, otherwise it will be interpreted as pixels. + * _RV Register or variable to return converted coordinate, in pixels + */ +!macro _ConvertLeftCoordForRTLCall _LEFT_DU _WIDTH _RV + Push "${_LEFT_DU}" + Push "${_WIDTH}" + ${CallArtificialFunction} _ConvertLeftCoordForRTL + Pop ${_RV} +!macroend + +!define ConvertLeftCoordForRTL "!insertmacro _ConvertLeftCoordForRTLCall" +!define un.ConvertLeftCoordForRTL "!insertmacro _ConvertLeftCoordForRTLCall" + +!macro _ConvertLeftCoordForRTL + ; Stack contents after each instruction (top of the stack on the left): + ; _WIDTH _LEFT_DU + Exch $0 ; $0 _LEFT_DU + Exch 1 ; _LEFT_DU $0 + Exch $1 ; $1 $0 + ; That's all the parameters, now save our scratch registers. + Push $2 ; width of the entire dialog, in pixels + Push $3 ; _LEFT_DU converted to pixels + Push $4 ; temp string search result + + !ifndef ${AB_CD}_rtl + StrCpy $0 "$1" + !else + ${GetDlgItemWidthHeight} $HWNDPARENT $2 $3 + ${DialogUnitsToPixels} $1 X $3 + + ClearErrors + ${${_MOZFUNC_UN}WordFind} "$0" "u" "E+1{" $4 + ${IfNot} ${Errors} + ${DialogUnitsToPixels} $4 X $0 + ${EndIf} + + IntOp $1 $2 - $3 + IntOp $1 $1 - $0 + StrCpy $0 $1 + !endif + + ; Restore the values that were in our scratch registers. + Pop $4 + Pop $3 + Pop $2 + ; Restore our parameter registers and return our result. + ; Stack contents after each instruction (top of the stack on the left): + ; $1 $0 + Pop $1 ; $0 + Exch $0 ; _RV +!macroend + +/** + * Gets the elapsed time in seconds between two values in milliseconds stored as + * an int64. The caller will typically get the millisecond values using + * GetTickCount with a long return value as follows. + * System::Call "kernel32::GetTickCount()l .s" + * Pop $varname + * + * _START_TICK_COUNT + * _FINISH_TICK_COUNT + * _RES_ELAPSED_SECONDS return value - elapsed time between _START_TICK_COUNT + * and _FINISH_TICK_COUNT in seconds. + */ +!macro GetSecondsElapsedCall _START_TICK_COUNT _FINISH_TICK_COUNT _RES_ELAPSED_SECONDS + Push "${_START_TICK_COUNT}" + Push "${_FINISH_TICK_COUNT}" + ${CallArtificialFunction} GetSecondsElapsed_ + Pop ${_RES_ELAPSED_SECONDS} +!macroend + +!define GetSecondsElapsed "!insertmacro GetSecondsElapsedCall" +!define un.GetSecondsElapsed "!insertmacro GetSecondsElapsedCall" + +!macro GetSecondsElapsed_ + Exch $0 ; finish tick count + Exch 1 + Exch $1 ; start tick count + + System::Int64Op $0 - $1 + Pop $0 + ; Discard the top bits of the int64 by bitmasking with MAXDWORD + System::Int64Op $0 & ${MAXDWORD} + Pop $0 + + ; Convert from milliseconds to seconds + System::Int64Op $0 / 1000 + Pop $0 + + Pop $1 + Exch $0 ; return elapsed seconds +!macroend + +/** + * Create a process to execute a command line. If it is successfully created, + * wait on it with WaitForInputIdle, to avoid exiting the current process too + * early (exiting early can cause the created process's windows to be opened in + * the background). + * + * CMDLINE Is the command line to execute, like the argument to Exec + */ +!define ExecAndWaitForInputIdle "!insertmacro ExecAndWaitForInputIdle_" +!define CREATE_DEFAULT_ERROR_MODE 0x04000000 +!macro ExecAndWaitForInputIdle_ CMDLINE + ; derived from https://stackoverflow.com/a/13960786/3444805 by Anders Kjersem + Push $0 + Push $1 + Push $2 + + ; Command line + StrCpy $0 "${CMDLINE}" + + ; STARTUPINFO + System::Alloc 68 + Pop $1 + ; fill in STARTUPINFO.cb (first field) with sizeof(STARTUPINFO) + System::Call "*$1(i 68)" + + ; PROCESS_INFORMATION + System::Call "*(i, i, i, i) i . r2" + + ; CREATE_DEFAULT_ERROR_MODE follows NSIS myCreateProcess used in Exec + System::Call "kernel32::CreateProcessW(i 0, t r0, i 0, i 0, i 0, i ${CREATE_DEFAULT_ERROR_MODE}, i 0, i 0, i r1, i r2) i . r0" + + System::Free $1 + ${If} $0 <> 0 + System::Call "*$2(i . r0, i . r1)" + ; $0: hProcess, $1: hThread + System::Call "user32::WaitForInputIdle(i $0, i 10000)" + System::Call "kernel32::CloseHandle(i $0)" + System::Call "kernel32::CloseHandle(i $1)" + ${EndIf} + System::Free $2 + + Pop $2 + Pop $1 + Pop $0 +!macroend + +Function WriteRegQWORD + ; Stack contents: + ; VALUE, VALUE_NAME, SUBKEY, ROOTKEY + Exch $3 ; $3, VALUE_NAME, SUBKEY, ROOTKEY + Exch 1 ; VALUE_NAME, $3, SUBKEY, ROOTKEY + Exch $2 ; $2, $3, SUBKEY, ROOTKEY + Exch 2 ; SUBKEY, $3, $2, ROOTKEY + Exch $1 ; $1, $3, $2, ROOTKEY + Exch 3 ; ROOTKEY, $3, $2, $1 + Exch $0 ; $0, $3, $2, $1 + System::Call "advapi32::RegSetKeyValueW(p r0, w r1, w r2, i 11, *l r3, i 8) i.r0" + ${IfNot} $0 = 0 + SetErrors + ${EndIf} + Pop $0 + Pop $3 + Pop $2 + Pop $1 +FunctionEnd +!macro WriteRegQWORD ROOTKEY SUBKEY VALUE_NAME VALUE + ${If} "${ROOTKEY}" == "HKCR" + Push 0x80000000 + ${ElseIf} "${ROOTKEY}" == "HKCU" + Push 0x80000001 + ${ElseIf} "${ROOTKEY}" == "HKLM" + Push 0x80000002 + ${Endif} + Push "${SUBKEY}" + Push "${VALUE_NAME}" + System::Int64Op ${VALUE} + 0 ; The result is pushed on the stack + Call WriteRegQWORD +!macroend +!define WriteRegQWORD "!insertmacro WriteRegQWORD" + +Function ReadRegQWORD + ; Stack contents: + ; VALUE_NAME, SUBKEY, ROOTKEY + Exch $2 ; $2, SUBKEY, ROOTKEY + Exch 1 ; SUBKEY, $2, ROOTKEY + Exch $1 ; $1, $2, ROOTKEY + Exch 2 ; ROOTKEY, $2, $1 + Exch $0 ; $0, $2, $1 + System::Call "advapi32::RegGetValueW(p r0, w r1, w r2, i 0x48, p 0, *l s, *i 8) i.r0" + ${IfNot} $0 = 0 + SetErrors + ${EndIf} + ; VALUE, $0, $2, $1 + Exch 3 ; $1, $0, $2, VALUE + Pop $1 ; $0, $2, VALUE + Pop $0 ; $2, VALUE + Pop $2 ; VALUE +FunctionEnd +!macro ReadRegQWORD DEST ROOTKEY SUBKEY VALUE_NAME + ${If} "${ROOTKEY}" == "HKCR" + Push 0x80000000 + ${ElseIf} "${ROOTKEY}" == "HKCU" + Push 0x80000001 + ${ElseIf} "${ROOTKEY}" == "HKLM" + Push 0x80000002 + ${Endif} + Push "${SUBKEY}" + Push "${VALUE_NAME}" + Call ReadRegQWORD + Pop ${DEST} +!macroend +!define ReadRegQWORD "!insertmacro ReadRegQWORD" + diff --git a/toolkit/mozapps/installer/windows/nsis/locale-fonts.nsh b/toolkit/mozapps/installer/windows/nsis/locale-fonts.nsh new file mode 100644 index 0000000000..0738a0ff1e --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/locale-fonts.nsh @@ -0,0 +1,675 @@ +# 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/. + +; Acholi +!if "${AB_CD}" == "ach" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Afrikaans +!if "${AB_CD}" == "af" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Akan +!if "${AB_CD}" == "ak" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Aragonese +!if "${AB_CD}" == "an" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Arabic +!if "${AB_CD}" == "ar" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Assamese +!if "${AB_CD}" == "as" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Asturian +!if "${AB_CD}" == "ast" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Azerbaijani +!if "${AB_CD}" == "az" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Belarusian +!if "${AB_CD}" == "be" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Bulgarian +!if "${AB_CD}" == "bg" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Bengali +!if "${AB_CD}" == "bn" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Breton +!if "${AB_CD}" == "br" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Bosnian +!if "${AB_CD}" == "bs" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Catalan +!if "${AB_CD}" == "ca" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Czech +!if "${AB_CD}" == "cs" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Kashubian +!if "${AB_CD}" == "csb" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Welsh +!if "${AB_CD}" == "cy" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Danish +!if "${AB_CD}" == "da" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; German +!if "${AB_CD}" == "de" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Greek +!if "${AB_CD}" == "el" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; English - Great Britain +!if "${AB_CD}" == "en-GB" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; English - United States +!if "${AB_CD}" == "en-US" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; English - South Africa +!if "${AB_CD}" == "en-ZA" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Esperanto +!if "${AB_CD}" == "eo" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Spanish - Argentina +!if "${AB_CD}" == "es-AR" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Spanish - Chile +!if "${AB_CD}" == "es-CL" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Spanish +!if "${AB_CD}" == "es-ES" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Spanish - Mexico +!if "${AB_CD}" == "es-MX" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Estonian +!if "${AB_CD}" == "et" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Basque +!if "${AB_CD}" == "eu" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Persian +!if "${AB_CD}" == "fa" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Fulah +!if "${AB_CD}" == "ff" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Finnish +!if "${AB_CD}" == "fi" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; French +!if "${AB_CD}" == "fr" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Frisian +!if "${AB_CD}" == "fy-NL" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Irish +!if "${AB_CD}" == "ga-IE" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Scottish Gaelic +!if "${AB_CD}" == "gd" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Galician +!if "${AB_CD}" == "gl" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Gujarati +!if "${AB_CD}" == "gu-IN" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Hawaiian +!if "${AB_CD}" == "haw" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Hebrew +!if "${AB_CD}" == "he" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; hindi +!if "${AB_CD}" == "hi-IN" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Croatian +!if "${AB_CD}" == "hr" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Upper Sorbian +!if "${AB_CD}" == "hsb" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Hungarian +!if "${AB_CD}" == "hu" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Armenian +!if "${AB_CD}" == "hy-AM" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Indonesian +!if "${AB_CD}" == "id" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Iloko +!if "${AB_CD}" == "ilo" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Icelandic +!if "${AB_CD}" == "is" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Italian +!if "${AB_CD}" == "it" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Japanese +!if "${AB_CD}" == "ja" +!define FONT_NAME1 "Meiryo UI" +!define FONT_FILE1 "meiryo.ttc" +!endif + +; Georgian +!if "${AB_CD}" == "ka" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Kazakh +!if "${AB_CD}" == "kk" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Khmer +!if "${AB_CD}" == "km" +!define FONT_NAME1 "Leelawadee UI" +!define FONT_FILE1 "LeelawUI.ttf" +!endif + +; Kannada +!if "${AB_CD}" == "kn" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Korean +!if "${AB_CD}" == "ko" +!define FONT_NAME1 "Malgun Gothic" +!define FONT_FILE1 "malgun.ttf" +!endif + +; Kurdish +!if "${AB_CD}" == "ku" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Luganda +!if "${AB_CD}" == "lg" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Ligurian +!if "${AB_CD}" == "lij" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Lithuanian +!if "${AB_CD}" == "lt" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Latvian +!if "${AB_CD}" == "lv" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Maithili +!if "${AB_CD}" == "mai" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Macedonian +!if "${AB_CD}" == "mk" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Malayalam +!if "${AB_CD}" == "ml" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Mongolian +!if "${AB_CD}" == "mn" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Marathi +!if "${AB_CD}" == "mr" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Malay +!if "${AB_CD}" == "ms" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Burmese +!if "${AB_CD}" == "my" +!define FONT_NAME1 "Myanmar Text" +!define FONT_FILE1 "mmrtext.ttf" +!endif + +; Norwegian Bokmål +!if "${AB_CD}" == "nb-NO" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Nepali Nepal +!if "${AB_CD}" == "ne-NP" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Dutch +!if "${AB_CD}" == "nl" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Norwegian Nynorsk +!if "${AB_CD}" == "nn-NO" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Southern Ndebele +!if "${AB_CD}" == "nr" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Northern Sotho +!if "${AB_CD}" == "nso" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Occitan +!if "${AB_CD}" == "oc" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Odia +!if "${AB_CD}" == "or" +!define FONT_NAME1 "Kalinga" +!define FONT_FILE1 "kalinga.ttf" +!endif + +; Punjabi +!if "${AB_CD}" == "pa-IN" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Polish +!if "${AB_CD}" == "pl" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Portugese - Brazil +!if "${AB_CD}" == "pt-BR" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Portugese +!if "${AB_CD}" == "pt-PT" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Romansh +!if "${AB_CD}" == "rm" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Romanian +!if "${AB_CD}" == "ro" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Russian +!if "${AB_CD}" == "ru" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Kinyarwanda +!if "${AB_CD}" == "rw" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Sakha +!if "${AB_CD}" == "sah" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Sinhala +!if "${AB_CD}" == "si" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Slovak +!if "${AB_CD}" == "sk" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Slovene +!if "${AB_CD}" == "sl" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Songhay +!if "${AB_CD}" == "son" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Albanian +!if "${AB_CD}" == "sq" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Serbian +!if "${AB_CD}" == "sr" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Swazi +!if "${AB_CD}" == "ss" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Southern Sotho +!if "${AB_CD}" == "st" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Swedish +!if "${AB_CD}" == "sv-Se" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Swahili +!if "${AB_CD}" == "sw" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Tamil +!if "${AB_CD}" == "ta" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Tamil - Sri Lanka +!if "${AB_CD}" == "ta-LK" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Telugu +!if "${AB_CD}" == "te" +!define FONT_NAME1 "Nirmala UI" +!define FONT_FILE1 "Nirmala.ttf" +!endif + +; Thai +!if "${AB_CD}" == "th" +!define FONT_NAME1 "Leelawadee UI" +!define FONT_FILE1 "LeelawUI.ttf" +!endif + +; Tswana +!if "${AB_CD}" == "tn" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Turkish +!if "${AB_CD}" == "tr" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Tsonga +!if "${AB_CD}" == "ts" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Uyghur +!if "${AB_CD}" == "ug" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Ukrainian +!if "${AB_CD}" == "uk" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Urdu +!if "${AB_CD}" == "ur" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Venda +!if "${AB_CD}" == "ve" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Vietnamese +!if "${AB_CD}" == "vi" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Wolof +!if "${AB_CD}" == "wo" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Xhosa +!if "${AB_CD}" == "xh" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif + +; Chinese (Simplified) +!if "${AB_CD}" == "zh-CN" +!define FONT_NAME1 "Microsoft YaHei UI" +!define FONT_FILE1 "msyh.ttc" +!endif + +; Chinese (Traditional) +!if "${AB_CD}" == "zh-TW" +!define FONT_NAME1 "Microsoft JhengHei UI" +!define FONT_FILE1 "msjh.ttc" +!endif + +; Zulu +!if "${AB_CD}" == "zu" +!define FONT_NAME1 "Segoe UI" +!define FONT_FILE1 "segoeui.ttf" +!endif diff --git a/toolkit/mozapps/installer/windows/nsis/locale-rtl.nlf b/toolkit/mozapps/installer/windows/nsis/locale-rtl.nlf new file mode 100644 index 0000000000..a4ea9ae6e1 --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/locale-rtl.nlf @@ -0,0 +1,12 @@ +# Header, don't edit +NLF v6 +# Start editing here +# Language ID +0 +# Font and size - dash (-) means default +- +- +# Codepage - dash (-) means ANSI code page +- +# RTL - anything else than RTL means LTR +RTL diff --git a/toolkit/mozapps/installer/windows/nsis/locale.nlf b/toolkit/mozapps/installer/windows/nsis/locale.nlf new file mode 100644 index 0000000000..0995026a0f --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/locale.nlf @@ -0,0 +1,12 @@ +# Header, don't edit +NLF v6 +# Start editing here +# Language ID +0 +# Font and size - dash (-) means default +- +- +# Codepage - dash (-) means ANSI code page +- +# RTL - anything else than RTL means LTR +- diff --git a/toolkit/mozapps/installer/windows/nsis/locales.nsi b/toolkit/mozapps/installer/windows/nsis/locales.nsi new file mode 100755 index 0000000000..938aef47f6 --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/locales.nsi @@ -0,0 +1,23 @@ +# 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/. + +/** + * "One off" locale configuration settings for RTL (e.g. locale text is read + * right to left). + */ + +; Arabic +!define ar_rtl + +; Hebrew +!define he_rtl + +; Persian +!define fa_rtl + +; Uyghur +!define ug_rtl + +; Urdu +!define ur_rtl diff --git a/toolkit/mozapps/installer/windows/nsis/makensis.mk b/toolkit/mozapps/installer/windows/nsis/makensis.mk new file mode 100755 index 0000000000..97667f4b00 --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/makensis.mk @@ -0,0 +1,133 @@ +# 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/. + +ifndef CONFIG_DIR +$(error CONFIG_DIR must be set before including makensis.mk) +endif + +ABS_CONFIG_DIR := $(abspath $(CONFIG_DIR)) + +SFX_MODULE ?= $(error SFX_MODULE is not defined) + +ifeq ($(TARGET_CPU), aarch64) +USE_UPX := +else +ifneq (,$(UPX)$(MOZ_AUTOMATION)) +USE_UPX := --use-upx +endif +endif + +TOOLKIT_NSIS_FILES = \ + common.nsh \ + locale.nlf \ + locale-fonts.nsh \ + locale-rtl.nlf \ + locales.nsi \ + overrides.nsh \ + setup.ico \ + $(NULL) + +CUSTOM_NSIS_PLUGINS = \ + AccessControl.dll \ + AppAssocReg.dll \ + ApplicationID.dll \ + BitsUtils.dll \ + CertCheck.dll \ + CityHash.dll \ + ExecInExplorer.dll \ + HttpPostFile.dll \ + InetBgDL.dll \ + InvokeShellVerb.dll \ + liteFirewallW.dll \ + nsJSON.dll \ + PinToTaskbar.dll \ + ServicesHelper.dll \ + ShellLink.dll \ + UAC.dll \ + WebBrowser.dll \ + $(NULL) + +CUSTOM_UI = \ + nsisui.exe \ + $(NULL) + +$(CONFIG_DIR)/setup.exe:: + $(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/,$(CUSTOM_UI)) $(CONFIG_DIR) + cd $(CONFIG_DIR) && $(MAKENSISU) $(MAKENSISU_FLAGS) installer.nsi +ifdef MOZ_STUB_INSTALLER + cd $(CONFIG_DIR) && $(MAKENSISU) $(MAKENSISU_FLAGS) stub.nsi +endif + +ifdef ZIP_IN +installer:: $(CONFIG_DIR)/setup.exe $(ZIP_IN) + @echo 'Packaging $(WIN32_INSTALLER_OUT).' + $(NSINSTALL) -D '$(ABS_DIST)/$(PKG_INST_PATH)' + $(PYTHON3) $(MOZILLA_DIR)/mach repackage installer \ + -o '$(ABS_DIST)/$(PKG_INST_PATH)$(PKG_INST_BASENAME).exe' \ + --package-name '$(MOZ_PKG_DIR)' \ + --package '$(ZIP_IN)' \ + --tag $(topsrcdir)/$(MOZ_BUILD_APP)/installer/windows/app.tag \ + --setupexe $(CONFIG_DIR)/setup.exe \ + --sfx-stub $(SFX_MODULE) \ + $(USE_UPX) +ifdef MOZ_STUB_INSTALLER + $(PYTHON3) $(MOZILLA_DIR)/mach repackage installer \ + -o '$(ABS_DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe' \ + --tag $(topsrcdir)/browser/installer/windows/stub.tag \ + --setupexe $(CONFIG_DIR)/setup-stub.exe \ + --sfx-stub $(SFX_MODULE) \ + $(USE_UPX) +endif +else +installer:: + $(error ZIP_IN must be set when building installer) +endif + +HELPER_DEPS = $(GLOBAL_DEPS) \ + $(addprefix $(srcdir)/,$(INSTALLER_FILES)) \ + $(addprefix $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/,$(BRANDING_FILES)) \ + $(srcdir)/nsis/defines.nsi.in \ + $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \ + $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) \ + $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) + +# For building the uninstaller during the application build so it can be +# included for mar file generation. +$(CONFIG_DIR)/helper.exe: $(HELPER_DEPS) + $(RM) -r $(CONFIG_DIR) + $(MKDIR) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/,$(BRANDING_FILES)) $(CONFIG_DIR) + $(call py_action,preprocessor defines.nsi,-Fsubstitution $(DEFINES) $(ACDEFINES) \ + $(srcdir)/nsis/defines.nsi.in -o $(CONFIG_DIR)/defines.nsi) + $(PYTHON3) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \ + --preprocess-locale $(topsrcdir) \ + $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR) + cd $(CONFIG_DIR) && $(MAKENSISU) $(MAKENSISU_FLAGS) uninstaller.nsi + +uninstaller:: $(CONFIG_DIR)/helper.exe + $(NSINSTALL) -D $(DIST)/bin/uninstall + cp $(CONFIG_DIR)/helper.exe $(DIST)/bin/uninstall + +ifdef MOZ_MAINTENANCE_SERVICE +maintenanceservice_installer:: + $(RM) -r $(CONFIG_DIR) + $(MKDIR) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/,$(BRANDING_FILES)) $(CONFIG_DIR) + $(call py_action,preprocessor defines.nsi,-Fsubstitution $(DEFINES) $(ACDEFINES) \ + $(srcdir)/nsis/defines.nsi.in -o $(CONFIG_DIR)/defines.nsi) + $(PYTHON3) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \ + --preprocess-locale $(topsrcdir) \ + $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR) + $(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR) + cd $(CONFIG_DIR) && $(MAKENSISU) $(MAKENSISU_FLAGS) maintenanceservice_installer.nsi + $(NSINSTALL) -D $(DIST)/bin/ + cp $(CONFIG_DIR)/maintenanceservice_installer.exe $(DIST)/bin +endif diff --git a/toolkit/mozapps/installer/windows/nsis/overrides.nsh b/toolkit/mozapps/installer/windows/nsis/overrides.nsh new file mode 100755 index 0000000000..a8f05622da --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/overrides.nsh @@ -0,0 +1,610 @@ +# 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/. + +################################################################################ +# Modified versions of macros provided by NSIS + +!ifndef OVERRIDES_INCLUDED +!define OVERRIDES_INCLUDED + +!ifndef ___X64__NSH___ +!include x64.nsh +!endif + +; When including a file check if its verbose macro is defined to prevent +; loading the file a second time. +!ifmacrondef TEXTFUNC_VERBOSE +!include TextFunc.nsh +!endif + +!ifmacrondef FILEFUNC_VERBOSE +!include FileFunc.nsh +!endif + +; This was added to NSIS 3.0.4 and is needed for Windows ARM64 support +!ifmacrondef GetNativeMachineArchitecture +!define GetNativeMachineArchitecture "!insertmacro GetNativeMachineArchitecture " +!macro GetNativeMachineArchitecture outvar + !define GetNativeMachineArchitecture_lbl lbl_GNMA_${__COUNTER__} + System::Call kernel32::GetCurrentProcess()p.s + System::Call kernel32::IsWow64Process2(ps,*i,*i0s) + Pop ${outvar} + IntCmp ${outvar} 0 "" ${GetNativeMachineArchitecture_lbl}_done ${GetNativeMachineArchitecture_lbl}_done + !if "${NSIS_PTR_SIZE}" <= 4 + !if "${NSIS_CHAR_SIZE}" <= 1 + System::Call 'USER32::CharNextW(w"")p.s' + Pop ${outvar} + IntPtrCmpU ${outvar} 0 "" ${GetNativeMachineArchitecture_lbl}_oldnt ${GetNativeMachineArchitecture_lbl}_oldnt + StrCpy ${outvar} 332 ; Always IMAGE_FILE_MACHINE_I386 on Win9x + Goto ${GetNativeMachineArchitecture_lbl}_done + ${GetNativeMachineArchitecture_lbl}_oldnt: + !endif + !endif + System::Call '*0x7FFE002E(&i2.s)' + Pop ${outvar} + ${GetNativeMachineArchitecture_lbl}_done: + !undef GetNativeMachineArchitecture_lbl +!macroend + +!macro _IsNativeMachineArchitecture _ignore _arc _t _f + !insertmacro _LOGICLIB_TEMP + ${GetNativeMachineArchitecture} $_LOGICLIB_TEMP + !insertmacro _= $_LOGICLIB_TEMP ${_arc} `${_t}` `${_f}` +!macroend + +!define IsNativeMachineArchitecture `"" IsNativeMachineArchitecture ` +!define IsNativeIA32 '${IsNativeMachineArchitecture} 332' ; Intel x86 +!define IsNativeAMD64 '${IsNativeMachineArchitecture} 34404' ; x86-64/x64 +!define IsNativeARM64 '${IsNativeMachineArchitecture} 43620' +!endif + +!verbose push +!verbose 3 +!ifndef _OVERRIDE_VERBOSE + !define _OVERRIDE_VERBOSE 3 +!endif +!verbose ${_OVERRIDE_VERBOSE} +!define OVERRIDE_VERBOSE `!insertmacro OVERRIDE_VERBOSE` +!define _OVERRIDE_UN +!define _OVERRIDE_S +!verbose pop + +!macro OVERRIDE_VERBOSE _VERBOSE + !verbose push + !verbose 3 + !undef _OVERRIDE_VERBOSE + !define _OVERRIDE_VERBOSE ${_VERBOSE} + !verbose pop +!macroend + +; Modified version of Locate from the NSIS File Functions Header v3.4 (it has +; the same version in earlier versions of NSIS even though it has changed) that +; is distributed with NSIS v2.46-Unicode. This version has the calls to +; SetDetailsPrint commented out. +; See <NSIS v2.46-Unicode App Dir>/include/FileFunc.nsh for more information. +!macro LocateNoDetailsCall _PATH _OPTIONS _FUNC + !verbose push + !verbose ${_OVERRIDE_VERBOSE} + Push $0 + Push `${_PATH}` + Push `${_OPTIONS}` + GetFunctionAddress $0 `${_FUNC}` + Push `$0` + Call LocateNoDetails + Pop $0 + !verbose pop +!macroend + +!macro LocateNoDetails + !ifndef ${_OVERRIDE_UN}LocateNoDetails + !verbose push + !verbose ${_OVERRIDE_VERBOSE} + !define ${_OVERRIDE_UN}LocateNoDetails `!insertmacro ${_OVERRIDE_UN}LocateNoDetailsCall` + + Function ${_OVERRIDE_UN}LocateNoDetails + Exch $2 + Exch + Exch $1 + Exch + Exch 2 + Exch $0 + Exch 2 + Push $3 + Push $4 + Push $5 + Push $6 + Push $7 + Push $8 + Push $9 + Push $R6 + Push $R7 + Push $R8 + Push $R9 + ClearErrors + + StrCpy $3 '' + StrCpy $4 '' + StrCpy $5 '' + StrCpy $6 '' + StrCpy $7 '' + StrCpy $8 0 + StrCpy $R7 '' + + StrCpy $R9 $0 1 -1 + StrCmp $R9 '\' 0 +3 + StrCpy $0 $0 -1 + goto -3 + IfFileExists '$0\*.*' 0 error + + option: + StrCpy $R9 $1 1 + StrCpy $1 $1 '' 1 + StrCmp $R9 ' ' -2 + StrCmp $R9 '' sizeset + StrCmp $R9 '/' 0 -4 + StrCpy $9 -1 + IntOp $9 $9 + 1 + StrCpy $R9 $1 1 $9 + StrCmp $R9 '' +2 + StrCmp $R9 '/' 0 -3 + StrCpy $R8 $1 $9 + StrCpy $R8 $R8 '' 2 + StrCpy $R9 $R8 '' -1 + StrCmp $R9 ' ' 0 +3 + StrCpy $R8 $R8 -1 + goto -3 + StrCpy $R9 $1 2 + StrCpy $1 $1 '' $9 + + StrCmp $R9 'L=' 0 mask + StrCpy $3 $R8 + StrCmp $3 '' +6 + StrCmp $3 'FD' +5 + StrCmp $3 'F' +4 + StrCmp $3 'D' +3 + StrCmp $3 'DE' +2 + StrCmp $3 'FDE' 0 error + goto option + + mask: + StrCmp $R9 'M=' 0 size + StrCpy $4 $R8 + goto option + + size: + StrCmp $R9 'S=' 0 gotosubdir + StrCpy $6 $R8 + goto option + + gotosubdir: + StrCmp $R9 'G=' 0 banner + StrCpy $7 $R8 + StrCmp $7 '' +3 + StrCmp $7 '1' +2 + StrCmp $7 '0' 0 error + goto option + + banner: + StrCmp $R9 'B=' 0 error + StrCpy $R7 $R8 + StrCmp $R7 '' +3 + StrCmp $R7 '1' +2 + StrCmp $R7 '0' 0 error + goto option + + sizeset: + StrCmp $6 '' default + StrCpy $9 0 + StrCpy $R9 $6 1 $9 + StrCmp $R9 '' +4 + StrCmp $R9 ':' +3 + IntOp $9 $9 + 1 + goto -4 + StrCpy $5 $6 $9 + IntOp $9 $9 + 1 + StrCpy $1 $6 1 -1 + StrCpy $6 $6 -1 $9 + StrCmp $5 '' +2 + IntOp $5 $5 + 0 + StrCmp $6 '' +2 + IntOp $6 $6 + 0 + + StrCmp $1 'B' 0 +3 + StrCpy $1 1 + goto default + StrCmp $1 'K' 0 +3 + StrCpy $1 1024 + goto default + StrCmp $1 'M' 0 +3 + StrCpy $1 1048576 + goto default + StrCmp $1 'G' 0 error + StrCpy $1 1073741824 + + default: + StrCmp $3 '' 0 +2 + StrCpy $3 'FD' + StrCmp $4 '' 0 +2 + StrCpy $4 '*.*' + StrCmp $7 '' 0 +2 + StrCpy $7 '1' + StrCmp $R7 '' 0 +2 + StrCpy $R7 '0' + StrCpy $7 'G$7B$R7' + + StrCpy $8 1 + Push $0 +; SetDetailsPrint textonly + + nextdir: + IntOp $8 $8 - 1 + Pop $R8 + + StrCpy $9 $7 2 2 + StrCmp $9 'B0' +3 + GetLabelAddress $9 findfirst + goto call +; DetailPrint 'Search in: $R8' + + findfirst: + FindFirst $0 $R7 '$R8\$4' + IfErrors subdir + StrCmp $R7 '.' 0 dir + FindNext $0 $R7 + StrCmp $R7 '..' 0 dir + FindNext $0 $R7 + IfErrors 0 dir + FindClose $0 + goto subdir + + dir: + IfFileExists '$R8\$R7\*.*' 0 file + StrCpy $R6 '' + StrCmp $3 'DE' +4 + StrCmp $3 'FDE' +3 + StrCmp $3 'FD' precall + StrCmp $3 'F' findnext precall + FindFirst $9 $R9 '$R8\$R7\*.*' + StrCmp $R9 '.' 0 +4 + FindNext $9 $R9 + StrCmp $R9 '..' 0 +2 + FindNext $9 $R9 + FindClose $9 + IfErrors precall findnext + + file: + StrCmp $3 'FDE' +3 + StrCmp $3 'FD' +2 + StrCmp $3 'F' 0 findnext + StrCpy $R6 0 + StrCmp $5$6 '' precall + FileOpen $9 '$R8\$R7' r + IfErrors +3 + FileSeek $9 0 END $R6 + FileClose $9 + System::Int64Op $R6 / $1 + Pop $R6 + StrCmp $5 '' +2 + IntCmp $R6 $5 0 findnext + StrCmp $6 '' +2 + IntCmp $R6 $6 0 0 findnext + + precall: + StrCpy $9 0 + StrCpy $R9 '$R8\$R7' + + call: + Push $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + Push $7 + Push $8 + Push $9 + Push $R7 + Push $R8 + StrCmp $9 0 +4 + StrCpy $R6 '' + StrCpy $R7 '' + StrCpy $R9 '' + Call $2 + Pop $R9 + Pop $R8 + Pop $R7 + Pop $9 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 + + IfErrors 0 +3 + FindClose $0 + goto error + StrCmp $R9 'StopLocateNoDetails' 0 +3 + FindClose $0 + goto clearstack + goto $9 + + findnext: + FindNext $0 $R7 + IfErrors 0 dir + FindClose $0 + + subdir: + StrCpy $9 $7 2 + StrCmp $9 'G0' end + FindFirst $0 $R7 '$R8\*.*' + StrCmp $R7 '.' 0 pushdir + FindNext $0 $R7 + StrCmp $R7 '..' 0 pushdir + FindNext $0 $R7 + IfErrors 0 pushdir + FindClose $0 + StrCmp $8 0 end nextdir + + pushdir: + IfFileExists '$R8\$R7\*.*' 0 +3 + Push '$R8\$R7' + IntOp $8 $8 + 1 + FindNext $0 $R7 + IfErrors 0 pushdir + FindClose $0 + StrCmp $8 0 end nextdir + + error: + SetErrors + + clearstack: + StrCmp $8 0 end + IntOp $8 $8 - 1 + Pop $R8 + goto clearstack + + end: +; SetDetailsPrint both + Pop $R9 + Pop $R8 + Pop $R7 + Pop $R6 + Pop $9 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro un.LocateNoDetailsCall _PATH _OPTIONS _FUNC + !verbose push + !verbose ${_OVERRIDE_VERBOSE} + Push $0 + Push `${_PATH}` + Push `${_OPTIONS}` + GetFunctionAddress $0 `${_FUNC}` + Push `$0` + Call un.LocateNoDetails + Pop $0 + !verbose pop +!macroend + +!macro un.LocateNoDetails + !ifndef un.LocateNoDetails + !verbose push + !verbose ${_OVERRIDE_VERBOSE} + !undef _OVERRIDE_UN + !define _OVERRIDE_UN `un.` + + !insertmacro LocateNoDetails + + !undef _OVERRIDE_UN + !define _OVERRIDE_UN + !verbose pop + !endif +!macroend + +; Modified version of TextCompare from the NSIS Text Functions Header v2.4 (it +; has the same version in earlier versions of NSIS even though it has changed) +; that is distributed with NSIS v2.46-Unicode. This version has the calls to +; SetDetailsPrint commented out. +; See <NSIS v2.46-Unicode App Dir>/include/TextFunc.nsh for more information. +!macro TextCompareNoDetailsCall _FILE1 _FILE2 _OPTION _FUNC + !verbose push + !verbose ${_OVERRIDE_VERBOSE} + Push $0 + Push `${_FILE1}` + Push `${_FILE2}` + Push `${_OPTION}` + GetFunctionAddress $0 `${_FUNC}` + Push `$0` + ${CallArtificialFunction} TextCompareNoDetails_ + Pop $0 + !verbose pop +!macroend + +!macro TextCompareNoDetailsSCall _FILE1 _FILE2 _OPTION _FUNC + !verbose push + !verbose ${_OVERRIDE_VERBOSE} + Push $0 + Push `${_FILE1}` + Push `${_FILE2}` + Push `${_OPTION}` + GetFunctionAddress $0 `${_FUNC}` + Push `$0` + ${CallArtificialFunction} TextCompareNoDetailsS_ + Pop $0 + !verbose pop +!macroend + + +!macro TextCompareNoDetailsBody _OVERRIDE_S + Exch $3 + Exch + Exch $2 + Exch + Exch 2 + Exch $1 + Exch 2 + Exch 3 + Exch $0 + Exch 3 + Push $4 + Push $5 + Push $6 + Push $7 + Push $8 + Push $9 + ClearErrors + + IfFileExists $0 0 TextFunc_TextCompareNoDetails${_OVERRIDE_S}_error + IfFileExists $1 0 TextFunc_TextCompareNoDetails${_OVERRIDE_S}_error + StrCmp $2 'FastDiff' +5 + StrCmp $2 'FastEqual' +4 + StrCmp $2 'SlowDiff' +3 + StrCmp $2 'SlowEqual' +2 + goto TextFunc_TextCompareNoDetails${_OVERRIDE_S}_error + + FileOpen $4 $0 r + IfErrors TextFunc_TextCompareNoDetails${_OVERRIDE_S}_error + FileOpen $5 $1 r + IfErrors TextFunc_TextCompareNoDetails${_OVERRIDE_S}_error +; SetDetailsPrint textonly + + StrCpy $6 0 + StrCpy $8 0 + + TextFunc_TextCompareNoDetails${_OVERRIDE_S}_nextline: + StrCmp${_OVERRIDE_S} $4 '' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_fast + IntOp $8 $8 + 1 + FileRead $4 $9 + IfErrors 0 +4 + FileClose $4 + StrCpy $4 '' + StrCmp${_OVERRIDE_S} $5 '' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_end + StrCmp $2 'FastDiff' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_fast + StrCmp $2 'FastEqual' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_fast TextFunc_TextCompareNoDetails${_OVERRIDE_S}_slow + + TextFunc_TextCompareNoDetails${_OVERRIDE_S}_fast: + StrCmp${_OVERRIDE_S} $5 '' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_call + IntOp $6 $6 + 1 + FileRead $5 $7 + IfErrors 0 +5 + FileClose $5 + StrCpy $5 '' + StrCmp${_OVERRIDE_S} $4 '' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_end + StrCmp $2 'FastDiff' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_call TextFunc_TextCompareNoDetails${_OVERRIDE_S}_close + StrCmp $2 'FastDiff' 0 +2 + StrCmp${_OVERRIDE_S} $7 $9 TextFunc_TextCompareNoDetails${_OVERRIDE_S}_nextline TextFunc_TextCompareNoDetails${_OVERRIDE_S}_call + StrCmp${_OVERRIDE_S} $7 $9 TextFunc_TextCompareNoDetails${_OVERRIDE_S}_call TextFunc_TextCompareNoDetails${_OVERRIDE_S}_nextline + + TextFunc_TextCompareNoDetails${_OVERRIDE_S}_slow: + StrCmp${_OVERRIDE_S} $4 '' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_close + StrCpy $6 '' +; DetailPrint '$8. $9' + FileSeek $5 0 + + TextFunc_TextCompareNoDetails${_OVERRIDE_S}_slownext: + FileRead $5 $7 + IfErrors 0 +2 + StrCmp $2 'SlowDiff' TextFunc_TextCompareNoDetails${_OVERRIDE_S}_call TextFunc_TextCompareNoDetails${_OVERRIDE_S}_nextline + StrCmp $2 'SlowDiff' 0 +2 + StrCmp${_OVERRIDE_S} $7 $9 TextFunc_TextCompareNoDetails${_OVERRIDE_S}_nextline TextFunc_TextCompareNoDetails${_OVERRIDE_S}_slownext + IntOp $6 $6 + 1 + StrCmp${_OVERRIDE_S} $7 $9 0 TextFunc_TextCompareNoDetails${_OVERRIDE_S}_slownext + + TextFunc_TextCompareNoDetails${_OVERRIDE_S}_call: + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + Push $7 + Push $8 + Push $9 + Call $3 + Pop $0 + Pop $9 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + StrCmp $0 'StopTextCompareNoDetails' 0 TextFunc_TextCompareNoDetails${_OVERRIDE_S}_nextline + + TextFunc_TextCompareNoDetails${_OVERRIDE_S}_close: + FileClose $4 + FileClose $5 + goto TextFunc_TextCompareNoDetails${_OVERRIDE_S}_end + + TextFunc_TextCompareNoDetails${_OVERRIDE_S}_error: + SetErrors + + TextFunc_TextCompareNoDetails${_OVERRIDE_S}_end: +; SetDetailsPrint both + Pop $9 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +!macroend + +!define TextCompareNoDetails `!insertmacro TextCompareNoDetailsCall` +!define un.TextCompareNoDetails `!insertmacro TextCompareNoDetailsCall` + +!macro TextCompareNoDetails +!macroend + +!macro un.TextCompareNoDetails +!macroend + +!macro TextCompareNoDetails_ + !verbose push + !verbose ${_OVERRIDE_VERBOSE} + + !insertmacro TextCompareNoDetailsBody '' + + !verbose pop +!macroend + +!define TextCompareNoDetailsS `!insertmacro TextCompareNoDetailsSCall` +!define un.TextCompareNoDetailsS `!insertmacro TextCompareNoDetailsSCall` + +!macro TextCompareNoDetailsS +!macroend + +!macro un.TextCompareNoDetailsS +!macroend + +!macro TextCompareNoDetailsS_ + !verbose push + !verbose ${_OVERRIDE_VERBOSE} + + !insertmacro TextCompareNoDetailsBody 'S' + + !verbose pop +!macroend + +!endif diff --git a/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py b/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py new file mode 100644 index 0000000000..280ab3d085 --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py @@ -0,0 +1,379 @@ +# preprocess-locale.py +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +# preprocess-locale.py provides two functions depending on the arguments passed +# to it when invoked. +# +# Preprocesses installer locale properties files and creates a basic NSIS nlf +# file when invoked with --preprocess-locale. +# +# Converts a UTF-8 file to a new UTF-16LE file when invoked with +# --convert-utf8-utf16le. + +from codecs import BOM_UTF16_LE +import io +from os.path import join, isfile +import sys +from optparse import OptionParser + + +def open_utf16le_file(path): + """ + Returns an opened file object with a a UTF-16LE byte order mark. + """ + fp = io.open(path, "w+b") + fp.write(BOM_UTF16_LE) + return fp + + +def get_locale_strings(path, prefix, middle, add_cr): + """ + Returns a string created by converting an installer locale properties file + into the format required by NSIS locale files. + + Parameters: + path - the path to the installer locale properties file to preprocess + prefix - a string to prefix each line with + middle - a string to insert between the name and value for each line + add_cr - boolean for whether to add an NSIS carriage return before NSIS + linefeeds when there isn't one already + """ + output = "" + fp = io.open(path, "r", encoding="utf-8") + for line in fp: + line = line.strip() + if line == "" or line[0] == "#": + continue + + name, value = line.split("=", 1) + value = value.strip() # trim whitespace from the start and end + if value and value[-1] == '"' and value[0] == '"': + value = value[1:-1] # remove " from the start and end + + if add_cr: + value = value.replace("\\n", "\\r\\n") # prefix $\n with $\r + value = value.replace("\\r\\r", "\\r") # replace $\r$\r with $\r + + value = value.replace('"', '$\\"') # prefix " with $\ + value = value.replace("\\r", "$\\r") # prefix \r with $ + value = value.replace("\\n", "$\\n") # prefix \n with $ + value = value.replace("\\t", "$\\t") # prefix \t with $ + + output += prefix + name.strip() + middle + ' "' + value + '"\n' + fp.close() + return output + + +def lookup(path, l10ndirs): + for d in l10ndirs: + if isfile(join(d, path)): + return join(d, path) + return join(l10ndirs[-1], path) + + +def preprocess_locale_files(config_dir, l10ndirs): + """ + Preprocesses the installer localized properties files into the format + required by NSIS and creates a basic NSIS nlf file. + + Parameters: + config_dir - the path to the destination directory + l10ndirs - list of paths to search for installer locale files + """ + + # Create the main NSIS language file + fp = open_utf16le_file(join(config_dir, "overrideLocale.nsh")) + locale_strings = get_locale_strings( + lookup("override.properties", l10ndirs), "LangString ^", " 0 ", False + ) + fp.write(locale_strings.encode("utf-16-le")) + fp.close() + + # Create the Modern User Interface language file + fp = open_utf16le_file(join(config_dir, "baseLocale.nsh")) + fp.write( + ( + """;NSIS Modern User Interface - Language File +;Compatible with Modern UI 1.68 +;Language: baseLocale (0) +!insertmacro MOZ_MUI_LANGUAGEFILE_BEGIN \"baseLocale\" +!define MUI_LANGNAME \"baseLocale\" +""" + ).encode("utf-16-le") + ) + locale_strings = get_locale_strings( + lookup("mui.properties", l10ndirs), "!define ", " ", True + ) + fp.write(locale_strings.encode("utf-16-le")) + fp.write("!insertmacro MOZ_MUI_LANGUAGEFILE_END\n".encode("utf-16-le")) + fp.close() + + # Create the custom language file for our custom strings + fp = open_utf16le_file(join(config_dir, "customLocale.nsh")) + locale_strings = get_locale_strings( + lookup("custom.properties", l10ndirs), "LangString ", " 0 ", True + ) + fp.write(locale_strings.encode("utf-16-le")) + fp.close() + + +def create_nlf_file(moz_dir, ab_cd, config_dir): + """ + Create a basic NSIS nlf file. + + Parameters: + moz_dir - the path to top source directory for the toolkit source + ab_cd - the locale code + config_dir - the path to the destination directory + """ + rtl = "-" + + # Check whether the locale is right to left from locales.nsi. + fp = io.open( + join(moz_dir, "toolkit/mozapps/installer/windows/nsis/locales.nsi"), + "r", + encoding="utf-8", + ) + for line in fp: + line = line.strip() + if line == "!define " + ab_cd + "_rtl": + rtl = "RTL" + break + + fp.close() + + # Create the main NSIS language file with RTL for right to left locales + # along with the default codepage, font name, and font size represented + # by the '-' character. + fp = open_utf16le_file(join(config_dir, "baseLocale.nlf")) + fp.write( + ( + """# Header, don't edit +NLF v6 +# Start editing here +# Language ID +0 +# Font and size - dash (-) means default +- +- +# Codepage - dash (-) means ANSI code page +- +# RTL - anything else than RTL means LTR +%s +""" + % rtl + ).encode("utf-16-le") + ) + fp.close() + + +def preprocess_locale_file(config_dir, l10ndirs, properties_filename, output_filename): + """ + Preprocesses a single localized properties file into the format + required by NSIS and creates a basic NSIS nlf file. + + Parameters: + config_dir - the path to the destination directory + l10ndirs - list of paths to search for installer locale files + properties_filename - the name of the properties file to search for + output_filename - the output filename to write + """ + + # Create the custom language file for our custom strings + fp = open_utf16le_file(join(config_dir, output_filename)) + locale_strings = get_locale_strings( + lookup(properties_filename, l10ndirs), "LangString ", " 0 ", True + ) + fp.write(locale_strings.encode("utf-16-le")) + fp.close() + + +def convert_utf8_utf16le(in_file_path, out_file_path): + """ + Converts a UTF-8 file to a new UTF-16LE file + + Arguments: + in_file_path - the path to the UTF-8 source file to convert + out_file_path - the path to the UTF-16LE destination file to create + """ + in_fp = open(in_file_path, "r", encoding="utf-8") + out_fp = open_utf16le_file(out_file_path) + out_fp.write(in_fp.read().encode("utf-16-le")) + in_fp.close() + out_fp.close() + + +if __name__ == "__main__": + usage = """usage: %prog command <args> + +Commands: + --convert-utf8-utf16le - Preprocesses installer locale properties files + --preprocess-locale - Preprocesses the installer localized properties + files into the format required by NSIS and + creates a basic NSIS nlf file. + --preprocess-single-file - Preprocesses a single properties file into the + format required by NSIS + --create-nlf-file - Creates a basic NSIS nlf file + +preprocess-locale.py --preprocess-locale <src> <locale> <code> <dest> + +Arguments: + <src> \tthe path to top source directory for the toolkit source + <locale>\tthe path to the installer's locale files + <code> \tthe locale code + <dest> \tthe path to the destination directory + + +preprocess-locale.py --preprocess-single-file <src> + <locale> + <dest> + <infile> + <outfile> + +Arguments: + <src> \tthe path to top source directory for the toolkit source + <locale> \tthe path to the installer's locale files + <dest> \tthe path to the destination directory + <infile> \tthe properties file to process + <outfile>\tthe nsh file to write + + +preprocess-locale.py --create-nlf-file <src> + <code> + <dest> + +Arguments: + <src> \tthe path to top source directory for the toolkit source + <code> \tthe locale code + <dest> \tthe path to the destination directory + + +preprocess-locale.py --convert-utf8-utf16le <src> <dest> + +Arguments: + <src> \tthe path to the UTF-8 source file to convert + <dest>\tthe path to the UTF-16LE destination file to create +""" + + preprocess_locale_args_help_string = """\ +Arguments to --preprocess-locale should be: + <src> <locale> <code> <dest> +or + <src> <code> <dest> --l10n-dir <dir> [--l10n-dir <dir> ...]""" + + preprocess_single_file_args_help_string = """\ +Arguments to --preprocess-single_file should be: + <src> <locale> <code> <dest> <infile> <outfile> +or + <src> <locale> <code> <dest> <infile> <outfile> + --l10n-dir <dir> [--l10n-dir <dir>...]""" + + create_nlf_args_help_string = """\ +Arguments to --create-nlf-file should be: + <src> <code> <dest>""" + + p = OptionParser(usage=usage) + p.add_option( + "--preprocess-locale", action="store_true", default=False, dest="preprocess" + ) + p.add_option( + "--preprocess-single-file", + action="store_true", + default=False, + dest="preprocessSingle", + ) + p.add_option( + "--create-nlf-file", action="store_true", default=False, dest="createNlf" + ) + p.add_option( + "--l10n-dir", + action="append", + default=[], + dest="l10n_dirs", + help="Add directory to lookup for locale files", + ) + p.add_option( + "--convert-utf8-utf16le", action="store_true", default=False, dest="convert" + ) + + options, args = p.parse_args() + + foundOne = False + if options.preprocess: + foundOne = True + if options.convert: + if foundOne: + p.error("More than one command specified") + else: + foundOne = True + if options.preprocessSingle: + if foundOne: + p.error("More than one command specified") + else: + foundOne = True + if options.createNlf: + if foundOne: + p.error("More than one command specified") + else: + foundOne = True + + if not foundOne: + p.error("No command specified") + + if options.preprocess: + if len(args) not in (3, 4): + p.error(preprocess_locale_args_help_string) + + # Parse args + pargs = args[:] + moz_dir = pargs[0] + if len(pargs) == 4: + l10n_dirs = [pargs[1]] + del pargs[1] + else: + if not options.l10n_dirs: + p.error(preprocess_locale_args_help_string) + l10n_dirs = options.l10n_dirs + ab_cd = pargs[1] + config_dir = pargs[2] + + # Create the output files + create_nlf_file(moz_dir, ab_cd, config_dir) + preprocess_locale_files(config_dir, l10n_dirs) + elif options.preprocessSingle: + if len(args) not in (4, 5): + p.error(preprocess_single_file_args_help_string) + + # Parse args + pargs = args[:] + moz_dir = pargs[0] + if len(pargs) == 5: + l10n_dirs = [pargs[1]] + del pargs[1] + else: + if not options.l10n_dirs: + p.error(preprocess_single_file_args_help_string) + l10n_dirs = options.l10n_dirs + config_dir = pargs[1] + in_file = pargs[2] + out_file = pargs[3] + + # Create the output files + preprocess_locale_file(config_dir, l10n_dirs, in_file, out_file) + elif options.createNlf: + if len(args) != 3: + p.error(create_nlf_args_help_string) + + # Parse args + pargs = args[:] + moz_dir = pargs[0] + ab_cd = pargs[1] + config_dir = pargs[2] + + # Create the output files + create_nlf_file(moz_dir, ab_cd, config_dir) + elif options.convert: + if len(args) != 2: + p.error("--convert-utf8-utf16le needs both of <src> <dest>") + convert_utf8_utf16le(*args) diff --git a/toolkit/mozapps/installer/windows/nsis/setup.ico b/toolkit/mozapps/installer/windows/nsis/setup.ico Binary files differnew file mode 100644 index 0000000000..9801fed54f --- /dev/null +++ b/toolkit/mozapps/installer/windows/nsis/setup.ico |