View Single Post
10-12-13, 03:44 PM   #1
Vrul
A Scalebane Royal Guard
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 404
Unwrapping a Script for a Specific Header

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.
  Reply With Quote