<commit_before>#include "meter_manager.hpp"

namespace nest {
namespace mc {
namespace util {

meter_manager::meter_manager() {
    // add time-measurement meter
    meters.emplace_back(new time_meter());

    // add memory consumption meter
    // TODO

    // add energy consumption meter
    // TODO
};

void meter_manager::checkpoint(std::string name) {
    checkpoint_names.push_back(std::move(name));

    // Enforce a global synchronization point the first time that the meters
    // are used, to ensure that times measured across all domains are
    // synchronised.
    if (meters.size()==0) {
        communication::global_policy::barrier();
    }

    for (auto& m: meters) {
        m->take_reading();
    }
}

nlohmann::json to_json(const meter_manager& manager) {
    using gcom = communication::global_policy;

    nlohmann::json meter_out;
    for (const auto& m: manager.meters) {
        for (const auto& measure: m->measurements()) {
            meter_out.push_back(to_json(measure));
        }
    }

    // Only the "root" process returns meter information
    if (gcom::id()==0) {
        return {
            {"checkpoints", manager.checkpoint_names},
            {"num_domains", gcom::size()},
            {"global_model", std::to_string(gcom::kind())},
            {"meters", meter_out},
            // TODO mapping of domains to nodes, which will be required to
            // calculate the total memory and energy consumption of a
            // distributed simulation.
        };
    }

    return {};
}

void save_to_file(const meter_manager& manager, const std::string& name) {
    auto measurements = to_json(manager);
    if (!communication::global_policy::id()) {
        std::ofstream fid;
        fid.exceptions(std::ios_base::badbit | std::ios_base::failbit);
        fid.open(name);
        fid << std::setw(1) << measurements << "\n";
    }
}

} // namespace util
} // namespace mc
} // namespace nest
<commit_msg>Feature/time meter (#219)<commit_after>#include "meter_manager.hpp"

namespace nest {
namespace mc {
namespace util {

meter_manager::meter_manager() {
    // add time-measurement meter
    meters.emplace_back(new time_meter());

    // add memory consumption meter
    // TODO

    // add energy consumption meter
    // TODO
};

void meter_manager::checkpoint(std::string name) {
    // Enforce a global synchronization point the first time that the meters
    // are used, to ensure that times measured across all domains are
    // synchronised.
    if (checkpoint_names.size()==0) {
        communication::global_policy::barrier();
    }

    checkpoint_names.push_back(std::move(name));
    for (auto& m: meters) {
        m->take_reading();
    }
}

nlohmann::json to_json(const meter_manager& manager) {
    using gcom = communication::global_policy;

    nlohmann::json meter_out;
    for (const auto& m: manager.meters) {
        for (const auto& measure: m->measurements()) {
            meter_out.push_back(to_json(measure));
        }
    }

    // Only the "root" process returns meter information
    if (gcom::id()==0) {
        return {
            {"checkpoints", manager.checkpoint_names},
            {"num_domains", gcom::size()},
            {"global_model", std::to_string(gcom::kind())},
            {"meters", meter_out},
            // TODO mapping of domains to nodes, which will be required to
            // calculate the total memory and energy consumption of a
            // distributed simulation.
        };
    }

    return {};
}

void save_to_file(const meter_manager& manager, const std::string& name) {
    auto measurements = to_json(manager);
    if (!communication::global_policy::id()) {
        std::ofstream fid;
        fid.exceptions(std::ios_base::badbit | std::ios_base::failbit);
        fid.open(name);
        fid << std::setw(1) << measurements << "\n";
    }
}

} // namespace util
} // namespace mc
} // namespace nest
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: UndoGuard.hxx,v $
 * $Revision: 1.5 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef CHART2_UNDOGUARD_HXX
#define CHART2_UNDOGUARD_HXX

#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/chart2/XUndoManager.hpp>
#include "charttoolsdllapi.hxx"

// header for class OUString
#include <rtl/ustring.hxx>

namespace chart
{
/** Base Class for UndoGuard and UndoLiveUpdateGuard
*/
class UndoGuard_Base
{
public:
    explicit UndoGuard_Base( const rtl::OUString & rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoGuard_Base();

OOO_DLLPUBLIC_CHARTTOOLS void commitAction();

protected:
    ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > m_xModel;
    ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > m_xUndoManager;

    rtl::OUString   m_aUndoString;
    bool            m_bActionPosted;
};

/** This guard calls preAction at the given Model in the CTOR and
    cancelAction in the DTOR if no other method is called.
    If commitAction is called the destructor does nothin anymore.
 */
class OOO_DLLPUBLIC_CHARTTOOLS UndoGuard : public UndoGuard_Base
{
public:
    explicit UndoGuard( const rtl::OUString& rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoGuard();
};

/** This guard calls preAction at the given Model in the CTOR and
    cancelActionUndo in the DTOR if no other method is called.
    If commitAction is called the destructor does nothin anymore.
 */
class OOO_DLLPUBLIC_CHARTTOOLS UndoLiveUpdateGuard : public UndoGuard_Base
{
public:
    explicit UndoLiveUpdateGuard( const rtl::OUString& rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoLiveUpdateGuard();
};

/** Same as UndoLiveUpdateGuard but with additional storage of the chart's data.
    Only use this if the data has internal data.
 */
class OOO_DLLPUBLIC_CHARTTOOLS UndoLiveUpdateGuardWithData :
        public UndoGuard_Base
{
public:
    explicit UndoLiveUpdateGuardWithData( const rtl::OUString& rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoLiveUpdateGuardWithData();
};

class OOO_DLLPUBLIC_CHARTTOOLS UndoGuardWithSelection : public UndoGuard_Base
{
public:
    explicit UndoGuardWithSelection( const rtl::OUString& rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoGuardWithSelection();
};

}
// CHART2_UNDOGUARD_HXX
#endif
<commit_msg>#i12587# Inserting/editing arbitrary text objects in chart<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: UndoGuard.hxx,v $
 * $Revision: 1.5 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef CHART2_UNDOGUARD_HXX
#define CHART2_UNDOGUARD_HXX

#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/chart2/XUndoManager.hpp>

// header for class OUString
#include <rtl/ustring.hxx>

namespace chart
{
/** Base Class for UndoGuard and UndoLiveUpdateGuard
*/
class UndoGuard_Base
{
public:
    explicit UndoGuard_Base( const rtl::OUString & rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoGuard_Base();

    void commitAction();

protected:
    ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > m_xModel;
    ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > m_xUndoManager;

    rtl::OUString   m_aUndoString;
    bool            m_bActionPosted;
};

/** This guard calls preAction at the given Model in the CTOR and
    cancelAction in the DTOR if no other method is called.
    If commitAction is called the destructor does nothin anymore.
 */
class UndoGuard : public UndoGuard_Base
{
public:
    explicit UndoGuard( const rtl::OUString& rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoGuard();
};

/** This guard calls preAction at the given Model in the CTOR and
    cancelActionUndo in the DTOR if no other method is called.
    If commitAction is called the destructor does nothin anymore.
 */
class UndoLiveUpdateGuard : public UndoGuard_Base
{
public:
    explicit UndoLiveUpdateGuard( const rtl::OUString& rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoLiveUpdateGuard();
};

/** Same as UndoLiveUpdateGuard but with additional storage of the chart's data.
    Only use this if the data has internal data.
 */
class UndoLiveUpdateGuardWithData :
        public UndoGuard_Base
{
public:
    explicit UndoLiveUpdateGuardWithData( const rtl::OUString& rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoLiveUpdateGuardWithData();
};

class UndoGuardWithSelection : public UndoGuard_Base
{
public:
    explicit UndoGuardWithSelection( const rtl::OUString& rUndoMessage
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::chart2::XUndoManager > & xUndoManager
        , const ::com::sun::star::uno::Reference<
            ::com::sun::star::frame::XModel > & xModel );
    virtual ~UndoGuardWithSelection();
};

}
// CHART2_UNDOGUARD_HXX
#endif
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_util.h"

#include "chrome/browser/automation/url_request_mock_http_job.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "chrome/views/dialog_delegate.h"
#include "net/url_request/url_request_unittest.h"

const std::string NOLISTENERS_HTML =
    "<title>nolisteners</title>";

const std::string UNLOAD_HTML =
    "<title>unload</title>"
    "";

const std::string BEFORE_UNLOAD_HTML =
    "<title>beforeunload</title>"
    ""
    "";

const std::string TWO_SECOND_BEFORE_UNLOAD_HTML =
    "<title>twosecondbeforeunload</title>"
    "";

const std::string INFINITE_UNLOAD_HTML =
    "<title>infiniteunload</title>"
    ""
    "";

const std::string INFINITE_BEFORE_UNLOAD_HTML =
    "<title>infinitebeforeunload</title>"
    ""
    "";

const std::string INFINITE_UNLOAD_ALERT_HTML =
    "<title>infiniteunloadalert</title>"
    "";

const std::string INFINITE_BEFORE_UNLOAD_ALERT_HTML =
    "<title>infinitebeforeunloadalert</title>"
    "";

const std::string TWO_SECOND_UNLOAD_ALERT_HTML =
    "<title>twosecondunloadalert</title>"
    "";

const std::string TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML =
    "<title>twosecondbeforeunloadalert</title>"
    "";

class UnloadTest : public UITest {
 public:
  void WaitForBrowserClosed() {
    const int kCheckDelayMs = 100;
    int max_wait_time = 5000;
    while (max_wait_time > 0) {
      max_wait_time -= kCheckDelayMs;
      Sleep(kCheckDelayMs);
      if (!IsBrowserRunning())
        break;
    }
  }

  void CheckTitle(const std::wstring& expected_title) {
    const int kCheckDelayMs = 100;
    int max_wait_time = 5000;
    while (max_wait_time > 0) {
      max_wait_time -= kCheckDelayMs;
      Sleep(kCheckDelayMs);
      if (expected_title == GetActiveTabTitle())
        break;
    }

    EXPECT_EQ(expected_title, GetActiveTabTitle());
  }

  void NavigateToDataURL(const std::string& html_content,
                         const std::wstring& expected_title) {
    NavigateToURL(GURL("data:text/html," + html_content));
    CheckTitle(expected_title);
  }

  void NavigateToNolistenersFileTwice() {
    NavigateToURL(
        URLRequestMockHTTPJob::GetMockUrl(L"title2.html"));
    CheckTitle(L"Title Of Awesomeness");
    NavigateToURL(
        URLRequestMockHTTPJob::GetMockUrl(L"title2.html"));
    CheckTitle(L"Title Of Awesomeness");
  }

  // Navigates to a URL asynchronously, then again synchronously. The first
  // load is purposely async to test the case where the user loads another
  // page without waiting for the first load to complete.
  void NavigateToNolistenersFileTwiceAsync() {
    // TODO(ojan): We hit a DCHECK in RenderViewHost::OnMsgShouldCloseACK
    // if we don't sleep here.
    Sleep(400);
    NavigateToURLAsync(
        URLRequestMockHTTPJob::GetMockUrl(L"title2.html"));
    Sleep(400);
    NavigateToURL(
        URLRequestMockHTTPJob::GetMockUrl(L"title2.html"));

    CheckTitle(L"Title Of Awesomeness");
  }

  void LoadUrlAndQuitBrowser(const std::string& html_content,
                             const std::wstring& expected_title = L"") {
    scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    NavigateToDataURL(html_content, expected_title);
    bool application_closed = false;
    EXPECT_TRUE(CloseBrowser(browser.get(), &application_closed));
  }

  void ClickModalDialogButton(views::DialogDelegate::DialogButton button) {
    bool modal_dialog_showing = false;
    views::DialogDelegate::DialogButton available_buttons;
    EXPECT_TRUE(automation()->WaitForAppModalDialog(3000));
    EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
        &available_buttons));
    ASSERT_TRUE(modal_dialog_showing);
    EXPECT_TRUE((button & available_buttons) != NULL);
    EXPECT_TRUE(automation()->ClickAppModalDialogButton(button));
  }
};

// Navigate to a page with an infinite unload handler.
// Then two two async crosssite requests to ensure
// we don't get confused and think we're closing the tab.
TEST_F(UnloadTest, CrossSiteInfiniteUnloadAsync) {
  // Tests makes no sense in single-process mode since the renderer is hung.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  NavigateToDataURL(INFINITE_UNLOAD_HTML, L"infiniteunload");
  // Must navigate to a non-data URL to trigger cross-site codepath.
  NavigateToNolistenersFileTwiceAsync();
  ASSERT_TRUE(IsBrowserRunning());
}

// Navigate to a page with an infinite unload handler.
// Then two two sync crosssite requests to ensure
// we correctly nav to each one.
TEST_F(UnloadTest, CrossSiteInfiniteUnloadSync) {
  // Tests makes no sense in single-process mode since the renderer is hung.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  NavigateToDataURL(INFINITE_UNLOAD_HTML, L"infiniteunload");
  // Must navigate to a non-data URL to trigger cross-site codepath.
  NavigateToNolistenersFileTwice();
  ASSERT_TRUE(IsBrowserRunning());
}

// Navigate to a page with an infinite beforeunload handler.
// Then two two async crosssite requests to ensure
// we don't get confused and think we're closing the tab.
TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadAsync) {
  // Tests makes no sense in single-process mode since the renderer is hung.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
  // Must navigate to a non-data URL to trigger cross-site codepath.
  NavigateToNolistenersFileTwiceAsync();
  ASSERT_TRUE(IsBrowserRunning());
}

// Navigate to a page with an infinite beforeunload handler.
// Then two two sync crosssite requests to ensure
// we correctly nav to each one.
TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadSync) {
  // Tests makes no sense in single-process mode since the renderer is hung.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
  // Must navigate to a non-data URL to trigger cross-site codepath.
  NavigateToNolistenersFileTwice();
  ASSERT_TRUE(IsBrowserRunning());
}

// Tests closing the browser on a page with no unload listeners registered.
TEST_F(UnloadTest, BrowserCloseNoUnloadListeners) {
  LoadUrlAndQuitBrowser(NOLISTENERS_HTML, L"nolisteners");
}

// Tests closing the browser on a page with an unload listener registered.
TEST_F(UnloadTest, BrowserCloseUnload) {
  LoadUrlAndQuitBrowser(UNLOAD_HTML, L"unload");
}

// Tests closing the browser with a beforeunload handler and clicking
// OK in the beforeunload confirm dialog.
TEST_F(UnloadTest, BrowserCloseBeforeUnloadOK) {
  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  NavigateToDataURL(BEFORE_UNLOAD_HTML, L"beforeunload");

  CloseBrowserAsync(browser.get());
  ClickModalDialogButton(views::DialogDelegate::DIALOGBUTTON_OK);
  WaitForBrowserClosed();
  EXPECT_FALSE(IsBrowserRunning());
}

// Tests closing the browser with a beforeunload handler and clicking
// CANCEL in the beforeunload confirm dialog.
TEST_F(UnloadTest, BrowserCloseBeforeUnloadCancel) {
  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  NavigateToDataURL(BEFORE_UNLOAD_HTML, L"beforeunload");

  CloseBrowserAsync(browser.get());
  ClickModalDialogButton(views::DialogDelegate::DIALOGBUTTON_CANCEL);
  WaitForBrowserClosed();
  EXPECT_TRUE(IsBrowserRunning());

  CloseBrowserAsync(browser.get());
  ClickModalDialogButton(views::DialogDelegate::DIALOGBUTTON_OK);
  WaitForBrowserClosed();
  EXPECT_FALSE(IsBrowserRunning());
}

// Tests closing the browser with a beforeunload handler that takes
// two seconds to run.
TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnload) {
  LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_HTML,
                        L"twosecondbeforeunload");
}

// Tests closing the browser on a page with an unload listener registered where
// the unload handler has an infinite loop.
TEST_F(UnloadTest, BrowserCloseInfiniteUnload) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(INFINITE_UNLOAD_HTML, L"infiniteunload");
}

// Tests closing the browser with a beforeunload handler that hangs.
TEST_F(UnloadTest, BrowserCloseInfiniteBeforeUnload) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
}

// Tests closing the browser on a page with an unload listener registered where
// the unload handler has an infinite loop followed by an alert.
TEST_F(UnloadTest, BrowserCloseInfiniteUnloadAlert) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(INFINITE_UNLOAD_ALERT_HTML, L"infiniteunloadalert");
}

// Tests closing the browser with a beforeunload handler that hangs then
// pops up an alert.
TEST_F(UnloadTest, BrowserCloseInfiniteBeforeUnloadAlert) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_ALERT_HTML,
                        L"infinitebeforeunloadalert");
}

