Exemplo n.º 1
0
    def __init__(self, marionette):
        BaseLib.__init__(self, marionette)

        self.app_info = AppInfo(marionette)
        self.prefs = Preferences(marionette)

        self._mar_channels = MARChannels(marionette)
        self._active_update = ActiveUpdate(marionette)
Exemplo n.º 2
0
        def download_via_update_wizard(dialog):
            """ Download the update via the old update wizard dialog.

            :param dialog: Instance of :class:`UpdateWizardDialog`.
            """
            prefs = Preferences(self.marionette)
            prefs.set_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE,
                           dialog.window_type)

            try:
                # If updates have already been found, proceed to download
                if dialog.wizard.selected_panel in [
                        dialog.wizard.updates_found_basic,
                        dialog.wizard.error_patching,
                ]:
                    dialog.select_next_page()

                # If incompatible add-on are installed, skip over the wizard page
                # TODO: Remove once we no longer support version Firefox 45.0ESR
                if self.puppeteer.utils.compare_version(
                        self.puppeteer.appinfo.version, '49.0a1') == -1:
                    if dialog.wizard.selected_panel == dialog.wizard.incompatible_list:
                        dialog.select_next_page()

                # Updates were stored in the cache, so no download is necessary
                if dialog.wizard.selected_panel in [
                        dialog.wizard.finished,
                        dialog.wizard.finished_background,
                ]:
                    pass

                # Download the update
                elif dialog.wizard.selected_panel == dialog.wizard.downloading:
                    if wait_for_finish:
                        start_time = datetime.now()
                        self.wait_for_download_finished(dialog, timeout)
                        self.download_duration = (datetime.now() -
                                                  start_time).total_seconds()

                        Wait(self.marionette).until(
                            lambda _: (dialog.wizard.selected_panel in [
                                dialog.wizard.finished,
                                dialog.wizard.finished_background,
                            ]),
                            message='Final wizard page has been selected.')

                else:
                    raise Exception(
                        'Invalid wizard page for downloading an update: {}'.
                        format(dialog.wizard.selected_panel))

            finally:
                self.marionette.clear_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE)
Exemplo n.º 3
0
    def __init__(self, marionette):
        BaseLib.__init__(self, marionette)

        self.prefs = Preferences(marionette)

        self.file_path = self.marionette.execute_script("""
          Components.utils.import('resource://gre/modules/Services.jsm');

          let file = Services.dirsvc.get('PrfDef', Components.interfaces.nsIFile);
          file.append('channel-prefs.js');

          return file.path;
        """)
Exemplo n.º 4
0
        def download_via_update_wizard(dialog):
            """ Download the update via the old update wizard dialog.

            :param dialog: Instance of :class:`UpdateWizardDialog`.
            """
            prefs = Preferences(lambda: self.marionette)
            prefs.set_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE, dialog.window_type)

            try:
                # If updates have already been found, proceed to download
                if dialog.wizard.selected_panel in [dialog.wizard.updates_found_basic,
                                                    dialog.wizard.updates_found_billboard,
                                                    dialog.wizard.error_patching,
                                                    ]:
                    dialog.select_next_page()

                # If incompatible add-on are installed, skip over the wizard page
                # TODO: Remove once we no longer support version Firefox 45.0ESR
                if self.utils.compare_version(self.appinfo.version, '49.0a1') == -1:
                    if dialog.wizard.selected_panel == dialog.wizard.incompatible_list:
                        dialog.select_next_page()

                # Updates were stored in the cache, so no download is necessary
                if dialog.wizard.selected_panel in [dialog.wizard.finished,
                                                    dialog.wizard.finished_background,
                                                    ]:
                    pass

                # Download the update
                elif dialog.wizard.selected_panel == dialog.wizard.downloading:
                    if wait_for_finish:
                        start_time = datetime.now()
                        self.wait_for_download_finished(dialog, timeout)
                        self.download_duration = (datetime.now() - start_time).total_seconds()

                        Wait(self.marionette).until(lambda _: (
                            dialog.wizard.selected_panel in [dialog.wizard.finished,
                                                             dialog.wizard.finished_background,
                                                             ]),
                                                    message='Final wizard page has been selected.')

                else:
                    raise Exception('Invalid wizard page for downloading an update: {}'.format(
                                    dialog.wizard.selected_panel))

            finally:
                prefs.restore_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE)
