It seems like all the code for wrapping/unwrapping script handlers was set up to allow unwrapping a script handler for a specific header that had already been wrapped again. But it doesn't quite work that way, it only removes the last wrap no matter the source. I would like to see some minor changes to some functions in SecureHandlers.lua, that maintain backwards compatability, but also allow for this behavior. The following functions would need modified as shown:
Code:
local function RemoveWrapper(frame, script, header)
local wrap = frame:GetScript(script);
if ((not issecure()) or (LOCAL_Wrapped_Handlers[wrap] == nil)) then
-- not valid
return;
end
if ((not header) or (wrap(MAGIC_UNWRAP) == header)) then
if (not securecall(RestoreWrapHandler, frame, script, wrap)) then
-- not valid
return;
end
else
local prevWrap;
repeat
prevWrap, wrap = wrap, LOCAL_Wrapped_Handlers[wrap];
if (not wrap) then return; end
until (wrap(MAGIC_UNWRAP) == header)
LOCAL_Wrapped_Handlers[prevWrap] = LOCAL_Wrapped_Handlers[wrap];
end
-- Extract header, preBody, postBody
return wrap(MAGIC_UNWRAP);
end
function SecureHandlerUnwrapScript(frame, script, header)
if (not IsValidFrame(frame)) then
error("Invalid frame");
end
if (type(script) ~= "string") then
error("Invalid script id");
end
if (header) then
if (not IsValidFrame(header)) then
error("Invalid header frame");
return;
end
if (not select(2, header:IsProtected())) then
error("Header frame must be explicitly protected");
return;
end
LOCAL_API_Frame:SetAttribute("_apiheader", header);
else
LOCAL_API_Frame:SetAttribute("_apiheader", nil);
end
wipe(UNWRAP_TEMP_TABLE);
UNWRAP_TEMP_TABLE[1] = false;
LOCAL_API_Frame:SetAttribute("_apiframe", frame);
LOCAL_API_Frame:SetAttribute("_apidata", UNWRAP_TEMP_TABLE);
LOCAL_API_Frame:SetAttribute("_unwrap", script);
local chkFrame = UNWRAP_TEMP_TABLE[1];
local chkScript = UNWRAP_TEMP_TABLE[2];
local header = UNWRAP_TEMP_TABLE[3];
local preBody = UNWRAP_TEMP_TABLE[4];
local postBody = UNWRAP_TEMP_TABLE[5];
if ((chkFrame ~= frame) or (chkScript ~= script)) then
error("Unable to retrieve unwrap results");
return;
end
wipe(UNWRAP_TEMP_TABLE);
return header, preBody, postBody;
end
local function SecureHandlerMethod_UnwrapScript(self, frame, script, restrict)
-- Wrapped since args are in different order
return SecureHandlerUnwrapScript(frame, script, restrict and self or nil);
end
The following portion of API_OnAttributeChanged would also need changed:
Code:
-- _unwrap restores a previously wrapped handler
if (name == "_unwrap") then
local frame = self:GetAttribute("_apiframe");
local header = self:GetAttribute("_apiheader");
local data = self:GetAttribute("_apidata");
if (data) then
self:SetAttribute("_apidata", nil);
end
self:SetAttribute("_unwrap", nil);
if (type(value) ~= "string") then
error("Invalid unwrap script id");
end
if (not IsValidFrame(frame)) then
error("Invalid unwrap frame");
end
if (header and (not IsValidFrame(header))) then
error("Invalid header frame");
end
local script = value;
if (not frame:HasScript(script)) then
error("Frame does not support script '" .. script .. "'");
end
local header, preBody, postBody = RemoveWrapper(frame, script, header);
if (type(data) == "table") then
forceinsecure();
wipe(data);
data[1] = frame;
data[2] = script;
data[3] = header;
data[4] = preBody;
data[5] = postBody;
end
return;
end
Those changes would allow you to undo a header's last wrap for a script with:
Code:
header:UnwrapScript(frame, script, true)
without the garbage churn from doing:
Code:
local function UnwrapScript(header, frame, script)
local _header, _preScript, _postScript = header:UnwrapScript(frame, script)
if _header and _header ~= header then
UnwrapScript(header, frame, script)
_header:WrapScript(frame, script, _preScript, _postScript)
end
end
UnwrapScript(header, frame, script)
I would go so far as to suggest that this behavior should be the default as it seems weird to allow any header to unwrap another header's code.