// Tests closing the browser on a page with an unload listener registered where
// the unload handler has an 2 second long loop followed by an alert.
TEST_F(UnloadTest, BrowserCloseTwoSecondUnloadAlert) {
  LoadUrlAndQuitBrowser(TWO_SECOND_UNLOAD_ALERT_HTML, L"twosecondunloadalert");
}

// Tests closing the browser with a beforeunload handler that takes
// two seconds to run then pops up an alert.
TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) {
  LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML,
                        L"twosecondbeforeunloadalert");
}

// TODO(ojan): Add tests for unload/beforeunload that have multiple tabs
// and multiple windows.<commit_msg>blacklist some more tests in single process<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_util.h"

#include "chrome/browser/automation/url_request_mock_http_job.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "chrome/views/dialog_delegate.h"
#include "net/url_request/url_request_unittest.h"

const std::string NOLISTENERS_HTML =
    "<title>nolisteners</title>";

const std::string UNLOAD_HTML =
    "<title>unload</title>"
    "";

const std::string BEFORE_UNLOAD_HTML =
    "<title>beforeunload</title>"
    ""
    "";

const std::string TWO_SECOND_BEFORE_UNLOAD_HTML =
    "<title>twosecondbeforeunload</title>"
    "";

const std::string INFINITE_UNLOAD_HTML =
    "<title>infiniteunload</title>"
    ""
    "";

const std::string INFINITE_BEFORE_UNLOAD_HTML =
    "<title>infinitebeforeunload</title>"
    ""
    "";

const std::string INFINITE_UNLOAD_ALERT_HTML =
    "<title>infiniteunloadalert</title>"
    "";

const std::string INFINITE_BEFORE_UNLOAD_ALERT_HTML =
    "<title>infinitebeforeunloadalert</title>"
    "";

const std::string TWO_SECOND_UNLOAD_ALERT_HTML =
    "<title>twosecondunloadalert</title>"
    "";

const std::string TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML =
    "<title>twosecondbeforeunloadalert</title>"
    "";

class UnloadTest : public UITest {
 public:
  void WaitForBrowserClosed() {
    const int kCheckDelayMs = 100;
    int max_wait_time = 5000;
    while (max_wait_time > 0) {
      max_wait_time -= kCheckDelayMs;
      Sleep(kCheckDelayMs);
      if (!IsBrowserRunning())
        break;
    }
  }

  void CheckTitle(const std::wstring& expected_title) {
    const int kCheckDelayMs = 100;
    int max_wait_time = 5000;
    while (max_wait_time > 0) {
      max_wait_time -= kCheckDelayMs;
      Sleep(kCheckDelayMs);
      if (expected_title == GetActiveTabTitle())
        break;
    }

    EXPECT_EQ(expected_title, GetActiveTabTitle());
  }

  void NavigateToDataURL(const std::string& html_content,
                         const std::wstring& expected_title) {
    NavigateToURL(GURL("data:text/html," + html_content));
    CheckTitle(expected_title);
  }

  void NavigateToNolistenersFileTwice() {
    NavigateToURL(
        URLRequestMockHTTPJob::GetMockUrl(L"title2.html"));
    CheckTitle(L"Title Of Awesomeness");
    NavigateToURL(
        URLRequestMockHTTPJob::GetMockUrl(L"title2.html"));
    CheckTitle(L"Title Of Awesomeness");
  }

  // Navigates to a URL asynchronously, then again synchronously. The first
  // load is purposely async to test the case where the user loads another
  // page without waiting for the first load to complete.
  void NavigateToNolistenersFileTwiceAsync() {
    // TODO(ojan): We hit a DCHECK in RenderViewHost::OnMsgShouldCloseACK
    // if we don't sleep here.
    Sleep(400);
    NavigateToURLAsync(
        URLRequestMockHTTPJob::GetMockUrl(L"title2.html"));
    Sleep(400);
    NavigateToURL(
        URLRequestMockHTTPJob::GetMockUrl(L"title2.html"));

    CheckTitle(L"Title Of Awesomeness");
  }

  void LoadUrlAndQuitBrowser(const std::string& html_content,
                             const std::wstring& expected_title = L"") {
    scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    NavigateToDataURL(html_content, expected_title);
    bool application_closed = false;
    EXPECT_TRUE(CloseBrowser(browser.get(), &application_closed));
  }

  void ClickModalDialogButton(views::DialogDelegate::DialogButton button) {
    bool modal_dialog_showing = false;
    views::DialogDelegate::DialogButton available_buttons;
    EXPECT_TRUE(automation()->WaitForAppModalDialog(3000));
    EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
        &available_buttons));
    ASSERT_TRUE(modal_dialog_showing);
    EXPECT_TRUE((button & available_buttons) != NULL);
    EXPECT_TRUE(automation()->ClickAppModalDialogButton(button));
  }
};

// Navigate to a page with an infinite unload handler.
// Then two two async crosssite requests to ensure
// we don't get confused and think we're closing the tab.
TEST_F(UnloadTest, CrossSiteInfiniteUnloadAsync) {
  // Tests makes no sense in single-process mode since the renderer is hung.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  NavigateToDataURL(INFINITE_UNLOAD_HTML, L"infiniteunload");
  // Must navigate to a non-data URL to trigger cross-site codepath.
  NavigateToNolistenersFileTwiceAsync();
  ASSERT_TRUE(IsBrowserRunning());
}