Exemplo n.º 5
0
    def __init__(self, marionette):
        BaseLib.__init__(self, marionette)

        self.app_info = AppInfo(marionette)
        self.prefs = Preferences(marionette)

        self._mar_channels = MARChannels(marionette)
        self._active_update = ActiveUpdate(marionette)
Exemplo n.º 6
0
    def __init__(self, marionette_getter, window_handle):
        BaseLib.__init__(self, marionette_getter)
        self._l10n = L10n(self.get_marionette)
        self._prefs = Preferences(self.get_marionette)
        self._windows = Windows(self.get_marionette)

        if window_handle not in self.marionette.chrome_window_handles:
            raise errors.UnknownWindowError(
                'Window with handle "%s" does not exist' % window_handle)
        self._handle = window_handle
Exemplo n.º 7
0
class UpdateChannel(BaseLib):
    """Class to handle the update channel as listed in channel-prefs.js"""
    REGEX_UPDATE_CHANNEL = re.compile(
        r'("app\.update\.channel", ")([^"].*)(?=")')

    def __init__(self, marionette):
        BaseLib.__init__(self, marionette)

        self.prefs = Preferences(marionette)

        self.file_path = self.marionette.execute_script("""
          Components.utils.import('resource://gre/modules/Services.jsm');

          let file = Services.dirsvc.get('PrfDef', Components.interfaces.nsIFile);
          file.append('channel-prefs.js');

          return file.path;
        """)

    @property
    def file_contents(self):
        """The contents of the channel-prefs.js file."""
        with open(self.file_path) as f:
            return f.read()

    @property
    def channel(self):
        """The name of the update channel as stored in the
        app.update.channel pref."""
        return self.prefs.get_pref('app.update.channel', True)

    @property
    def default_channel(self):
        """Get the default update channel

        :returns: Current default update channel
        """
        matches = re.search(self.REGEX_UPDATE_CHANNEL,
                            self.file_contents).groups()
        assert len(matches) == 2, 'Update channel value has been found'

        return matches[1]

    @default_channel.setter
    def default_channel(self, channel):
        """Set default update channel.

        :param channel: New default update channel
        """
        assert channel, 'Update channel has been specified'
        new_content = re.sub(self.REGEX_UPDATE_CHANNEL, r'\g<1>' + channel,
                             self.file_contents)
        with open(self.file_path, 'w') as f:
            f.write(new_content)
Exemplo n.º 8
0
class UpdateChannel(BaseLib):
    """Class to handle the update channel as listed in channel-prefs.js"""

    REGEX_UPDATE_CHANNEL = re.compile(r'("app\.update\.channel", ")([^"].*)(?=")')

    def __init__(self, marionette_getter):
        BaseLib.__init__(self, marionette_getter)

        self.prefs = Preferences(marionette_getter)

        self.file_path = self.marionette.execute_script(
            """
          Components.utils.import('resource://gre/modules/Services.jsm');

          let file = Services.dirsvc.get('PrfDef', Components.interfaces.nsIFile);
          file.append('channel-prefs.js');

          return file.path;
        """
        )

    @property
    def file_contents(self):
        """The contents of the channel-prefs.js file."""
        with open(self.file_path) as f:
            return f.read()

    @property
    def channel(self):
        """The name of the update channel as stored in the
        app.update.channel pref."""
        return self.prefs.get_pref("app.update.channel", True)

    @property
    def default_channel(self):
        """Get the default update channel

        :returns: Current default update channel
        """
        matches = re.search(self.REGEX_UPDATE_CHANNEL, self.file_contents).groups()
        assert len(matches) == 2, "Update channel value has been found"

        return matches[1]

    @default_channel.setter
    def default_channel(self, channel):
        """Set default update channel.

        :param channel: New default update channel
        """
        assert channel, "Update channel has been specified"
        new_content = re.sub(self.REGEX_UPDATE_CHANNEL, r"\g<1>" + channel, self.file_contents)
        with open(self.file_path, "w") as f:
            f.write(new_content)
