Skip to content

Commit

Permalink
[refactor][hotfix]: implement TaintLess.xml style cleanup for touch…
Browse files Browse the repository at this point in the history
…ed DropDownList buttons

- This prevents any taint error that could be induced by us not using the UIDropDownMenu API correctly.
- This is a temporary solution to rewriting the whole `Tool.CreatePopup` system to support the new menuGenerator APIs.
  • Loading branch information
juemrami committed Jan 30, 2025
1 parent 164cb26 commit 4af53c7
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 29 deletions.
4 changes: 2 additions & 2 deletions LFGBulletinBoard/GroupBulletinBoard.lua
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ function GBB.BtnSettings(button )
if shouldOpen then
GBB.PopupDynamic:AddItem(FILTERS, false, GBB.OptionsBuilder.OpenCategoryPanel, 2)
GBB.PopupDynamic:AddItem(ALL_SETTINGS, false, GBB.OptionsBuilder.OpenCategoryPanel, 1)
GBB.PopupDynamic:AddItem(GBB.L["BtnCancel"],false)
GBB.PopupDynamic:AddItem(GBB.L["BtnCancel"], false, nil, nil, nil, true)
GBB.PopupDynamic:Show(GroupBulletinBoardFrameSettingsButton,0,0)
end
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION, "SFX")
Expand Down Expand Up @@ -532,7 +532,7 @@ function GBB.Popup_Minimap(frame,showMinimapOptions)
end
end
GBB.PopupDynamic:AddItem("",true)
GBB.PopupDynamic:AddItem(GBB.L["BtnCancel"],false)
GBB.PopupDynamic:AddItem(GBB.L["BtnCancel"], false, nil, nil, nil, true)

GBB.PopupDynamic:Show(frame,0,0)
end
Expand Down
16 changes: 8 additions & 8 deletions LFGBulletinBoard/LfgToolList.lua
Original file line number Diff line number Diff line change
Expand Up @@ -321,16 +321,16 @@ local function createMenu(DungeonID,req) -- shared right-click menu for headers
return
end
if req then
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnWho"],req.name),false,WhoRequest,req.name)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnWhisper"],req.name),false,WhisperRequest,req.name)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnInvite"],req.name),false,InviteRequest,req.name)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnIgnore"],req.name),false,IgnoreRequest,req.name)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnWho"],req.name),false,WhoRequest,req.name,nil,true)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnWhisper"],req.name),false,WhisperRequest,req.name,nil,true)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnInvite"],req.name),false,InviteRequest,req.name,nil,true)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnIgnore"],req.name),false,IgnoreRequest,req.name,nil,true)
GBB.PopupDynamic:AddItem("",true)
end
if DungeonID then
GBB.PopupDynamic:AddItem(GBB.L["BtnFold"], false, toggleHeaderCollapseByKey, DungeonID)
GBB.PopupDynamic:AddItem(GBB.L["BtnFoldAll"], false, setAllHeadersCollapsed, true)
GBB.PopupDynamic:AddItem(GBB.L["BtnUnFoldAll"], false, setAllHeadersCollapsed, false)
GBB.PopupDynamic:AddItem(GBB.L["BtnFold"], false, toggleHeaderCollapseByKey, DungeonID, nil, true)
GBB.PopupDynamic:AddItem(GBB.L["BtnFoldAll"], false, setAllHeadersCollapsed, true, nil, true)
GBB.PopupDynamic:AddItem(GBB.L["BtnUnFoldAll"], false, setAllHeadersCollapsed, false, nil, true)
GBB.PopupDynamic:AddItem("",true)
end
GBB.PopupDynamic:AddItem(GBB.L["CboxShowTotalTime"],false,GBB.DB,"ShowTotalTime")
Expand All @@ -345,7 +345,7 @@ local function createMenu(DungeonID,req) -- shared right-click menu for headers
GBB.PopupDynamic:AddItem(GBB.L["CboxRemoveRealm"],false,GBB.DB,"RemoveRealm")
GBB.PopupDynamic:AddItem("",true)
GBB.PopupDynamic:AddItem(SETTINGS, false, GBB.OptionsBuilder.OpenCategoryPanel, 1)
GBB.PopupDynamic:AddItem(GBB.L["BtnCancel"],false)
GBB.PopupDynamic:AddItem(GBB.L["BtnCancel"], false, nil, nil, nil, true)
GBB.PopupDynamic:Show()
end
--------------------------------------------------------------------------------
Expand Down
72 changes: 61 additions & 11 deletions LFGBulletinBoard/LibGPIToolBox.lua
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,22 @@ end
--------------------------------------------------------------------------------