// Navigate to a page with an infinite unload handler.
// Then two two sync crosssite requests to ensure
// we correctly nav to each one.
TEST_F(UnloadTest, CrossSiteInfiniteUnloadSync) {
  // Tests makes no sense in single-process mode since the renderer is hung.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  NavigateToDataURL(INFINITE_UNLOAD_HTML, L"infiniteunload");
  // Must navigate to a non-data URL to trigger cross-site codepath.
  NavigateToNolistenersFileTwice();
  ASSERT_TRUE(IsBrowserRunning());
}

// Navigate to a page with an infinite beforeunload handler.
// Then two two async crosssite requests to ensure
// we don't get confused and think we're closing the tab.
TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadAsync) {
  // Tests makes no sense in single-process mode since the renderer is hung.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
  // Must navigate to a non-data URL to trigger cross-site codepath.
  NavigateToNolistenersFileTwiceAsync();
  ASSERT_TRUE(IsBrowserRunning());
}

// Navigate to a page with an infinite beforeunload handler.
// Then two two sync crosssite requests to ensure
// we correctly nav to each one.
TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadSync) {
  // Tests makes no sense in single-process mode since the renderer is hung.
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
  // Must navigate to a non-data URL to trigger cross-site codepath.
  NavigateToNolistenersFileTwice();
  ASSERT_TRUE(IsBrowserRunning());
}

// Tests closing the browser on a page with no unload listeners registered.
TEST_F(UnloadTest, BrowserCloseNoUnloadListeners) {
  LoadUrlAndQuitBrowser(NOLISTENERS_HTML, L"nolisteners");
}

// Tests closing the browser on a page with an unload listener registered.
TEST_F(UnloadTest, BrowserCloseUnload) {
  LoadUrlAndQuitBrowser(UNLOAD_HTML, L"unload");
}

// Tests closing the browser with a beforeunload handler and clicking
// OK in the beforeunload confirm dialog.
TEST_F(UnloadTest, BrowserCloseBeforeUnloadOK) {
  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  NavigateToDataURL(BEFORE_UNLOAD_HTML, L"beforeunload");

  CloseBrowserAsync(browser.get());
  ClickModalDialogButton(views::DialogDelegate::DIALOGBUTTON_OK);
  WaitForBrowserClosed();
  EXPECT_FALSE(IsBrowserRunning());
}

// Tests closing the browser with a beforeunload handler and clicking
// CANCEL in the beforeunload confirm dialog.
TEST_F(UnloadTest, BrowserCloseBeforeUnloadCancel) {
  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  NavigateToDataURL(BEFORE_UNLOAD_HTML, L"beforeunload");

  CloseBrowserAsync(browser.get());
  ClickModalDialogButton(views::DialogDelegate::DIALOGBUTTON_CANCEL);
  WaitForBrowserClosed();
  EXPECT_TRUE(IsBrowserRunning());

  CloseBrowserAsync(browser.get());
  ClickModalDialogButton(views::DialogDelegate::DIALOGBUTTON_OK);
  WaitForBrowserClosed();
  EXPECT_FALSE(IsBrowserRunning());
}

// Tests closing the browser with a beforeunload handler that takes
// two seconds to run.
TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnload) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_HTML,
                        L"twosecondbeforeunload");
}

// Tests closing the browser on a page with an unload listener registered where
// the unload handler has an infinite loop.
TEST_F(UnloadTest, BrowserCloseInfiniteUnload) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(INFINITE_UNLOAD_HTML, L"infiniteunload");
}

// Tests closing the browser with a beforeunload handler that hangs.
TEST_F(UnloadTest, BrowserCloseInfiniteBeforeUnload) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
}

// Tests closing the browser on a page with an unload listener registered where
// the unload handler has an infinite loop followed by an alert.
TEST_F(UnloadTest, BrowserCloseInfiniteUnloadAlert) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(INFINITE_UNLOAD_ALERT_HTML, L"infiniteunloadalert");
}

// Tests closing the browser with a beforeunload handler that hangs then
// pops up an alert.
TEST_F(UnloadTest, BrowserCloseInfiniteBeforeUnloadAlert) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_ALERT_HTML,
                        L"infinitebeforeunloadalert");
}

// Tests closing the browser on a page with an unload listener registered where
// the unload handler has an 2 second long loop followed by an alert.
TEST_F(UnloadTest, BrowserCloseTwoSecondUnloadAlert) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(TWO_SECOND_UNLOAD_ALERT_HTML, L"twosecondunloadalert");
}

// Tests closing the browser with a beforeunload handler that takes
// two seconds to run then pops up an alert.
TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) {
  // TODO(jabdelmalek): BUG(7933)  this started hanging now, should it be
  // disabled in single process mode like the other tests?
  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    return;

  LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML,
                        L"twosecondbeforeunloadalert");
}

// TODO(ojan): Add tests for unload/beforeunload that have multiple tabs
// and multiple windows.<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome_frame/chrome_launcher.h"

#include <windows.h>
#include <shellapi.h>
#include <shlwapi.h>

// Herein lies stuff selectively stolen from Chrome. We don't pull it in
// directly because all of it results in many things we don't want being
// included as well.
namespace {

// These are the switches we will allow (along with their values) in the
// safe-for-Low-Integrity version of the Chrome command line.
// Including the chrome switch files pulls in a bunch of dependencies sadly, so
// we redefine things here:
const wchar_t* kAllowedSwitches[] = {
  L"automation-channel",
  L"chrome-frame",
  L"chrome-version",
  L"disable-renderer-accessibility",
  L"enable-experimental-extension-apis",
  L"force-renderer-accessibility",
  L"lang",
  L"no-default-browser-check",
  L"noerrdialogs",
  L"no-first-run",
  L"user-data-dir",
  L"disable-popup-blocking",
  L"full-memory-crash-report",
};

const wchar_t kWhitespaceChars[] = {
  0x0009, /* <control-0009> to <control-000D> */
  0x000A,
  0x000B,
  0x000C,
  0x000D,
  0x0020, /* Space */
  0x0085, /* <control-0085> */
  0x00A0, /* No-Break Space */
  0x1680, /* Ogham Space Mark */
  0x180E, /* Mongolian Vowel Separator */
  0x2000, /* En Quad to Hair Space */
  0x2001,
  0x2002,
  0x2003,
  0x2004,
  0x2005,
  0x2006,
  0x2007,
  0x2008,
  0x2009,
  0x200A,
  0x200C, /* Zero Width Non-Joiner */
  0x2028, /* Line Separator */
  0x2029, /* Paragraph Separator */
  0x202F, /* Narrow No-Break Space */
  0x205F, /* Medium Mathematical Space */
  0x3000, /* Ideographic Space */
  0
};

const wchar_t kLauncherExeBaseName[] = L"chrome_launcher.exe";
const wchar_t kBrowserProcessExecutableName[] = L"chrome.exe";

}  // end namespace

namespace chrome_launcher {

std::wstring TrimWhiteSpace(const wchar_t* input_str) {
  std::wstring output;
  if (input_str != NULL) {
    std::wstring str(input_str);

    const std::wstring::size_type first_good_char =
        str.find_first_not_of(kWhitespaceChars);
    const std::wstring::size_type last_good_char =
        str.find_last_not_of(kWhitespaceChars);

    if (first_good_char != std::wstring::npos &&
        last_good_char != std::wstring::npos &&
        last_good_char >= first_good_char) {
      // + 1 because find_last_not_of returns the index, and we want the count
      output = str.substr(first_good_char,
                          last_good_char - first_good_char + 1);
    }
  }

  return output;
}

bool IsValidArgument(const std::wstring& arg) {
  if (arg.length() < 2) {
    return false;
  }

  for (int i = 0; i < arraysize(kAllowedSwitches); ++i) {
    size_t arg_length = lstrlenW(kAllowedSwitches[i]);
    if (arg.find(kAllowedSwitches[i], 2) == 2) {
      // The argument starts off right, now it must either end here, or be
      // followed by an equals sign.
      if (arg.length() == (arg_length + 2) ||
          (arg.length() > (arg_length + 2) && arg[arg_length+2] == L'=')) {
        return true;
      }
    }
  }

  return false;
}

bool IsValidCommandLine(const wchar_t* command_line) {
  if (command_line == NULL) {
    return false;
  }

  int num_args = 0;
  wchar_t** args = NULL;
  args = CommandLineToArgvW(command_line, &num_args);

  bool success = true;
  // Note that we skip args[0] since that is just our executable name and
  // doesn't get passed through to Chrome.
  for (int i = 1; i < num_args; ++i) {
    std::wstring trimmed_arg = TrimWhiteSpace(args[i]);
    if (!IsValidArgument(trimmed_arg.c_str())) {
      success = false;
      break;
    }
  }

  return success;
}

bool SanitizeAndLaunchChrome(const wchar_t* command_line) {
  bool success = false;
  if (IsValidCommandLine(command_line)) {
    std::wstring chrome_path;
    if (GetChromeExecutablePath(&chrome_path)) {
      const wchar_t* args = PathGetArgs(command_line);
      if (args != NULL) {
        chrome_path += L" ";
        chrome_path += args;
      }

      STARTUPINFO startup_info = {0};
      startup_info.cb = sizeof(startup_info);
      startup_info.dwFlags = STARTF_USESHOWWINDOW;
      startup_info.wShowWindow = SW_SHOW;
      PROCESS_INFORMATION process_info = {0};
      if (CreateProcess(NULL, &chrome_path[0],
                        NULL, NULL, FALSE, 0, NULL, NULL,
                        &startup_info, &process_info)) {
        // Close handles.
        CloseHandle(process_info.hThread);
        CloseHandle(process_info.hProcess);
        success = true;
      } else {
        _ASSERT(FALSE);
      }
    }
  }

  return success;
}

bool GetChromeExecutablePath(std::wstring* chrome_path) {
  _ASSERT(chrome_path);

  wchar_t cur_path[MAX_PATH * 4] = {0};
  // Assume that we are always built into an exe.
  GetModuleFileName(NULL, cur_path, arraysize(cur_path) / 2);

  PathRemoveFileSpec(cur_path);

  bool success = false;
  if (PathAppend(cur_path, kBrowserProcessExecutableName)) {
    if (!PathFileExists(cur_path)) {
      // The installation model for Chrome places the DLLs in a versioned
      // sub-folder one down from the Chrome executable. If we fail to find
      // chrome.exe in the current path, try looking one up and launching that
      // instead. In practice, that means we back up two and append the
      // executable name again.
      PathRemoveFileSpec(cur_path);
      PathRemoveFileSpec(cur_path);
      PathAppend(cur_path, kBrowserProcessExecutableName);
    }

    if (PathFileExists(cur_path)) {
      *chrome_path = cur_path;
      success = true;
    }
  }

  return success;
}

}  // namespace chrome_launcher
<commit_msg>Fix for chrome_launcher.exe command line construction.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome_frame/chrome_launcher.h"