Exemplo n.º 9
0
    def __init__(self, marionette):
        BaseLib.__init__(self, marionette)

        self.prefs = Preferences(marionette)

        self.file_path = self.marionette.execute_script("""
          Components.utils.import('resource://gre/modules/Services.jsm');

          let file = Services.dirsvc.get('PrfDef', Components.interfaces.nsIFile);
          file.append('channel-prefs.js');

          return file.path;
        """)
Exemplo n.º 10
0
class SoftwareUpdate(BaseLib):
    """The SoftwareUpdate API adds support for an easy access to the update process."""
    PREF_APP_DISTRIBUTION = 'distribution.id'
    PREF_APP_DISTRIBUTION_VERSION = 'distribution.version'
    PREF_APP_UPDATE_CHANNEL = 'app.update.channel'
    PREF_APP_UPDATE_URL = 'app.update.url'
    PREF_DISABLED_ADDONS = 'extensions.disabledAddons'

    def __init__(self, marionette):
        BaseLib.__init__(self, marionette)

        self.app_info = AppInfo(marionette)
        self.prefs = Preferences(marionette)

        self._mar_channels = MARChannels(marionette)
        self._active_update = ActiveUpdate(marionette)

    @property
    def ABI(self):
        """Get the customized ABI for the update service.

        :returns: ABI version
        """
        abi = self.app_info.XPCOMABI
        if mozinfo.isMac:
            abi += self.marionette.execute_script("""
              let macutils = Components.classes['@mozilla.org/xpcom/mac-utils;1']
                             .getService(Components.interfaces.nsIMacUtils);
              if (macutils.isUniversalBinary) {
                return '-u-' + macutils.architecturesInBinary;
              }
              return '';
            """)

        return abi

    @property
    def active_update(self):
        """ Holds a reference to an :class:`ActiveUpdate` object."""
        return self._active_update

    @property
    def allowed(self):
        """Check if the user has permissions to run the software update

        :returns: Status if the user has the permissions
        """
        return self.marionette.execute_script("""
          let aus = Components.classes['@mozilla.org/updates/update-service;1']
                    .getService(Components.interfaces.nsIApplicationUpdateService);
          return aus.canCheckForUpdates && aus.canApplyUpdates;
        """)

    @property
    def build_info(self):
        """Return information of the current build version

        :returns: A dictionary of build information
        """
        update_url = self.get_formatted_update_url(True)

        return {
            'buildid': self.app_info.appBuildID,
            'channel': self.update_channel,
            'disabled_addons': self.prefs.get_pref(self.PREF_DISABLED_ADDONS),
            'locale': self.app_info.locale,
            'mar_channels': self.mar_channels.channels,
            'update_url': update_url,
            'update_snippet': self.get_update_snippet(update_url),
            'user_agent': self.app_info.user_agent,
            'version': self.app_info.version
        }

    @property
    def is_complete_update(self):
        """Return true if the offered update is a complete update

        :returns: True if the offered update is a complete update
        """
        # Throw when isCompleteUpdate is called without an update. This should
        # never happen except if the test is incorrectly written.
        assert self.active_update.exists, 'An active update has been found'

        patch_count = self.active_update.patch_count
        assert patch_count == 1 or patch_count == 2,\
            'An update must have one or two patches included'

        # Ensure Partial and Complete patches produced have unique urls
        if patch_count == 2:
            patch0_url = self.active_update.get_patch_at(0)['URL']
            patch1_url = self.active_update.get_patch_at(1)['URL']
            assert patch0_url != patch1_url,\
                'Partial and Complete download URLs are different'

        return self.active_update.selected_patch['type'] == 'complete'

    @property
    def mar_channels(self):
        """ Holds a reference to a :class:`MARChannels` object."""
        return self._mar_channels

    @property
    def os_version(self):
        """Returns information about the OS version

        :returns: The OS version
        """
        return self.marionette.execute_script("""
          Components.utils.import("resource://gre/modules/Services.jsm");

          let osVersion;
          try {
            osVersion = Services.sysinfo.getProperty("name") + " " +
                        Services.sysinfo.getProperty("version");
          }
          catch (ex) {
          }

          if (osVersion) {
            try {
              osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
            }
            catch (e) {
              // Not all platforms have a secondary widget library,
              // so an error is nothing to worry about.
            }
            osVersion = encodeURIComponent(osVersion);
          }
          return osVersion;
        """)

    @property
    def patch_info(self):
        """ Returns information of the active update in the queue."""
        info = {'channel': self.update_channel}

        if (self.active_update.exists):
            info['buildid'] = self.active_update.buildID
            info['is_complete'] = self.is_complete_update
            info['size'] = self.active_update.selected_patch['size']
            info['type'] = self.update_type
            info['url_mirror'] = \
                self.active_update.selected_patch['finalURL'] or 'n/a'
            info['version'] = self.active_update.appVersion

        return info

    @property
    def staging_directory(self):
        """ Returns the path to the updates staging directory."""
        return self.marionette.execute_script("""
          let aus = Components.classes['@mozilla.org/updates/update-service;1']
                    .getService(Components.interfaces.nsIApplicationUpdateService);
          return aus.getUpdatesDirectory().path;
        """)

    @property
    def update_channel(self):
        """Return the currently used update channel."""
        return self.prefs.get_pref(self.PREF_APP_UPDATE_CHANNEL,
                                   default_branch=True)

    @update_channel.setter
    def update_channel(self, channel):
        """Set the update channel to be used for update checks.

        :param channel: New update channel to use

        """
        self.prefs.set_pref(self.PREF_APP_UPDATE_CHANNEL,
                            channel,
                            default_branch=True)

    @property
    def update_url(self):
        """Return the update URL used for update checks."""
        return self.prefs.get_pref(self.PREF_APP_UPDATE_URL,
                                   default_branch=True)

    @update_url.setter
    def update_url(self, url):
        """Set the update URL to be used for update checks.

        :param url: New update URL to use

        """
        self.prefs.set_pref(self.PREF_APP_UPDATE_URL, url, default_branch=True)

    @property
    def update_type(self):
        """Returns the type of the active update."""
        return self.active_update.type

    def force_fallback(self):
        """Update the update.status file and set the status to 'failed:6'"""
        with open(os.path.join(self.staging_directory, 'update.status'),
                  'w') as f:
            f.write('failed: 6\n')

    def get_update_snippet(self, update_url):
        """Retrieve contents of the update snippet.

        :param update_url: URL to the update snippet
        """
        snippet = None
        try:
            import urllib2
            response = urllib2.urlopen(update_url)
            snippet = response.read()
        except Exception:
            pass

        return snippet

    def get_formatted_update_url(self, force=False):
        """Retrieve the formatted AUS update URL the update snippet is retrieved from.

        :param force: Boolean flag to force an update check

        :returns: The URL of the update snippet
        """
        # Format the URL by replacing placeholders
        url = self.marionette.execute_script("""
          Components.utils.import("resource://gre/modules/UpdateUtils.jsm")
          return UpdateUtils.formatUpdateURL(arguments[0]);
        """,
                                             script_args=[self.update_url])

        if force:
            if '?' in url:
                url += '&'
            else:
                url += '?'
            url += 'force=1'

        return url