local PopupDepth
local addonDropdowns = {} -- # {[dropdownFrame]: popup}
local touchedButtonInfoKeys = {} -- used to track tainted variables

--- Securely nils variable by hacking the `TextureLoadingGroupMixin.RemoveTexture` framexml function
local purgeInsecureVariable = function(table, key)
TextureLoadingGroupMixin.RemoveTexture({textures = table}, key)
end
---@return boolean `true` if mouse is over any DropDownList (including submenus)
local isMouseOverDropdown = function()
for i = 1, UIDROPDOWNMENU_MAXLEVELS or 0 do
local dropdown = _G['DropDownList'..i]
if dropdown:IsMouseOver() then return true end;
end
return false
end

local function PopupClick(self, arg1, arg2, checked)
if type(self.value)=="table" then
local handle = Addon.OptionsBuilder.GetSavedVarHandle(self.value, arg1)
Expand All @@ -450,13 +466,13 @@ end

---@param text string
---@param disabled boolean
---@param value table|function savedVar db or onclick function
---@param value table|function|nil savedVar db or onclick function
---@param arg1 any? If value is a table, arg1 is the key, else arg1 is the 1st arg for `value`
---@param arg2 any? If value is a function arg2 is the 2nd arg
local function PopupAddItem(self,text,disabled,value,arg1,arg2)
---@param closeOnClick boolean? If true, the dropdown will close after the button is clicked
local function PopupAddItem(self,text,disabled,value,arg1,arg2, closeOnClick)
local c=self._Frame._GPIPRIVAT_Items.count+1
self._Frame._GPIPRIVAT_Items.count=c

if not self._Frame._GPIPRIVAT_Items[c] then
self._Frame._GPIPRIVAT_Items[c]={}
end
Expand All @@ -466,6 +482,7 @@ local function PopupAddItem(self,text,disabled,value,arg1,arg2)
t.value=value
t.arg1=arg1
t.arg2=arg2
t.closeOnClick = closeOnClick
t.MenuDepth=PopupDepth
end

Expand Down Expand Up @@ -519,35 +536,37 @@ local function PopupCreate(frame, level, menuList)
info.notCheckable = true
end
info.disabled=(val.disabled==true or val.text=="" )
info.keepShownOnClick=(val.disabled=="keep")
info.keepShownOnClick=true
info.value=val.value
info.arg1=val.arg1
if type(val.value)=="table" then
info.arg2=frame._GPIPRIVAT_TableCallback
elseif type(val.value)=="function" then
info.arg2=val.arg2
end
info.func=PopupClick
info.func=function(...)
PopupClick(...);
local popup = addonDropdowns[frame]
if val.closeOnClick and popup then PopupWipe(popup) end;
end
info.hasArrow=false
info.menuList=nil
--info.isNotRadio=true
end
for key, _ in pairs(info) do touchedButtonInfoKeys[key] = true end
UIDropDownMenu_AddButton(info,level)
end
end
end

local function PopupShow(self,where,x,y)
where=where or "cursor"
if UIDROPDOWNMENU_OPEN_MENU ~= self._Frame then
UIDropDownMenu_Initialize(self._Frame, PopupCreate, "MENU")
end
UIDropDownMenu_Initialize(self._Frame, PopupCreate, "MENU")
ToggleDropDownMenu(nil, nil, self._Frame, where, x,y)
self._where=where
self._x=x
self._y=y
end

function Tool.CreatePopup(TableCallback)
local popup={}
popup._Frame=CreateFrame("Frame", nil, UIParent, "UIDropDownMenuTemplate")
Expand All @@ -558,9 +577,40 @@ function Tool.CreatePopup(TableCallback)
popup.SubMenu=PopupAddSubMenu
popup.Show=PopupShow
popup.Wipe=PopupWipe
addonDropdowns[popup._Frame] = popup
return popup
end

