#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "binaryninjaapi.h" #include "action.h" #include "globalarea.h" #include "uitypes.h" #define SCRIPT_OUTPUT_UPDATE_INTERVAL 100 class ScriptingConsole; /*! \defgroup scriptingconsole ScriptingConsole \ingroup uiapi */ /*! \ingroup scriptingconsole */ class BINARYNINJAUIAPI ScriptingCompletionModel : public QAbstractListModel { Q_OBJECT std::vector m_completions; bool m_searching; public: ScriptingCompletionModel(QWidget* parent); virtual QModelIndex index(int row, int col, const QModelIndex& parent = QModelIndex()) const override; virtual QModelIndex parent(const QModelIndex& i) const override; virtual bool hasChildren(const QModelIndex& parent) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent) const override; virtual QVariant data(const QModelIndex& i, int role) const override; void setModelData(const std::vector& completions, bool searching); }; /*! \ingroup scriptingconsole */ class BINARYNINJAUIAPI ScriptingCompletionPopup : public QDialog { Q_OBJECT QListView* m_list; ScriptingCompletionModel* m_model; protected: virtual bool eventFilter(QObject* obj, QEvent* event) override; public: ScriptingCompletionPopup(QWidget* parent); void showWithData(QPoint pt, int cursorSize, const std::vector& completions, bool searching = false); bool handleKeyEvent(QKeyEvent* event); virtual ~ScriptingCompletionPopup(); private Q_SLOTS: void clickRow(const QModelIndex& index); Q_SIGNALS: void complete(QString text); }; /*! Argument display popup. \ingroup scriptingconsole */ class BINARYNINJAUIAPI ScriptingArgumentAssistPopup : public QDialog { Q_OBJECT QTextBrowser* m_textDisplay; QString m_text; Q_PROPERTY(QString text READ getText WRITE setText RESET unsetText) void updateText(); public: ScriptingArgumentAssistPopup(QWidget* parent); virtual ~ScriptingArgumentAssistPopup(); void setText(const QString& text) { m_text = text; updateText(); } QString getText() const { return m_text; } void unsetText() { m_text.clear(); updateText(); } protected: virtual bool eventFilter(QObject* obj, QEvent* event) override; }; /*! \ingroup scriptingconsole */ class BINARYNINJAUIAPI ScriptingConsoleEdit : public QTextEdit { Q_OBJECT public: typedef std::function(const std::string&)> CompletionCallback; typedef std::function CanDoCompleteArgumentsCallback; typedef std::function(const std::string&)> CompleteArgumentsCallback; private: struct ArgumentAssistRequestState { std::atomic latestRequest {0}; std::mutex callbackMutex; }; static constexpr int ArgumentAssistDebounceInterval = 100; ScriptingConsole* m_console; int m_charHeight; bool m_continuation; bool m_searching; bool m_searchError; CompletionCallback m_completionCallback; ScriptingCompletionPopup* m_popup; CanDoCompleteArgumentsCallback m_canDoArgumentCompletionCallback; CompleteArgumentsCallback m_completeArgumentsCallback; ScriptingArgumentAssistPopup* m_argumentAssistPopup; QTimer* m_argumentAssistUpdateTimer; std::shared_ptr m_argumentAssistState; bool m_argumentAssistRequestRunning; bool m_argumentAssistRequestPending; uint64_t m_argumentAssistPopupPosition; uint64_t m_completionRegionStart; uint64_t m_completionRegionInitialStop; uint64_t m_completionRegionStop; bool canShowArgumentAssistPopup() const; void hideArgumentAssistPopup(); void scheduleArgumentAssistPopupUpdate(); void updateArgumentAssistPopup(); void applyArgumentAssistPopup(const QString& text, uint64_t popupPosition); public: ScriptingConsoleEdit(ScriptingConsole* parent); void setCharHeight(int height); void setContinuation(bool cont); void setCompletionCallback(CompletionCallback callback) { m_completionCallback = callback; } void setCanDoCompleteArgumentsCallback(CanDoCompleteArgumentsCallback callback) { m_canDoArgumentCompletionCallback = callback; } void setCompleteArgumentsCallback(CompleteArgumentsCallback callback) { m_completeArgumentsCallback = callback; } void insertFromMimeData(const QMimeData* source) override; private Q_SLOTS: void complete(QString text); protected: virtual void keyPressEvent(QKeyEvent* event) override; virtual void resizeEvent(QResizeEvent* event) override; virtual void scrollContentsBy(int dx, int dy) override; virtual void focusOutEvent(QFocusEvent* event) override; virtual void hideEvent(QHideEvent* event) override; virtual void changeEvent(QEvent* event) override; }; /*! \ingroup scriptingconsole */ class BINARYNINJAUIAPI ScriptingConsoleOutput : public QPlainTextEdit { Q_OBJECT ScriptingConsole* m_console; UIActionHandler* m_handler; UIActionHandler m_actionHandler; ContextMenuManager* m_contextMenuManager; BinaryViewRef m_data; Menu* m_menu; public: ScriptingConsoleOutput(ScriptingConsole* parent, Menu* menu); bool IsNavigable(const QString& str, const std::pair& offsetLen, uint64_t& value, bool highlight) const; protected: void contextMenuEvent(QContextMenuEvent* event) override; public Q_SLOTS: virtual void mouseReleaseEvent(QMouseEvent* event) override; void viewChanged(QWidget* frame); }; class ScriptingConsoleWidget; /*! \ingroup scriptingconsole */ class BINARYNINJAUIAPI ScriptingConsole : public SidebarWidget, BinaryNinja::ScriptingOutputListener { Q_OBJECT struct ScriptOutput { std::string text; bool isError; bool isWarning; }; QString m_providerName; QString m_instanceName; ScriptingInstanceRef m_instance; ScriptingConsoleOutput* m_output; ScriptingConsoleEdit* m_input; Splitter* m_splitter; QLabel* m_prompt; QPushButton* m_button; QTimer* m_runTimer; bool m_scriptActive; BinaryNinja::Ref m_logger; std::mutex m_mutex; std::vector m_pendingOutput; QTimer* m_updateTimer; BNScriptingProviderInputReadyState m_currentState; QStringList m_history; QString m_inputOutsideHistory; int m_currentHistoryEntry; static int m_stateUpdatedEventType; private Q_SLOTS: void updateTimerEvent(); void consoleTextChanged(); void cancel(); void showCancelButton(); Q_SIGNALS: void viewChanged(QWidget* frame); void onScriptExecution(); void onScriptCompletion(); protected: void customEvent(QEvent* event) override; public: ScriptingConsole( QWidget* parent, const QString& providerName, const QString& instanceName, ScriptingInstanceRef instance); virtual ~ScriptingConsole(); QString getProviderName() const { return m_providerName; } QString getInstanceName() const { return m_instanceName; } ScriptingInstanceRef getInstance() { return m_instance; } bool getScriptIsActive() const { return m_scriptActive; } void clearConsole(); void addInput(const std::string& text); virtual void NotifyOutput(const std::string& text) override; virtual void NotifyWarning(const std::string& text) override; virtual void NotifyError(const std::string& text) override; virtual void NotifyInputReadyStateChanged(BNScriptingProviderInputReadyState state) override; virtual void notifyViewChanged(ViewFrame* frame) override; virtual void notifyFontChanged() override; virtual void focus() override; void moveUpInHistory(); void moveDownInHistory(); void stopMovingInHistory(); std::vector reverseSearch(const QString& text); void closing() override; void runScriptFromFile(const std::string& filename); }; /*! \ingroup scriptingconsole */ class BINARYNINJAUIAPI ScriptingConsoleSidebarWidgetType : public SidebarWidgetType { public: ScriptingConsoleSidebarWidgetType(); SidebarWidgetLocation defaultLocation() const override { return SidebarWidgetLocation::LeftBottom; } SidebarContextSensitivity contextSensitivity() const override { return GlobalSidebarContext; } bool alwaysShowTabs() const override { return true; } #ifdef DEMO_EDITION virtual QString noWidgetMessage() const override; #endif };