Exemplo n.º 11
0
class SoftwareUpdate(BaseLib):
    """The SoftwareUpdate API adds support for an easy access to the update process."""

    PREF_APP_DISTRIBUTION = "distribution.id"
    PREF_APP_DISTRIBUTION_VERSION = "distribution.version"
    PREF_APP_UPDATE_CHANNEL = "app.update.channel"
    PREF_APP_UPDATE_URL = "app.update.url"
    PREF_DISABLED_ADDONS = "extensions.disabledAddons"

    def __init__(self, marionette):
        BaseLib.__init__(self, marionette)

        self.app_info = AppInfo(marionette)
        self.prefs = Preferences(marionette)

        self._mar_channels = MARChannels(marionette)
        self._active_update = ActiveUpdate(marionette)

    @property
    def ABI(self):
        """Get the customized ABI for the update service.

        :returns: ABI version
        """
        abi = self.app_info.XPCOMABI
        if mozinfo.isMac:
            abi += self.marionette.execute_script(
                """
              let macutils = Components.classes['@mozilla.org/xpcom/mac-utils;1']
                             .getService(Components.interfaces.nsIMacUtils);
              if (macutils.isUniversalBinary) {
                return '-u-' + macutils.architecturesInBinary;
              }
              return '';
            """
            )

        return abi

    @property
    def active_update(self):
        """ Holds a reference to an :class:`ActiveUpdate` object."""
        return self._active_update

    @property
    def allowed(self):
        """Check if the user has permissions to run the software update

        :returns: Status if the user has the permissions
        """
        return self.marionette.execute_script(
            """
          let aus = Components.classes['@mozilla.org/updates/update-service;1']
                    .getService(Components.interfaces.nsIApplicationUpdateService);
          return aus.canCheckForUpdates && aus.canApplyUpdates;
        """
        )

    @property
    def build_info(self):
        """Return information of the current build version

        :returns: A dictionary of build information
        """
        update_url = self.get_formatted_update_url(True)

        return {
            "buildid": self.app_info.appBuildID,
            "channel": self.update_channel,
            "disabled_addons": self.prefs.get_pref(self.PREF_DISABLED_ADDONS),
            "locale": self.app_info.locale,
            "mar_channels": self.mar_channels.channels,
            "update_url": update_url,
            "update_snippet": self.get_update_snippet(update_url),
            "user_agent": self.app_info.user_agent,
            "version": self.app_info.version,
        }

    @property
    def is_complete_update(self):
        """Return true if the offered update is a complete update

        :returns: True if the offered update is a complete update
        """
        # Throw when isCompleteUpdate is called without an update. This should
        # never happen except if the test is incorrectly written.
        assert self.active_update.exists, "An active update has been found"

        patch_count = self.active_update.patch_count
        assert patch_count == 1 or patch_count == 2, "An update must have one or two patches included"

        # Ensure Partial and Complete patches produced have unique urls
        if patch_count == 2:
            patch0_url = self.active_update.get_patch_at(0)["URL"]
            patch1_url = self.active_update.get_patch_at(1)["URL"]
            assert patch0_url != patch1_url, "Partial and Complete download URLs are different"

        return self.active_update.selected_patch["type"] == "complete"

    @property
    def mar_channels(self):
        """ Holds a reference to a :class:`MARChannels` object."""
        return self._mar_channels

    @property
    def os_version(self):
        """Returns information about the OS version

        :returns: The OS version
        """
        return self.marionette.execute_script(
            """
          Components.utils.import("resource://gre/modules/Services.jsm");

          let osVersion;
          try {
            osVersion = Services.sysinfo.getProperty("name") + " " +
                        Services.sysinfo.getProperty("version");
          }
          catch (ex) {
          }

          if (osVersion) {
            try {
              osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
            }
            catch (e) {
              // Not all platforms have a secondary widget library,
              // so an error is nothing to worry about.
            }
            osVersion = encodeURIComponent(osVersion);
          }
          return osVersion;
        """
        )

    @property
    def patch_info(self):
        """ Returns information of the active update in the queue."""
        info = {"channel": self.update_channel}

        if self.active_update.exists:
            info["buildid"] = self.active_update.buildID
            info["is_complete"] = self.is_complete_update
            info["size"] = self.active_update.selected_patch["size"]
            info["type"] = self.update_type
            info["url_mirror"] = self.active_update.selected_patch["finalURL"] or "n/a"
            info["version"] = self.active_update.appVersion

        return info

    @property
    def staging_directory(self):
        """ Returns the path to the updates staging directory."""
        return self.marionette.execute_script(
            """
          let aus = Components.classes['@mozilla.org/updates/update-service;1']
                    .getService(Components.interfaces.nsIApplicationUpdateService);
          return aus.getUpdatesDirectory().path;
        """
        )

    @property
    def update_channel(self):
        """Return the currently used update channel."""
        return self.prefs.get_pref(self.PREF_APP_UPDATE_CHANNEL, default_branch=True)

    @update_channel.setter
    def update_channel(self, channel):
        """Set the update channel to be used for update checks.

        :param channel: New update channel to use

        """
        self.prefs.set_pref(self.PREF_APP_UPDATE_CHANNEL, channel, default_branch=True)

    @property
    def update_url(self):
        """Return the update URL used for update checks."""
        return self.prefs.get_pref(self.PREF_APP_UPDATE_URL, default_branch=True)

    @update_url.setter
    def update_url(self, url):
        """Set the update URL to be used for update checks.

        :param url: New update URL to use

        """
        self.prefs.set_pref(self.PREF_APP_UPDATE_URL, url, default_branch=True)

    @property
    def update_type(self):
        """Returns the type of the active update."""
        return self.active_update.type

    def force_fallback(self):
        """Update the update.status file and set the status to 'failed:6'"""
        with open(os.path.join(self.staging_directory, "update.status"), "w") as f:
            f.write("failed: 6\n")

    def get_update_snippet(self, update_url):
        """Retrieve contents of the update snippet.

        :param update_url: URL to the update snippet
        """
        snippet = None
        try:
            import urllib2

            response = urllib2.urlopen(update_url)
            snippet = response.read()
        except Exception:
            pass

        return snippet

    def get_formatted_update_url(self, force=False):
        """Retrieve the formatted AUS update URL the update snippet is retrieved from.

        :param force: Boolean flag to force an update check

        :returns: The URL of the update snippet
        """
        # Format the URL by replacing placeholders
        url = self.marionette.execute_script(
            """
          Components.utils.import("resource://gre/modules/UpdateUtils.jsm")
          return UpdateUtils.formatUpdateURL(arguments[0]);
        """,
            script_args=[self.update_url],
        )

        if force:
            if "?" in url:
                url += "&"
            else:
                url += "?"
            url += "force=1"

        return url