#include <windows.h>
#include <shellapi.h>
#include <shlwapi.h>

// Herein lies stuff selectively stolen from Chrome. We don't pull it in
// directly because all of it results in many things we don't want being
// included as well.
namespace {

// These are the switches we will allow (along with their values) in the
// safe-for-Low-Integrity version of the Chrome command line.
// Including the chrome switch files pulls in a bunch of dependencies sadly, so
// we redefine things here:
const wchar_t* kAllowedSwitches[] = {
  L"automation-channel",
  L"chrome-frame",
  L"chrome-version",
  L"disable-renderer-accessibility",
  L"enable-experimental-extension-apis",
  L"force-renderer-accessibility",
  L"lang",
  L"no-default-browser-check",
  L"noerrdialogs",
  L"no-first-run",
  L"user-data-dir",
  L"disable-popup-blocking",
  L"full-memory-crash-report",
};

const wchar_t kWhitespaceChars[] = {
  0x0009, /* <control-0009> to <control-000D> */
  0x000A,
  0x000B,
  0x000C,
  0x000D,
  0x0020, /* Space */
  0x0085, /* <control-0085> */
  0x00A0, /* No-Break Space */
  0x1680, /* Ogham Space Mark */
  0x180E, /* Mongolian Vowel Separator */
  0x2000, /* En Quad to Hair Space */
  0x2001,
  0x2002,
  0x2003,
  0x2004,
  0x2005,
  0x2006,
  0x2007,
  0x2008,
  0x2009,
  0x200A,
  0x200C, /* Zero Width Non-Joiner */
  0x2028, /* Line Separator */
  0x2029, /* Paragraph Separator */
  0x202F, /* Narrow No-Break Space */
  0x205F, /* Medium Mathematical Space */
  0x3000, /* Ideographic Space */
  0
};

const wchar_t kLauncherExeBaseName[] = L"chrome_launcher.exe";
const wchar_t kBrowserProcessExecutableName[] = L"chrome.exe";

}  // end namespace

namespace chrome_launcher {

std::wstring TrimWhiteSpace(const wchar_t* input_str) {
  std::wstring output;
  if (input_str != NULL) {
    std::wstring str(input_str);

    const std::wstring::size_type first_good_char =
        str.find_first_not_of(kWhitespaceChars);
    const std::wstring::size_type last_good_char =
        str.find_last_not_of(kWhitespaceChars);

    if (first_good_char != std::wstring::npos &&
        last_good_char != std::wstring::npos &&
        last_good_char >= first_good_char) {
      // + 1 because find_last_not_of returns the index, and we want the count
      output = str.substr(first_good_char,
                          last_good_char - first_good_char + 1);
    }
  }

  return output;
}

bool IsValidArgument(const std::wstring& arg) {
  if (arg.length() < 2) {
    return false;
  }

  for (int i = 0; i < arraysize(kAllowedSwitches); ++i) {
    size_t arg_length = lstrlenW(kAllowedSwitches[i]);
    if (arg.find(kAllowedSwitches[i], 2) == 2) {
      // The argument starts off right, now it must either end here, or be
      // followed by an equals sign.
      if (arg.length() == (arg_length + 2) ||
          (arg.length() > (arg_length + 2) && arg[arg_length+2] == L'=')) {
        return true;
      }
    }
  }

  return false;
}

bool IsValidCommandLine(const wchar_t* command_line) {
  if (command_line == NULL) {
    return false;
  }

  int num_args = 0;
  wchar_t** args = NULL;
  args = CommandLineToArgvW(command_line, &num_args);

  bool success = true;
  // Note that we skip args[0] since that is just our executable name and
  // doesn't get passed through to Chrome.
  for (int i = 1; i < num_args; ++i) {
    std::wstring trimmed_arg = TrimWhiteSpace(args[i]);
    if (!IsValidArgument(trimmed_arg.c_str())) {
      success = false;
      break;
    }
  }

  return success;
}

bool SanitizeAndLaunchChrome(const wchar_t* command_line) {
  bool success = false;
  if (IsValidCommandLine(command_line)) {
    std::wstring chrome_path;
    if (GetChromeExecutablePath(&chrome_path)) {
      const wchar_t* args = PathGetArgs(command_line);

      // Build the command line string with the quoted path to chrome.exe.
      std::wstring command_line;
      command_line.reserve(chrome_path.size() + 2);
      command_line.append(1, L'\"').append(chrome_path).append(1, L'\"');

      if (args != NULL) {
        command_line += L' ';
        command_line += args;
      }

      STARTUPINFO startup_info = {0};
      startup_info.cb = sizeof(startup_info);
      startup_info.dwFlags = STARTF_USESHOWWINDOW;
      startup_info.wShowWindow = SW_SHOW;
      PROCESS_INFORMATION process_info = {0};
      if (CreateProcess(&chrome_path[0], &command_line[0],
                        NULL, NULL, FALSE, 0, NULL, NULL,
                        &startup_info, &process_info)) {
        // Close handles.
        CloseHandle(process_info.hThread);
        CloseHandle(process_info.hProcess);
        success = true;
      } else {
        _ASSERT(FALSE);
      }
    }
  }

  return success;
}

bool GetChromeExecutablePath(std::wstring* chrome_path) {
  _ASSERT(chrome_path);

  wchar_t cur_path[MAX_PATH * 4] = {0};
  // Assume that we are always built into an exe.
  GetModuleFileName(NULL, cur_path, arraysize(cur_path) / 2);

  PathRemoveFileSpec(cur_path);

  bool success = false;
  if (PathAppend(cur_path, kBrowserProcessExecutableName)) {
    if (!PathFileExists(cur_path)) {
      // The installation model for Chrome places the DLLs in a versioned
      // sub-folder one down from the Chrome executable. If we fail to find
      // chrome.exe in the current path, try looking one up and launching that
      // instead. In practice, that means we back up two and append the
      // executable name again.
      PathRemoveFileSpec(cur_path);
      PathRemoveFileSpec(cur_path);
      PathAppend(cur_path, kBrowserProcessExecutableName);
    }

    if (PathFileExists(cur_path)) {
      *chrome_path = cur_path;
      success = true;
    }
  }

  return success;
}

}  // namespace chrome_launcher
<|endoftext|>


<commit_before>// Copyright (c) 2021 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.

#include "qt/pivx/governancemodel.h"

#include "budget/budgetmanager.h"
#include "budget/budgetutil.h"
#include "destination_io.h"
#include "guiconstants.h"
#include "masternode-sync.h"
#include "script/standard.h"
#include "qt/transactiontablemodel.h"
#include "qt/transactionrecord.h"
#include "qt/pivx/mnmodel.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include "walletmodel.h"

#include <algorithm>
#include <QTimer>

GovernanceModel::GovernanceModel(ClientModel* _clientModel, MNModel* _mnModel) : clientModel(_clientModel), mnModel(_mnModel) {}
GovernanceModel::~GovernanceModel() {}

void GovernanceModel::setWalletModel(WalletModel* _walletModel)
{
    walletModel = _walletModel;
    connect(walletModel->getTransactionTableModel(), &TransactionTableModel::txLoaded, this, &GovernanceModel::txLoaded);
}

ProposalInfo GovernanceModel::buidProposalInfo(const CBudgetProposal* prop, bool isPassing, bool isPending)
{
    CTxDestination recipient;
    ExtractDestination(prop->GetPayee(), recipient);

    // Calculate status
    int votesYes = prop->GetYeas();
    int votesNo = prop->GetNays();
    ProposalInfo::Status status;

    if (isPending) {
        // Proposal waiting for confirmation to be broadcasted.
        status = ProposalInfo::WAITING_FOR_APPROVAL;
    } else {
        if (isPassing) {
            status = ProposalInfo::PASSING;
        } else if (votesYes > votesNo) {
            status = ProposalInfo::PASSING_NOT_FUNDED;
        } else {
            status = ProposalInfo::NOT_PASSING;
        }
    }

    return ProposalInfo(prop->GetHash(),
            prop->GetName(),
            prop->GetURL(),
            votesYes,
            votesNo,
            Standard::EncodeDestination(recipient),
            prop->GetAmount(),
            prop->GetTotalPaymentCount(),
            prop->GetRemainingPaymentCount(clientModel->getLastBlockProcessedHeight()),
            status);
}

std::list<ProposalInfo> GovernanceModel::getProposals()
{
    if (!clientModel) return {};
    std::list<ProposalInfo> ret;
    std::vector<CBudgetProposal> budget = g_budgetman.GetBudget();
    for (const auto& prop : g_budgetman.GetAllProposalsOrdered()) {
        bool isPassing = std::find(budget.begin(), budget.end(), *prop) != budget.end();
        ret.emplace_back(buidProposalInfo(prop, isPassing, false));
        if (isPassing) allocatedAmount += prop->GetAmount();
    }

    // Add pending proposals
    for (const auto& prop : waitingPropsForConfirmations) {
        ret.emplace_back(buidProposalInfo(&prop, false, true));
    }
    return ret;
}

bool GovernanceModel::hasProposals()
{
    return g_budgetman.HasAnyProposal();
}

CAmount GovernanceModel::getMaxAvailableBudgetAmount() const
{
    return Params().GetConsensus().nBudgetCycleBlocks * COIN;
}

int GovernanceModel::getNumBlocksPerBudgetCycle() const
{
    return Params().GetConsensus().nBudgetCycleBlocks;
}

int GovernanceModel::getPropMaxPaymentsCount() const
{
    return Params().GetConsensus().nMaxProposalPayments;
}

int GovernanceModel::getNextSuperblockHeight() const
{
    const int nBlocksPerCycle = getNumBlocksPerBudgetCycle();
    const int chainHeight = clientModel->getNumBlocks();
    return chainHeight - chainHeight % nBlocksPerCycle + nBlocksPerCycle;
}

std::vector<VoteInfo> GovernanceModel::getLocalMNsVotesForProposal(const ProposalInfo& propInfo)
{
    // First, get the local masternodes
    std::vector<COutPoint> vecLocalMn;
    for (int i = 0; i < mnModel->rowCount(); ++i) {
        vecLocalMn.emplace_back(
                uint256S(mnModel->index(i, MNModel::COLLATERAL_ID, QModelIndex()).data().toString().toStdString()),
                mnModel->index(i, MNModel::COLLATERAL_OUT_INDEX, QModelIndex()).data().toInt()
        );
    }

    std::vector<VoteInfo> localVotes;
    // Get the budget proposal, get the votes, then loop over it and return the ones that correspond to the local masternodes here.
    CBudgetProposal* prop = g_budgetman.FindProposal(propInfo.id);
    const auto& mapVotes = prop->GetVotes();
    for (const auto& it : mapVotes) {
        for (const auto& mn : vecLocalMn) {
            if (it.first == mn && it.second.IsValid()) {
                localVotes.emplace_back(mn, (VoteInfo::VoteDirection) it.second.GetDirection());
                break;
            }
        }
    }
    return localVotes;
}