end
-- hack: whenever any of our dropdowns are hidden purge ANY insecure button variables/member
-- note! I'll refrain from cleaning up taint caused by other addons with this fix mostly because-
-- this fix requires buttonInfo to have the `keepShownOnClick` member set true.
-- Otherwise this hack would clear the button's `.func` handler before it has the chance to be called-
-- resulting in broken buttons that do nothing when clicked.
hooksecurefunc("UIDropDownMenu_OnHide", function(listFrame)
if not listFrame.dropdown or not addonDropdowns[listFrame.dropdown] then return end;
for i = 1, UIDROPDOWNMENU_MAXLEVELS or 0 do
for j = 1, UIDROPDOWNMENU_MAXBUTTONS or 0 do
local button = _G["DropDownList" .. i .. "Button" .. j]
if button then for key, _ in pairs(touchedButtonInfoKeys) do
local isSecure, _taintSource = issecurevariable(button, key)
if not isSecure then purgeInsecureVariable(button, key) end
end; end;
end
end
end)
-- Hide dropdown anytime mouse is clicked outside of the dropdown or its anchor region.
Tool.RegisterEvent("GLOBAL_MOUSE_DOWN",function(event, buttonName)
local dropdown = UIDROPDOWNMENU_OPEN_MENU;
if not dropdown or not addonDropdowns[dropdown] then return end;
local popup = addonDropdowns[dropdown];
local anchorFrame;
if type(popup._where) == "table" and popup._where.IsMouseOver then anchorFrame = popup._where;
elseif type(popup._where) == "string" then anchorFrame = _G[popup._where] end;

local shouldHide = true
if anchorFrame then shouldHide = not (anchorFrame:IsMouseOver() or isMouseOverDropdown());
else shouldHide = not isMouseOverDropdown(); end
if shouldHide then PopupWipe(popup); end
end)
--------------------------------------------------------------------------------
-- TAB

Expand Down
16 changes: 8 additions & 8 deletions LFGBulletinBoard/RequestList.lua
Original file line number Diff line number Diff line change
Expand Up @@ -971,16 +971,16 @@ local function createMenu(DungeonID,req)
return
end
if req then
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnWho"],req.name),false,WhoRequest,req.name)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnWhisper"],req.name),false,WhisperRequest,req.name)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnInvite"],req.name),false,InviteRequest,req.name)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnIgnore"],req.name),false,IgnoreRequest,req.name)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnWho"],req.name),false,WhoRequest,req.name,nil,true)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnWhisper"],req.name),false,WhisperRequest,req.name,nil,true)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnInvite"],req.name),false,InviteRequest,req.name,nil,true)
GBB.PopupDynamic:AddItem(string.format(GBB.L["BtnIgnore"],req.name),false,IgnoreRequest,req.name,nil,true)
GBB.PopupDynamic:AddItem("",true)
end
if DungeonID then
GBB.PopupDynamic:AddItem(GBB.L["BtnFold"], false,GBB.FoldedDungeons,DungeonID)
GBB.PopupDynamic:AddItem(GBB.L["BtnFoldAll"], false,GBB.FoldAllDungeon)
GBB.PopupDynamic:AddItem(GBB.L["BtnUnFoldAll"], false,GBB.UnfoldAllDungeon)
GBB.PopupDynamic:AddItem(GBB.L["BtnFold"], false,GBB.FoldedDungeons,DungeonID, nil, true)
GBB.PopupDynamic:AddItem(GBB.L["BtnFoldAll"], false,GBB.FoldAllDungeon, nil, true)
GBB.PopupDynamic:AddItem(GBB.L["BtnUnFoldAll"], false,GBB.UnfoldAllDungeon, nil, true)
GBB.PopupDynamic:AddItem("",true)
end
GBB.PopupDynamic:AddItem(GBB.L["CboxShowTotalTime"],false,GBB.DB,"ShowTotalTime")
Expand All @@ -997,7 +997,7 @@ local function createMenu(DungeonID,req)
GBB.PopupDynamic:AddItem(SETTINGS, false, GBB.OptionsBuilder.OpenCategoryPanel, 1)
-- todo: Open to filter settings to expac related to DungeonID
GBB.PopupDynamic:AddItem(FILTERS, false, GBB.OptionsBuilder.OpenCategoryPanel, 2)
GBB.PopupDynamic:AddItem(GBB.L["BtnCancel"],false)
GBB.PopupDynamic:AddItem(GBB.L["BtnCancel"], false, nil, nil, nil, true)
GBB.PopupDynamic:Show()
end

Expand Down

0 comments on commit 4af53c7

Please sign in to comment.