Exemplo n.º 12
0
class SoftwareUpdate(BaseLib):
    """The SoftwareUpdate API adds support for an easy access to the update process."""
    PREF_APP_DISTRIBUTION = 'distribution.id'
    PREF_APP_DISTRIBUTION_VERSION = 'distribution.version'
    PREF_APP_UPDATE_URL = 'app.update.url'
    PREF_APP_UPDATE_URL_OVERRIDE = 'app.update.url.override'
    PREF_DISABLED_ADDONS = 'extensions.disabledAddons'

    def __init__(self, marionette):
        BaseLib.__init__(self, marionette)

        self.app_info = AppInfo(marionette)
        self.prefs = Preferences(marionette)

        self._update_channel = UpdateChannel(marionette)
        self._mar_channels = MARChannels(marionette)
        self._active_update = ActiveUpdate(marionette)

    @property
    def ABI(self):
        """Get the customized ABI for the update service.

        :returns: ABI version
        """
        abi = self.app_info.XPCOMABI
        if mozinfo.isMac:
            abi += self.marionette.execute_script("""
              let macutils = Components.classes['@mozilla.org/xpcom/mac-utils;1']
                             .getService(Components.interfaces.nsIMacUtils);
              if (macutils.isUniversalBinary) {
                return '-u-' + macutils.architecturesInBinary;
              }
              return '';
            """)

        return abi

    @property
    def active_update(self):
        """ Holds a reference to an :class:`ActiveUpdate` object."""
        return self._active_update

    @property
    def allowed(self):
        """Check if the user has permissions to run the software update

        :returns: Status if the user has the permissions
        """
        return self.marionette.execute_script("""
          let aus = Components.classes['@mozilla.org/updates/update-service;1']
                    .getService(Components.interfaces.nsIApplicationUpdateService);
          return aus.canCheckForUpdates && aus.canApplyUpdates;
        """)

    @property
    def build_info(self):
        """Return information of the current build version

        :returns: A dictionary of build information
        """
        update_url = self.get_update_url(True)

        return {
            'buildid': self.app_info.appBuildID,
            'channel': self.update_channel.channel,
            'disabled_addons': self.prefs.get_pref(self.PREF_DISABLED_ADDONS),
            'locale': self.app_info.locale,
            'mar_channels': self.mar_channels.channels,
            'update_url': update_url,
            'update_snippet': self.get_update_snippet(update_url),
            'user_agent': self.app_info.user_agent,
            'version': self.app_info.version
        }

    @property
    def is_complete_update(self):
        """Return true if the offered update is a complete update

        :returns: True if the offered update is a complete update
        """
        # Throw when isCompleteUpdate is called without an update. This should
        # never happen except if the test is incorrectly written.
        assert self.active_update.exists, 'An active update has been found'

        patch_count = self.active_update.patch_count
        assert patch_count == 1 or patch_count == 2,\
            'An update must have one or two patches included'

        # Ensure Partial and Complete patches produced have unique urls
        if patch_count == 2:
            patch0_url = self.active_update.get_patch_at(0)['URL']
            patch1_url = self.active_update.get_patch_at(1)['URL']
            assert patch0_url != patch1_url,\
                'Partial and Complete download URLs are different'

        return self.active_update.selected_patch['type'] == 'complete'

    @property
    def mar_channels(self):
        """ Holds a reference to a :class:`MARChannels` object."""
        return self._mar_channels

    @property
    def os_version(self):
        """Returns information about the OS version

        :returns: The OS version
        """
        return self.marionette.execute_script("""
          Components.utils.import("resource://gre/modules/Services.jsm");

          let osVersion;
          try {
            osVersion = Services.sysinfo.getProperty("name") + " " +
                        Services.sysinfo.getProperty("version");
          }
          catch (ex) {
          }

          if (osVersion) {
            try {
              osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
            }
            catch (e) {
              // Not all platforms have a secondary widget library,
              // so an error is nothing to worry about.
            }
            osVersion = encodeURIComponent(osVersion);
          }
          return osVersion;
        """)

    @property
    def patch_info(self):
        """ Returns information of the active update in the queue."""
        info = {'channel': self.update_channel.channel}

        if (self.active_update.exists):
            info['buildid'] = self.active_update.buildID
            info['is_complete'] = self.is_complete_update
            info['size'] = self.active_update.selected_patch['size']
            info['type'] = self.update_type
            info['url_mirror'] = \
                self.active_update.selected_patch['finalURL'] or 'n/a'
            info['version'] = self.active_update.appVersion

        return info

    @property
    def staging_directory(self):
        """ Returns the path to the updates staging directory."""
        return self.marionette.execute_script("""
          let aus = Components.classes['@mozilla.org/updates/update-service;1']
                    .getService(Components.interfaces.nsIApplicationUpdateService);
          return aus.getUpdatesDirectory().path;
        """)

    @property
    def update_channel(self):
        """ Holds a reference to an :class:`UpdateChannel` object."""
        return self._update_channel

    @property
    def update_type(self):
        """Returns the type of the active update."""
        return self.active_update.type

    def force_fallback(self):
        """Update the update.status file and set the status to 'failed:6'"""
        with open(os.path.join(self.staging_directory, 'update.status'), 'w') as f:
            f.write('failed: 6\n')

    def get_update_snippet(self, update_url):
        """Retrieve contents of the update snippet.

        :param update_url: URL to the update snippet
        """
        snippet = None
        try:
            import urllib2
            response = urllib2.urlopen(update_url)
            snippet = response.read()
        except Exception:
            pass

        return snippet

    def get_update_url(self, force=False):
        """Retrieve the AUS update URL the update snippet is retrieved from.

        :param force: Boolean flag to force an update check

        :returns: The URL of the update snippet
        """
        url = self.prefs.get_pref(self.PREF_APP_UPDATE_URL_OVERRIDE)
        if not url:
            url = self.prefs.get_pref(self.PREF_APP_UPDATE_URL)

        # Format the URL by replacing placeholders
        url = self.marionette.execute_script("""
          Components.utils.import("resource://gre/modules/UpdateUtils.jsm")
          return UpdateUtils.formatUpdateURL(arguments[0]);
        """, script_args=[url])

        if force:
            if '?' in url:
                url += '&'
            else:
                url += '?'
            url += 'force=1'

        return url