OperationResult GovernanceModel::validatePropURL(const QString& url) const
{
    std::string strError;
    return {validateURL(url.toStdString(), strError, PROP_URL_MAX_SIZE), strError};
}

OperationResult GovernanceModel::validatePropAmount(CAmount amount) const
{
    if (amount > getMaxAvailableBudgetAmount()) {
        return {false, strprintf("Amount exceeding the maximum available budget amount of %s PIV", FormatMoney(amount))};
    }
    return {true};
}

OperationResult GovernanceModel::validatePropPaymentCount(int paymentCount) const
{
    if (paymentCount < 1) return { false, "Invalid payment count, must be greater than zero."};
    int nMaxPayments = getPropMaxPaymentsCount();
    if (paymentCount > nMaxPayments) {
        return { false, strprintf("Invalid payment count, cannot be greater than %d", nMaxPayments)};
    }
    return {true};
}

bool GovernanceModel::isTierTwoSync()
{
    return masternodeSync.IsBlockchainSynced();
}

OperationResult GovernanceModel::createProposal(const std::string& strProposalName,
                                                const std::string& strURL,
                                                int nPaymentCount,
                                                CAmount nAmount,
                                                const std::string& strPaymentAddr)
{
    // First get the next superblock height
    int nBlockStart = getNextSuperblockHeight();

    // Parse address
    const CTxDestination* dest = Standard::GetTransparentDestination(Standard::DecodeDestination(strPaymentAddr));
    if (!dest) return {false, "invalid recipient address for the proposal"};
    CScript scriptPubKey = GetScriptForDestination(*dest);

    // Validate proposal
    CBudgetProposal proposal(strProposalName, strURL, nPaymentCount, scriptPubKey, nAmount, nBlockStart, UINT256_ZERO);
    if (!proposal.IsWellFormed(g_budgetman.GetTotalBudget(proposal.GetBlockStart()))) {
        return {false, strprintf("Proposal is not valid %s", proposal.IsInvalidReason())};
    }

    // Craft and send transaction.
    auto opRes = walletModel->createAndSendProposalFeeTx(proposal);
    if (!opRes) return opRes;
    scheduleBroadcast(proposal);

    return {true};
}

OperationResult GovernanceModel::voteForProposal(const ProposalInfo& prop,
                                                 bool isVotePositive,
                                                 const std::vector<std::string>& mnVotingAlias)
{
    UniValue ret; // future: don't use UniValue here.
    for (const auto& mnAlias : mnVotingAlias) {
        bool fLegacyMN = true; // For now, only legacy MNs
        ret = mnBudgetVoteInner(nullptr,
                          fLegacyMN,
                          prop.id,
                          false,
                          isVotePositive ? CBudgetVote::VoteDirection::VOTE_YES : CBudgetVote::VoteDirection::VOTE_NO,
                          mnAlias);
        if (ret.exists("detail") && ret["detail"].isArray()) {
            const UniValue& obj = ret["detail"].get_array()[0];
            if (obj["result"].getValStr() != "success") {
                return {false, obj["error"].getValStr()};
            }
        }
    }
    // add more information with ret["overall"]
    return {true};
}

void GovernanceModel::scheduleBroadcast(const CBudgetProposal& proposal)
{
    // Cache the proposal to be sent as soon as it gets the minimum required confirmations
    // without requiring user interaction
    waitingPropsForConfirmations.emplace_back(proposal);

    // Launch timer if it's not already running
    if (!pollTimer) pollTimer = new QTimer(this);
    if (!pollTimer->isActive()) {
        connect(pollTimer, &QTimer::timeout, this, &GovernanceModel::pollGovernanceChanged);
        pollTimer->start(MODEL_UPDATE_DELAY * 60 * 3.5); // Every 3.5 minutes
    }
}

void GovernanceModel::pollGovernanceChanged()
{
    if (!isTierTwoSync()) return;

    int chainHeight = clientModel->getNumBlocks();
    // Try to broadcast any pending for confirmations proposal
    auto it = waitingPropsForConfirmations.begin();
    while (it != waitingPropsForConfirmations.end()) {
        if (!g_budgetman.AddProposal(*it)) {
            LogPrint(BCLog::QT, "Cannot broadcast budget proposal - %s", it->IsInvalidReason());
            if (it->GetBlockStart() >= chainHeight) {
                // Edge case, the proposal was never broadcasted before the next superblock, can be removed.
                // future: notify the user about it.
                it = waitingPropsForConfirmations.erase(it);
            } else {
                it++;
            }
            continue;
        }
        it->Relay();
        it = waitingPropsForConfirmations.erase(it);
    }

    // If there are no more waiting proposals, turn the timer off.
    if (waitingPropsForConfirmations.empty()) {
        pollTimer->stop();
    }
}

void GovernanceModel::stopPolling()
{
    if (pollTimer && pollTimer->isActive()) {
        pollTimer->stop();
    }
}

void GovernanceModel::txLoaded(const QString& id, const int txType, const int txStatus)
{
    if (txType == TransactionRecord::SendToNobody) {
        // If this is a proposal fee, parse it.
        const auto& wtx = walletModel->getTx(uint256S(id.toStdString()));
        assert(wtx);
        const auto& it = wtx->mapValue.find("proposal");
        if (it != wtx->mapValue.end()) {
            const std::vector<unsigned char> vec = ParseHex(it->second);
            if (vec.empty()) return;
            CDataStream ss(vec, SER_DISK, CLIENT_VERSION);
            CBudgetProposal proposal;
            ss >> proposal;
            if (!g_budgetman.HaveProposal(proposal.GetHash()) &&
                proposal.GetBlockStart() < clientModel->getNumBlocks()) {
                scheduleBroadcast(proposal);
            }
        }
    }
}
<commit_msg>Model governance: do not try to broadcast proposals that expired or that due a reorg lost the fee transaction.<commit_after>// Copyright (c) 2021 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.

#include "qt/pivx/governancemodel.h"

#include "budget/budgetmanager.h"
#include "budget/budgetutil.h"
#include "destination_io.h"
#include "guiconstants.h"
#include "masternode-sync.h"
#include "script/standard.h"
#include "qt/transactiontablemodel.h"
#include "qt/transactionrecord.h"
#include "qt/pivx/mnmodel.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include "walletmodel.h"

#include <algorithm>
#include <QTimer>

GovernanceModel::GovernanceModel(ClientModel* _clientModel, MNModel* _mnModel) : clientModel(_clientModel), mnModel(_mnModel) {}
GovernanceModel::~GovernanceModel() {}

void GovernanceModel::setWalletModel(WalletModel* _walletModel)
{
    walletModel = _walletModel;
    connect(walletModel->getTransactionTableModel(), &TransactionTableModel::txLoaded, this, &GovernanceModel::txLoaded);
}

ProposalInfo GovernanceModel::buidProposalInfo(const CBudgetProposal* prop, bool isPassing, bool isPending)
{
    CTxDestination recipient;
    ExtractDestination(prop->GetPayee(), recipient);

    // Calculate status
    int votesYes = prop->GetYeas();
    int votesNo = prop->GetNays();
    ProposalInfo::Status status;

    if (isPending) {
        // Proposal waiting for confirmation to be broadcasted.
        status = ProposalInfo::WAITING_FOR_APPROVAL;
    } else {
        if (isPassing) {
            status = ProposalInfo::PASSING;
        } else if (votesYes > votesNo) {
            status = ProposalInfo::PASSING_NOT_FUNDED;
        } else {
            status = ProposalInfo::NOT_PASSING;
        }
    }

    return ProposalInfo(prop->GetHash(),
            prop->GetName(),
            prop->GetURL(),
            votesYes,
            votesNo,
            Standard::EncodeDestination(recipient),
            prop->GetAmount(),
            prop->GetTotalPaymentCount(),
            prop->GetRemainingPaymentCount(clientModel->getLastBlockProcessedHeight()),
            status);
}

std::list<ProposalInfo> GovernanceModel::getProposals()
{
    if (!clientModel) return {};
    std::list<ProposalInfo> ret;
    std::vector<CBudgetProposal> budget = g_budgetman.GetBudget();
    for (const auto& prop : g_budgetman.GetAllProposalsOrdered()) {
        bool isPassing = std::find(budget.begin(), budget.end(), *prop) != budget.end();
        ret.emplace_back(buidProposalInfo(prop, isPassing, false));
        if (isPassing) allocatedAmount += prop->GetAmount();
    }

    // Add pending proposals
    for (const auto& prop : waitingPropsForConfirmations) {
        ret.emplace_back(buidProposalInfo(&prop, false, true));
    }
    return ret;
}

bool GovernanceModel::hasProposals()
{
    return g_budgetman.HasAnyProposal();
}

CAmount GovernanceModel::getMaxAvailableBudgetAmount() const
{
    return Params().GetConsensus().nBudgetCycleBlocks * COIN;
}

int GovernanceModel::getNumBlocksPerBudgetCycle() const
{
    return Params().GetConsensus().nBudgetCycleBlocks;
}

int GovernanceModel::getPropMaxPaymentsCount() const
{
    return Params().GetConsensus().nMaxProposalPayments;
}

int GovernanceModel::getNextSuperblockHeight() const
{
    const int nBlocksPerCycle = getNumBlocksPerBudgetCycle();
    const int chainHeight = clientModel->getNumBlocks();
    return chainHeight - chainHeight % nBlocksPerCycle + nBlocksPerCycle;
}

std::vector<VoteInfo> GovernanceModel::getLocalMNsVotesForProposal(const ProposalInfo& propInfo)
{
    // First, get the local masternodes
    std::vector<COutPoint> vecLocalMn;
    for (int i = 0; i < mnModel->rowCount(); ++i) {
        vecLocalMn.emplace_back(
                uint256S(mnModel->index(i, MNModel::COLLATERAL_ID, QModelIndex()).data().toString().toStdString()),
                mnModel->index(i, MNModel::COLLATERAL_OUT_INDEX, QModelIndex()).data().toInt()
        );
    }

    std::vector<VoteInfo> localVotes;
    // Get the budget proposal, get the votes, then loop over it and return the ones that correspond to the local masternodes here.
    CBudgetProposal* prop = g_budgetman.FindProposal(propInfo.id);
    const auto& mapVotes = prop->GetVotes();
    for (const auto& it : mapVotes) {
        for (const auto& mn : vecLocalMn) {
            if (it.first == mn && it.second.IsValid()) {
                localVotes.emplace_back(mn, (VoteInfo::VoteDirection) it.second.GetDirection());
                break;
            }
        }
    }
    return localVotes;
}

OperationResult GovernanceModel::validatePropURL(const QString& url) const
{
    std::string strError;
    return {validateURL(url.toStdString(), strError, PROP_URL_MAX_SIZE), strError};
}

OperationResult GovernanceModel::validatePropAmount(CAmount amount) const
{
    if (amount > getMaxAvailableBudgetAmount()) {
        return {false, strprintf("Amount exceeding the maximum available budget amount of %s PIV", FormatMoney(amount))};
    }
    return {true};
}