Exemplo n.º 13
0
    def __init__(self, *args, **kwargs):
        BaseWindow.__init__(self, *args, **kwargs)

        self._prefs = Preferences(lambda: self.marionette)
        self._software_update = SoftwareUpdate(lambda: self.marionette)
        self._download_duration = None
Exemplo n.º 14
0
class UpdateWizardDialog(BaseWindow):
    """Representation of the old Software Update Wizard Dialog."""
    window_type = 'Update:Wizard'

    dtds = [
        'chrome://branding/locale/brand.dtd',
        'chrome://mozapps/locale/update/updates.dtd',
    ]

    properties = [
        'chrome://branding/locale/brand.properties',
        'chrome://mozapps/locale/update/updates.properties',
    ]

    # For the old update wizard, the errors are displayed inside the dialog. For the
    # handling of updates in the about window the errors are displayed in new dialogs.
    # When the old wizard is open we have to set the preference, so the errors will be
    # shown as expected, otherwise we would have unhandled modal dialogs when errors are raised.
    # See:
    # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/update/nsUpdateService.js?rev=a9240b1eb2fb#4813
    # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/update/nsUpdateService.js?rev=a9240b1eb2fb#4756
    PREF_APP_UPDATE_ALTWINDOWTYPE = 'app.update.altwindowtype'

    TIMEOUT_UPDATE_DOWNLOAD = 360

    def __init__(self, *args, **kwargs):
        BaseWindow.__init__(self, *args, **kwargs)

        self._prefs = Preferences(lambda: self.marionette)
        self._software_update = SoftwareUpdate(lambda: self.marionette)
        self._download_duration = None

    @property
    def wizard(self):
        """The :class:`Wizard` instance which represents the wizard.

        :returns: Reference to the wizard.
        """
        # The deck is also the root element
        wizard = self.marionette.find_element(By.ID, 'updates')
        return Wizard(lambda: self.marionette, self, wizard)

    @property
    def patch_info(self):
        """ Returns information about the active update in the queue.

        :returns: A dictionary with information about the active patch
        """
        patch = self._software_update.patch_info
        patch['download_duration'] = self._download_duration
        return patch

    def download(self, wait_for_finish=True, timeout=TIMEOUT_UPDATE_DOWNLOAD):
        """ Download the update.

        :param wait_for_finish: Optional, if True the function has to wait
         for the download to be finished, default to `True`
        :param timeout: Optional, How long to wait for the download to finish,
         default to 360 seconds
        """
        self._prefs.set_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE, self.window_type)

        try:
            # If updates have already been found, proceed to download
            if self.wizard.selected_panel in (self.wizard.updates_found_basic,
                                              self.wizard.updates_found_billboard,
                                              self.wizard.error_patching,
                                              ):
                self.select_next_page()

            # If incompatible add-on are installed, skip over the wizard page
            if self.wizard.selected_panel == self.wizard.incompatible_list:
                self.select_next_page()

            # Updates were stored in the cache, so no download is necessary
            if self.wizard.selected_panel in (self.wizard.finished,
                                              self.wizard.finished_background,
                                              ):
                pass

            # Download the update
            elif self.wizard.selected_panel == self.wizard.downloading:
                if wait_for_finish:
                    start_time = datetime.now()
                    self.wait_for_download_finished(timeout)
                    self._download_duration = (datetime.now() - start_time).total_seconds()

                    Wait(self.marionette).until(
                        lambda _: self.wizard.selected_panel in (self.wizard.finished,
                                                                 self.wizard.finished_background,
                                                                 ),
                        message='Final wizard page has been selected.')

            else:
                raise Exception('Invalid wizard page for downloading an update: {}'.format(
                                self.wizard.selected_panel))

        finally:
            self._prefs.restore_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE)

    def select_next_page(self):
        """Clicks on "Next" button, and waits for the next page to show up."""
        current_panel = self.wizard.selected_panel

        self.wizard.next_button.click()
        Wait(self.marionette).until(lambda _: self.wizard.selected_panel != current_panel)

    def wait_for_download_finished(self, timeout=TIMEOUT_UPDATE_DOWNLOAD):
        """Waits until download is completed.

        :param timeout: Optional, How long to wait for the download to finish,
        default to 360 seconds.
        """
        Wait(self.marionette, timeout=timeout).until(
            lambda _: self.wizard.selected_panel != self.wizard.downloading,
            message='Download has been completed.')

        assert self.wizard.selected_panel not in (self.wizard.error,
                                                  self.wizard.error_extra,
                                                  ), \
            'Update has been successfully downloaded.'