OperationResult GovernanceModel::validatePropPaymentCount(int paymentCount) const
{
    if (paymentCount < 1) return { false, "Invalid payment count, must be greater than zero."};
    int nMaxPayments = getPropMaxPaymentsCount();
    if (paymentCount > nMaxPayments) {
        return { false, strprintf("Invalid payment count, cannot be greater than %d", nMaxPayments)};
    }
    return {true};
}

bool GovernanceModel::isTierTwoSync()
{
    return masternodeSync.IsBlockchainSynced();
}

OperationResult GovernanceModel::createProposal(const std::string& strProposalName,
                                                const std::string& strURL,
                                                int nPaymentCount,
                                                CAmount nAmount,
                                                const std::string& strPaymentAddr)
{
    // First get the next superblock height
    int nBlockStart = getNextSuperblockHeight();

    // Parse address
    const CTxDestination* dest = Standard::GetTransparentDestination(Standard::DecodeDestination(strPaymentAddr));
    if (!dest) return {false, "invalid recipient address for the proposal"};
    CScript scriptPubKey = GetScriptForDestination(*dest);

    // Validate proposal
    CBudgetProposal proposal(strProposalName, strURL, nPaymentCount, scriptPubKey, nAmount, nBlockStart, UINT256_ZERO);
    if (!proposal.IsWellFormed(g_budgetman.GetTotalBudget(proposal.GetBlockStart()))) {
        return {false, strprintf("Proposal is not valid %s", proposal.IsInvalidReason())};
    }

    // Craft and send transaction.
    auto opRes = walletModel->createAndSendProposalFeeTx(proposal);
    if (!opRes) return opRes;
    scheduleBroadcast(proposal);

    return {true};
}

OperationResult GovernanceModel::voteForProposal(const ProposalInfo& prop,
                                                 bool isVotePositive,
                                                 const std::vector<std::string>& mnVotingAlias)
{
    UniValue ret; // future: don't use UniValue here.
    for (const auto& mnAlias : mnVotingAlias) {
        bool fLegacyMN = true; // For now, only legacy MNs
        ret = mnBudgetVoteInner(nullptr,
                          fLegacyMN,
                          prop.id,
                          false,
                          isVotePositive ? CBudgetVote::VoteDirection::VOTE_YES : CBudgetVote::VoteDirection::VOTE_NO,
                          mnAlias);
        if (ret.exists("detail") && ret["detail"].isArray()) {
            const UniValue& obj = ret["detail"].get_array()[0];
            if (obj["result"].getValStr() != "success") {
                return {false, obj["error"].getValStr()};
            }
        }
    }
    // add more information with ret["overall"]
    return {true};
}

void GovernanceModel::scheduleBroadcast(const CBudgetProposal& proposal)
{
    // Cache the proposal to be sent as soon as it gets the minimum required confirmations
    // without requiring user interaction
    waitingPropsForConfirmations.emplace_back(proposal);

    // Launch timer if it's not already running
    if (!pollTimer) pollTimer = new QTimer(this);
    if (!pollTimer->isActive()) {
        connect(pollTimer, &QTimer::timeout, this, &GovernanceModel::pollGovernanceChanged);
        pollTimer->start(MODEL_UPDATE_DELAY * 60 * (walletModel->isTestNetwork() ? 0.5 : 3.5)); // Every 3.5 minutes
    }
}

void GovernanceModel::pollGovernanceChanged()
{
    if (!isTierTwoSync()) return;

    int chainHeight = clientModel->getNumBlocks();
    // Try to broadcast any pending for confirmations proposal
    auto it = waitingPropsForConfirmations.begin();
    while (it != waitingPropsForConfirmations.end()) {
        // Remove expired proposals
        if (it->IsExpired(clientModel->getNumBlocks())) {
            it = waitingPropsForConfirmations.erase(it);
            continue;
        }

        // Try to add it
        if (!g_budgetman.AddProposal(*it)) {
            LogPrint(BCLog::QT, "Cannot broadcast budget proposal - %s", it->IsInvalidReason());
            // Remove proposals who due a reorg lost their fee tx
            if (it->IsInvalidReason().find("Can't find collateral tx") != std::string::npos) {
                // future: notify the user about it.
                it = waitingPropsForConfirmations.erase(it);
                continue;
            }
            // Check if the proposal didn't exceed the superblock start height
            if (it->GetBlockStart() >= chainHeight) {
                // Edge case, the proposal was never broadcasted before the next superblock, can be removed.
                // future: notify the user about it.
                it = waitingPropsForConfirmations.erase(it);
            } else {
                it++;
            }
            continue;
        }
        it->Relay();
        it = waitingPropsForConfirmations.erase(it);
    }

    // If there are no more waiting proposals, turn the timer off.
    if (waitingPropsForConfirmations.empty()) {
        pollTimer->stop();
    }
}

void GovernanceModel::stopPolling()
{
    if (pollTimer && pollTimer->isActive()) {
        pollTimer->stop();
    }
}

void GovernanceModel::txLoaded(const QString& id, const int txType, const int txStatus)
{
    if (txType == TransactionRecord::SendToNobody) {
        // If this is a proposal fee, parse it.
        const auto& wtx = walletModel->getTx(uint256S(id.toStdString()));
        assert(wtx);
        const auto& it = wtx->mapValue.find("proposal");
        if (it != wtx->mapValue.end()) {
            const std::vector<unsigned char> vec = ParseHex(it->second);
            if (vec.empty()) return;
            CDataStream ss(vec, SER_DISK, CLIENT_VERSION);
            CBudgetProposal proposal;
            ss >> proposal;
            if (!g_budgetman.HaveProposal(proposal.GetHash()) &&
                !proposal.IsExpired(clientModel->getNumBlocks()) &&
                proposal.GetBlockStart() < clientModel->getNumBlocks()) {
                scheduleBroadcast(proposal);
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2021 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.

#include "qt/pivx/governancemodel.h"

#include "budget/budgetmanager.h"
#include "budget/budgetutil.h"
#include "destination_io.h"
#include "guiconstants.h"
#include "script/standard.h"
#include "qt/transactiontablemodel.h"
#include "qt/transactionrecord.h"
#include "qt/pivx/mnmodel.h"
#include "tiertwo/tiertwo_sync_state.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include "walletmodel.h"

#include <algorithm>
#include <QTimer>

std::string ProposalInfo::statusToStr() const
{
    switch(status) {
        case WAITING_FOR_APPROVAL:
            return _("Waiting");
        case PASSING:
            return _("Passing");
        case PASSING_NOT_FUNDED:
            return _("Passing not funded");
        case NOT_PASSING:
            return _("Not Passing");
        case FINISHED:
            return _("Finished");
    }
    return "";
}

GovernanceModel::GovernanceModel(ClientModel* _clientModel, MNModel* _mnModel) : clientModel(_clientModel), mnModel(_mnModel) {}
GovernanceModel::~GovernanceModel() {}

void GovernanceModel::setWalletModel(WalletModel* _walletModel)
{
    walletModel = _walletModel;
    connect(walletModel->getTransactionTableModel(), &TransactionTableModel::txLoaded, this, &GovernanceModel::txLoaded);
}

ProposalInfo GovernanceModel::buildProposalInfo(const CBudgetProposal* prop, bool isPassing, bool isPending)
{
    CTxDestination recipient;
    ExtractDestination(prop->GetPayee(), recipient);

    // Calculate status
    int votesYes = prop->GetYeas();
    int votesNo = prop->GetNays();
    int remainingPayments = prop->GetRemainingPaymentCount(clientModel->getLastBlockProcessedHeight());
    ProposalInfo::Status status;

    if (isPending) {
        // Proposal waiting for confirmation to be broadcasted.
        status = ProposalInfo::WAITING_FOR_APPROVAL;
    } else {
        if (remainingPayments <= 0) {
            status = ProposalInfo::FINISHED;
        } else if (isPassing) {
            status = ProposalInfo::PASSING;
        } else if (votesYes > votesNo) {
            status = ProposalInfo::PASSING_NOT_FUNDED;
        } else {
            status = ProposalInfo::NOT_PASSING;
        }
    }

    return ProposalInfo(prop->GetHash(),
            prop->GetName(),
            prop->GetURL(),
            votesYes,
            votesNo,
            Standard::EncodeDestination(recipient),
            prop->GetAmount(),
            prop->GetTotalPaymentCount(),
            remainingPayments,
            status,
            prop->GetBlockStart(),
            prop->GetBlockEnd());
}

std::list<ProposalInfo> GovernanceModel::getProposals(const ProposalInfo::Status* filterByStatus, bool filterFinished)
{
    if (!clientModel) return {};
    std::list<ProposalInfo> ret;
    std::vector<CBudgetProposal> budget = g_budgetman.GetBudget();
    allocatedAmount = 0;
    for (const auto& prop : g_budgetman.GetAllProposalsOrdered()) {
        bool isPassing = std::find(budget.begin(), budget.end(), *prop) != budget.end();
        ProposalInfo propInfo = buildProposalInfo(prop, isPassing, false);

        if (filterFinished && propInfo.isFinished()) continue;
        if (!filterByStatus || propInfo.status == *filterByStatus) {
            ret.emplace_back(propInfo);
        }
        if (isPassing) allocatedAmount += prop->GetAmount();
    }

    // Add pending proposals
    for (const auto& prop : waitingPropsForConfirmations) {
        ProposalInfo propInfo = buildProposalInfo(&prop, false, true);
        if (!filterByStatus || propInfo.status == *filterByStatus) {
            ret.emplace_back(propInfo);
        }
    }
    return ret;
}

bool GovernanceModel::hasProposals()
{
    return g_budgetman.HasAnyProposal() || !waitingPropsForConfirmations.empty();
}

CAmount GovernanceModel::getMaxAvailableBudgetAmount() const
{
    return g_budgetman.GetTotalBudget(clientModel->getNumBlocks());
}

int GovernanceModel::getNumBlocksPerBudgetCycle() const
{
    return Params().GetConsensus().nBudgetCycleBlocks;
}

int GovernanceModel::getProposalVoteUpdateMinTime() const
{
    return BUDGET_VOTE_UPDATE_MIN;
}

int GovernanceModel::getPropMaxPaymentsCount() const
{
    return Params().GetConsensus().nMaxProposalPayments;
}

CAmount GovernanceModel::getProposalFeeAmount() const
{
    return PROPOSAL_FEE_TX;
}

int GovernanceModel::getNextSuperblockHeight() const
{
    const int nBlocksPerCycle = getNumBlocksPerBudgetCycle();
    const int chainHeight = clientModel->getNumBlocks();
    return chainHeight - chainHeight % nBlocksPerCycle + nBlocksPerCycle;
}

std::vector<VoteInfo> GovernanceModel::getLocalMNsVotesForProposal(const ProposalInfo& propInfo)
{
    // First, get the local masternodes
    std::vector<std::pair<COutPoint, std::string>> vecLocalMn;
    for (int i = 0; i < mnModel->rowCount(); ++i) {
        vecLocalMn.emplace_back(std::make_pair(
                COutPoint(uint256S(mnModel->index(i, MNModel::COLLATERAL_ID, QModelIndex()).data().toString().toStdString()),
                mnModel->index(i, MNModel::COLLATERAL_OUT_INDEX, QModelIndex()).data().toInt()),
                mnModel->index(i, MNModel::ALIAS, QModelIndex()).data().toString().toStdString())
        );
    }

    std::vector<VoteInfo> localVotes;
    {
        LOCK(g_budgetman.cs_proposals); // future: encapsulate this mutex lock.
        // Get the budget proposal, get the votes, then loop over it and return the ones that correspond to the local masternodes here.
        CBudgetProposal* prop = g_budgetman.FindProposal(propInfo.id);
        const auto& mapVotes = prop->GetVotes();
        for (const auto& it : mapVotes) {
            for (const auto& mn : vecLocalMn) {
                if (it.first == mn.first && it.second.IsValid()) {
                    localVotes.emplace_back(mn.first, (VoteInfo::VoteDirection) it.second.GetDirection(), mn.second, it.second.GetTime());
                    break;
                }
            }
        }
    }
    return localVotes;
}

OperationResult GovernanceModel::validatePropName(const QString& name) const
{
    if ((int) name.toUtf8().size() > PROP_NAME_MAX_SIZE) { // limit
        return {false, _("Invalid name, maximum size exceeded")};
    }
    return {true};
}

OperationResult GovernanceModel::validatePropURL(const QString& url) const
{
    std::string strError;
    return {validateURL(url.toStdString(), strError, PROP_URL_MAX_SIZE), strError};
}

OperationResult GovernanceModel::validatePropAmount(CAmount amount) const
{
    if (amount < PROPOSAL_MIN_AMOUNT) { // Future: move constant to a budget interface.
        return {false, strprintf(_("Amount below the minimum of %s PIV"), FormatMoney(PROPOSAL_MIN_AMOUNT))};
    }

    if (amount > getMaxAvailableBudgetAmount()) {
        return {false, strprintf(_("Amount exceeding the maximum available of %s PIV"), FormatMoney(getMaxAvailableBudgetAmount()))};
    }
    return {true};
}

OperationResult GovernanceModel::validatePropPaymentCount(int paymentCount) const
{
    if (paymentCount < 1) return { false, _("Invalid payment count, must be greater than zero.")};
    int nMaxPayments = getPropMaxPaymentsCount();
    if (paymentCount > nMaxPayments) {
        return { false, strprintf(_("Invalid payment count, cannot be greater than %d"), nMaxPayments)};
    }
    return {true};
}

bool GovernanceModel::isTierTwoSync()
{
    return g_tiertwo_sync_state.IsBlockchainSynced();
}

OperationResult GovernanceModel::createProposal(const std::string& strProposalName,
                                                const std::string& strURL,
                                                int nPaymentCount,
                                                CAmount nAmount,
                                                const std::string& strPaymentAddr)
{
    // First get the next superblock height
    int nBlockStart = getNextSuperblockHeight();

    // Parse address
    const CTxDestination* dest = Standard::GetTransparentDestination(Standard::DecodeDestination(strPaymentAddr));
    if (!dest) return {false, _("invalid recipient address for the proposal")};
    CScript scriptPubKey = GetScriptForDestination(*dest);

    // Validate proposal
    CBudgetProposal proposal(strProposalName, strURL, nPaymentCount, scriptPubKey, nAmount, nBlockStart, UINT256_ZERO);
    if (!proposal.IsWellFormed(g_budgetman.GetTotalBudget(proposal.GetBlockStart()))) {
        return {false, strprintf(_("Proposal is not valid %s"), proposal.IsInvalidReason())};
    }

    // Craft and send transaction.
    auto opRes = walletModel->createAndSendProposalFeeTx(proposal);
    if (!opRes) return opRes;
    scheduleBroadcast(proposal);

    return {true};
}

OperationResult GovernanceModel::voteForProposal(const ProposalInfo& prop,
                                                 bool isVotePositive,
                                                 const std::vector<std::string>& mnVotingAlias)
{
    UniValue ret; // future: don't use UniValue here.
    for (const auto& mnAlias : mnVotingAlias) {
        bool fLegacyMN = true; // For now, only legacy MNs
        ret = mnBudgetVoteInner(nullptr,
                          fLegacyMN,
                          prop.id,
                          false,
                          isVotePositive ? CBudgetVote::VoteDirection::VOTE_YES : CBudgetVote::VoteDirection::VOTE_NO,
                          mnAlias);
        if (ret.exists("detail") && ret["detail"].isArray()) {
            const UniValue& obj = ret["detail"].get_array()[0];
            if (obj["result"].getValStr() != "success") {
                return {false, obj["error"].getValStr()};
            }
        }
    }
    // add more information with ret["overall"]
    return {true};
}

void GovernanceModel::scheduleBroadcast(const CBudgetProposal& proposal)
{
    // Cache the proposal to be sent as soon as it gets the minimum required confirmations
    // without requiring user interaction
    waitingPropsForConfirmations.emplace_back(proposal);

    // Launch timer if it's not already running
    if (!pollTimer) pollTimer = new QTimer(this);
    if (!pollTimer->isActive()) {
        connect(pollTimer, &QTimer::timeout, this, &GovernanceModel::pollGovernanceChanged);
        pollTimer->start(MODEL_UPDATE_DELAY * 60 * (walletModel->isTestNetwork() ? 0.5 : 3.5)); // Every 3.5 minutes
    }
}

void GovernanceModel::pollGovernanceChanged()
{
    if (!isTierTwoSync()) return;

    int chainHeight = clientModel->getNumBlocks();
    // Try to broadcast any pending for confirmations proposal
    auto it = waitingPropsForConfirmations.begin();
    while (it != waitingPropsForConfirmations.end()) {
        // Remove expired proposals
        if (it->IsExpired(clientModel->getNumBlocks())) {
            it = waitingPropsForConfirmations.erase(it);
            continue;
        }

        // Try to add it
        if (!g_budgetman.AddProposal(*it)) {
            LogPrint(BCLog::QT, "Cannot broadcast budget proposal - %s", it->IsInvalidReason());
            // Remove proposals which due a reorg lost their fee tx
            if (it->IsInvalidReason().find("Can't find collateral tx") != std::string::npos) {
                // future: notify the user about it.
                it = waitingPropsForConfirmations.erase(it);
                continue;
            }
            // Check if the proposal didn't exceed the superblock start height
            if (chainHeight >= it->GetBlockStart()) {
                // Edge case, the proposal was never broadcasted before the next superblock, can be removed.
                // future: notify the user about it.
                it = waitingPropsForConfirmations.erase(it);
            } else {
                it++;
            }
            continue;
        }
        it->Relay();
        it = waitingPropsForConfirmations.erase(it);
    }

    // If there are no more waiting proposals, turn the timer off.
    if (waitingPropsForConfirmations.empty()) {
        pollTimer->stop();
    }
}

void GovernanceModel::stop()
{
    if (pollTimer && pollTimer->isActive()) {
        pollTimer->stop();
    }
}

void GovernanceModel::txLoaded(const QString& id, const int txType, const int txStatus)
{
    if (txType == TransactionRecord::SendToNobody) {
        // If the tx is not longer available in the mainchain, drop it.
        if (txStatus == TransactionStatus::Conflicted ||
            txStatus == TransactionStatus::NotAccepted) {
            return;
        }
        // If this is a proposal fee, parse it.
        const auto& wtx = walletModel->getTx(uint256S(id.toStdString()));
        assert(wtx);
        const auto& it = wtx->mapValue.find("proposal");
        if (it != wtx->mapValue.end()) {
            const std::vector<unsigned char> vec = ParseHex(it->second);
            if (vec.empty()) return;
            CDataStream ss(vec, SER_DISK, CLIENT_VERSION);
            CBudgetProposal proposal;
            ss >> proposal;
            if (!g_budgetman.HaveProposal(proposal.GetHash()) &&
                !proposal.IsExpired(clientModel->getNumBlocks()) &&
                proposal.GetBlockStart() > clientModel->getNumBlocks()) {
                scheduleBroadcast(proposal);
            }
        }
    }
}
<commit_msg>Build: Fix sign comparison in governancemodel.cpp<commit_after>// Copyright (c) 2021 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.

#include "qt/pivx/governancemodel.h"

#include "budget/budgetmanager.h"
#include "budget/budgetutil.h"
#include "destination_io.h"
#include "guiconstants.h"
#include "script/standard.h"
#include "qt/transactiontablemodel.h"
#include "qt/transactionrecord.h"
#include "qt/pivx/mnmodel.h"
#include "tiertwo/tiertwo_sync_state.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include "walletmodel.h"

#include <algorithm>
#include <QTimer>

std::string ProposalInfo::statusToStr() const
{
    switch(status) {
        case WAITING_FOR_APPROVAL:
            return _("Waiting");
        case PASSING:
            return _("Passing");
        case PASSING_NOT_FUNDED:
            return _("Passing not funded");
        case NOT_PASSING:
            return _("Not Passing");
        case FINISHED:
            return _("Finished");
    }
    return "";
}

GovernanceModel::GovernanceModel(ClientModel* _clientModel, MNModel* _mnModel) : clientModel(_clientModel), mnModel(_mnModel) {}
GovernanceModel::~GovernanceModel() {}

void GovernanceModel::setWalletModel(WalletModel* _walletModel)
{
    walletModel = _walletModel;
    connect(walletModel->getTransactionTableModel(), &TransactionTableModel::txLoaded, this, &GovernanceModel::txLoaded);
}

ProposalInfo GovernanceModel::buildProposalInfo(const CBudgetProposal* prop, bool isPassing, bool isPending)
{
    CTxDestination recipient;
    ExtractDestination(prop->GetPayee(), recipient);

    // Calculate status
    int votesYes = prop->GetYeas();
    int votesNo = prop->GetNays();
    int remainingPayments = prop->GetRemainingPaymentCount(clientModel->getLastBlockProcessedHeight());
    ProposalInfo::Status status;

    if (isPending) {
        // Proposal waiting for confirmation to be broadcasted.
        status = ProposalInfo::WAITING_FOR_APPROVAL;
    } else {
        if (remainingPayments <= 0) {
            status = ProposalInfo::FINISHED;
        } else if (isPassing) {
            status = ProposalInfo::PASSING;
        } else if (votesYes > votesNo) {
            status = ProposalInfo::PASSING_NOT_FUNDED;
        } else {
            status = ProposalInfo::NOT_PASSING;
        }
    }

    return ProposalInfo(prop->GetHash(),
            prop->GetName(),
            prop->GetURL(),
            votesYes,
            votesNo,
            Standard::EncodeDestination(recipient),
            prop->GetAmount(),
            prop->GetTotalPaymentCount(),
            remainingPayments,
            status,
            prop->GetBlockStart(),
            prop->GetBlockEnd());
}

std::list<ProposalInfo> GovernanceModel::getProposals(const ProposalInfo::Status* filterByStatus, bool filterFinished)
{
    if (!clientModel) return {};
    std::list<ProposalInfo> ret;
    std::vector<CBudgetProposal> budget = g_budgetman.GetBudget();
    allocatedAmount = 0;
    for (const auto& prop : g_budgetman.GetAllProposalsOrdered()) {
        bool isPassing = std::find(budget.begin(), budget.end(), *prop) != budget.end();
        ProposalInfo propInfo = buildProposalInfo(prop, isPassing, false);

        if (filterFinished && propInfo.isFinished()) continue;
        if (!filterByStatus || propInfo.status == *filterByStatus) {
            ret.emplace_back(propInfo);
        }
        if (isPassing) allocatedAmount += prop->GetAmount();
    }

    // Add pending proposals
    for (const auto& prop : waitingPropsForConfirmations) {
        ProposalInfo propInfo = buildProposalInfo(&prop, false, true);
        if (!filterByStatus || propInfo.status == *filterByStatus) {
            ret.emplace_back(propInfo);
        }
    }
    return ret;
}

bool GovernanceModel::hasProposals()
{
    return g_budgetman.HasAnyProposal() || !waitingPropsForConfirmations.empty();
}

CAmount GovernanceModel::getMaxAvailableBudgetAmount() const
{
    return g_budgetman.GetTotalBudget(clientModel->getNumBlocks());
}

int GovernanceModel::getNumBlocksPerBudgetCycle() const
{
    return Params().GetConsensus().nBudgetCycleBlocks;
}

int GovernanceModel::getProposalVoteUpdateMinTime() const
{
    return BUDGET_VOTE_UPDATE_MIN;
}

int GovernanceModel::getPropMaxPaymentsCount() const
{
    return Params().GetConsensus().nMaxProposalPayments;
}

CAmount GovernanceModel::getProposalFeeAmount() const
{
    return PROPOSAL_FEE_TX;
}

int GovernanceModel::getNextSuperblockHeight() const
{
    const int nBlocksPerCycle = getNumBlocksPerBudgetCycle();
    const int chainHeight = clientModel->getNumBlocks();
    return chainHeight - chainHeight % nBlocksPerCycle + nBlocksPerCycle;
}

std::vector<VoteInfo> GovernanceModel::getLocalMNsVotesForProposal(const ProposalInfo& propInfo)
{
    // First, get the local masternodes
    std::vector<std::pair<COutPoint, std::string>> vecLocalMn;
    for (int i = 0; i < mnModel->rowCount(); ++i) {
        vecLocalMn.emplace_back(std::make_pair(
                COutPoint(uint256S(mnModel->index(i, MNModel::COLLATERAL_ID, QModelIndex()).data().toString().toStdString()),
                mnModel->index(i, MNModel::COLLATERAL_OUT_INDEX, QModelIndex()).data().toInt()),
                mnModel->index(i, MNModel::ALIAS, QModelIndex()).data().toString().toStdString())
        );
    }

    std::vector<VoteInfo> localVotes;
    {
        LOCK(g_budgetman.cs_proposals); // future: encapsulate this mutex lock.
        // Get the budget proposal, get the votes, then loop over it and return the ones that correspond to the local masternodes here.
        CBudgetProposal* prop = g_budgetman.FindProposal(propInfo.id);
        const auto& mapVotes = prop->GetVotes();
        for (const auto& it : mapVotes) {
            for (const auto& mn : vecLocalMn) {
                if (it.first == mn.first && it.second.IsValid()) {
                    localVotes.emplace_back(mn.first, (VoteInfo::VoteDirection) it.second.GetDirection(), mn.second, it.second.GetTime());
                    break;
                }
            }
        }
    }
    return localVotes;
}

OperationResult GovernanceModel::validatePropName(const QString& name) const
{
    if (name.toUtf8().size() > (int)PROP_NAME_MAX_SIZE) { // limit
        return {false, _("Invalid name, maximum size exceeded")};
    }
    return {true};
}

OperationResult GovernanceModel::validatePropURL(const QString& url) const
{
    std::string strError;
    return {validateURL(url.toStdString(), strError, PROP_URL_MAX_SIZE), strError};
}

OperationResult GovernanceModel::validatePropAmount(CAmount amount) const
{
    if (amount < PROPOSAL_MIN_AMOUNT) { // Future: move constant to a budget interface.
        return {false, strprintf(_("Amount below the minimum of %s PIV"), FormatMoney(PROPOSAL_MIN_AMOUNT))};
    }

    if (amount > getMaxAvailableBudgetAmount()) {
        return {false, strprintf(_("Amount exceeding the maximum available of %s PIV"), FormatMoney(getMaxAvailableBudgetAmount()))};
    }
    return {true};
}

OperationResult GovernanceModel::validatePropPaymentCount(int paymentCount) const
{
    if (paymentCount < 1) return { false, _("Invalid payment count, must be greater than zero.")};
    int nMaxPayments = getPropMaxPaymentsCount();
    if (paymentCount > nMaxPayments) {
        return { false, strprintf(_("Invalid payment count, cannot be greater than %d"), nMaxPayments)};
    }
    return {true};
}

bool GovernanceModel::isTierTwoSync()
{
    return g_tiertwo_sync_state.IsBlockchainSynced();
}

OperationResult GovernanceModel::createProposal(const std::string& strProposalName,
                                                const std::string& strURL,
                                                int nPaymentCount,
                                                CAmount nAmount,
                                                const std::string& strPaymentAddr)
{
    // First get the next superblock height
    int nBlockStart = getNextSuperblockHeight();

    // Parse address
    const CTxDestination* dest = Standard::GetTransparentDestination(Standard::DecodeDestination(strPaymentAddr));
    if (!dest) return {false, _("invalid recipient address for the proposal")};
    CScript scriptPubKey = GetScriptForDestination(*dest);

    // Validate proposal
    CBudgetProposal proposal(strProposalName, strURL, nPaymentCount, scriptPubKey, nAmount, nBlockStart, UINT256_ZERO);
    if (!proposal.IsWellFormed(g_budgetman.GetTotalBudget(proposal.GetBlockStart()))) {
        return {false, strprintf(_("Proposal is not valid %s"), proposal.IsInvalidReason())};
    }

    // Craft and send transaction.
    auto opRes = walletModel->createAndSendProposalFeeTx(proposal);
    if (!opRes) return opRes;
    scheduleBroadcast(proposal);

    return {true};
}

OperationResult GovernanceModel::voteForProposal(const ProposalInfo& prop,
                                                 bool isVotePositive,
                                                 const std::vector<std::string>& mnVotingAlias)
{
    UniValue ret; // future: don't use UniValue here.
    for (const auto& mnAlias : mnVotingAlias) {
        bool fLegacyMN = true; // For now, only legacy MNs
        ret = mnBudgetVoteInner(nullptr,
                          fLegacyMN,
                          prop.id,
                          false,
                          isVotePositive ? CBudgetVote::VoteDirection::VOTE_YES : CBudgetVote::VoteDirection::VOTE_NO,
                          mnAlias);
        if (ret.exists("detail") && ret["detail"].isArray()) {
            const UniValue& obj = ret["detail"].get_array()[0];
            if (obj["result"].getValStr() != "success") {
                return {false, obj["error"].getValStr()};
            }
        }
    }
    // add more information with ret["overall"]
    return {true};
}

void GovernanceModel::scheduleBroadcast(const CBudgetProposal& proposal)
{
    // Cache the proposal to be sent as soon as it gets the minimum required confirmations
    // without requiring user interaction
    waitingPropsForConfirmations.emplace_back(proposal);

    // Launch timer if it's not already running
    if (!pollTimer) pollTimer = new QTimer(this);
    if (!pollTimer->isActive()) {
        connect(pollTimer, &QTimer::timeout, this, &GovernanceModel::pollGovernanceChanged);
        pollTimer->start(MODEL_UPDATE_DELAY * 60 * (walletModel->isTestNetwork() ? 0.5 : 3.5)); // Every 3.5 minutes
    }
}

void GovernanceModel::pollGovernanceChanged()
{
    if (!isTierTwoSync()) return;

    int chainHeight = clientModel->getNumBlocks();
    // Try to broadcast any pending for confirmations proposal
    auto it = waitingPropsForConfirmations.begin();
    while (it != waitingPropsForConfirmations.end()) {
        // Remove expired proposals
        if (it->IsExpired(clientModel->getNumBlocks())) {
            it = waitingPropsForConfirmations.erase(it);
            continue;
        }

        // Try to add it
        if (!g_budgetman.AddProposal(*it)) {
            LogPrint(BCLog::QT, "Cannot broadcast budget proposal - %s", it->IsInvalidReason());
            // Remove proposals which due a reorg lost their fee tx
            if (it->IsInvalidReason().find("Can't find collateral tx") != std::string::npos) {
                // future: notify the user about it.
                it = waitingPropsForConfirmations.erase(it);
                continue;
            }
            // Check if the proposal didn't exceed the superblock start height
            if (chainHeight >= it->GetBlockStart()) {
                // Edge case, the proposal was never broadcasted before the next superblock, can be removed.
                // future: notify the user about it.
                it = waitingPropsForConfirmations.erase(it);
            } else {
                it++;
            }
            continue;
        }
        it->Relay();
        it = waitingPropsForConfirmations.erase(it);
    }

    // If there are no more waiting proposals, turn the timer off.
    if (waitingPropsForConfirmations.empty()) {
        pollTimer->stop();
    }
}

void GovernanceModel::stop()
{
    if (pollTimer && pollTimer->isActive()) {
        pollTimer->stop();
    }
}

void GovernanceModel::txLoaded(const QString& id, const int txType, const int txStatus)
{
    if (txType == TransactionRecord::SendToNobody) {
        // If the tx is not longer available in the mainchain, drop it.
        if (txStatus == TransactionStatus::Conflicted ||
            txStatus == TransactionStatus::NotAccepted) {
            return;
        }
        // If this is a proposal fee, parse it.
        const auto& wtx = walletModel->getTx(uint256S(id.toStdString()));
        assert(wtx);
        const auto& it = wtx->mapValue.find("proposal");
        if (it != wtx->mapValue.end()) {
            const std::vector<unsigned char> vec = ParseHex(it->second);
            if (vec.empty()) return;
            CDataStream ss(vec, SER_DISK, CLIENT_VERSION);
            CBudgetProposal proposal;
            ss >> proposal;
            if (!g_budgetman.HaveProposal(proposal.GetHash()) &&
                !proposal.IsExpired(clientModel->getNumBlocks()) &&
                proposal.GetBlockStart() > clientModel->getNumBlocks()) {
                scheduleBroadcast(proposal);
            }
        }
    }
}
<|endoftext|>