Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manually/Artificially throwing a DOMException with JavaScript

Tags:

javascript

dom

Is it possible to manually throw a DOMException error in pure JavaScript? Documentation I've read suggests it should be relatively easy to construct (at least in Java.)

However, in Chrome, following code returns TypeError: Illegal constructor:

// DOM SYNTAX_ERR (12)
var myDOMException = new DOMException(12,"I'm sorry Dave, I'm afraid I can't do that.");

Regrettably, this is what I expected after reading the W3 docs, which don't appear to specify a constructor at all. (As an aside, while I'm not particularly 'au fait' with IDL, I would have assumed their variant would support specification of constructors.)

Frustratingly, the DOMException class lurks tantalisingly in the global scope. How can I use it? Can I use it?

Update

Since I wrote this, I've made a couple of discoveries - namely:

var myDOMException = DOMException.constructor(12,"Error Message");
var myDOMException2 = DOMException.constructor.call(DOMException,DOMException.SYNTAX_ERR,"Error Message");

Looks like it worked!

...not so fast.

$> myDOMException instanceof DOMException
false
$> myDOMException2 instanceof DOMException
false

And possibly even more offputting:

$> myDOMException.constructor
function Number() {
    [native code]
}

As always, any assistance would be greatly appreciated.

Update #2

Just to clarify my reasons for returning a DOMException object as opposed to a more generic Error - I'm trying to implement the WHATWG's Timed Text Track spec in pure JavaScript. There are a number of instances where a proper solution would be required to return a DOMException object, specifically one with a code of 12 (SYNTAX_ERR.)

like image 990
Christopher Avatar asked Feb 27 '11 23:02

Christopher


2 Answers

In Firefox, at least, DOMException is not a function. It is an object that defines several constants.

 typeof DOMException === 'object' // true (not 'function')

It could be used like this:

try {
    throw DOMException;
} catch(e) {
    if (e === DOMException)
        console.log("caught DOMException")
}

This works if you're trying to signal a DOMException but don't need an actual instance of DOMException.

Ugly, ugly hack (that basically works)

If you absolutely need an instance of DOMException that has the SYNTAX_ERR code, you could perform an action that causes one to be created and throw that:

function createSyntaxException() {
    try {
        // will cause a DOMException
        document.querySelectorAll("div:foo");
    } catch(e) {
        return e;
    }
}

throw createSyntaxException();

The details of the thrown exception won't match your specific situation, of course, but the resulting object will have the correct code and pass instanceof checks.

var e = createSyntaxException();
console.log(e instanceof DOMException); // true
console.log(e.code === e.SYNTAX_ERR); // true

You could mitigate the details issue by subclassing DOMException and adding getters/setters for each of its (read-only) properties.

function DOMExceptionCustom() {
    var message;
    this.__defineGetter__("message", function(){
        return message;
    });
    this.__defineSetter__("message", function(val){
        message = val;
    });
}

// subclass DOMException
DOMExceptionCustom.prototype = createSyntaxException();

var err = new DOMExceptionCustom();
err.message = "my custom message";

The resulting object has the desired properties:

console.log(err.code === err.SYNTAX_ERR); // true
console.log(err.message); // "my custom message"
console.log(err instanceof DOMExceptionCustom); // true
console.log(err instanceof DOMException); // true
like image 123
Wayne Avatar answered Oct 16 '22 06:10

Wayne


Here is my crack at it. Solution based on ECMAScript 5 and WebIDL. I've discussed this with the W3C/ECMAScript join task force that are working on WebIDL. They said it's basically impossible to do because it relies on internal platform behaviour... but here is something that may be close enough.

function CustomDOMException(code, message) {
    //throw on missing code
    if (typeof code !== "number") {
        throw TypeError("Wrong argument");
    }

    //we need the codes, to get the "name" property.  
    var consts = {
        1: "INDEX_SIZE_ERR",
        3: "HIERARCHY_REQUEST_ERR",
        4: "WRONG_DOCUMENT_ERR",
        5: "INVALID_CHARACTER_ERR",
        7: "NO_MODIFICATION_ALLOWED_ERR",
        8: "NOT_FOUND_ERR",
        9: "NOT_SUPPORTED_ERR",
        11: "INVALID_STATE_ERR",
        12: "SYNTAX_ERR",
        13: "INVALID_MODIFICATION_ERR",
        14: "NAMESPACE_ERR",
        15: "INVALID_ACCESS_ERR",
        17: "TYPE_MISMATCH_ERR",
        18: "SECURITY_ERR",
        19: "NETWORK_ERR",
        20: "ABORT_ERR",
        21: "URL_MISMATCH_ERR",
        22: "QUOTA_EXCEEDED_ERR",
        23: "TIMEOUT_ERR",
        24: "INVALID_NODE_TYPE_ERR",
        25: "DATA_CLONE_ERR"
    }
    if ((code in consts) === false) {
        throw TypeError("Unknown exception code: " + code);
    }

    //props for adding properties
    var props = {};
    //generate an exception object 
    var newException;
    try {
        //force an exception to be generated; 
        document.removeChild({})
    } catch (e) {
        //use it as the prototype  
        newException = Object.create(Object.getPrototypeOf(e));
    }
    //get the name of the exception type        
    var name = consts[code];

    //add the properties
    var props = {value: null, writable: true, enumerable: false, Configurable: true};
    //name
    props.value = name; 
    Object.defineProperty(newException, "name",    props);
    props.value = code; 
    Object.defineProperty(newException, "code",    props);
    props.value = message; 
    Object.defineProperty(newException, "message", props);

    //Make sure it "stringifies" properly 
    var finalMessage;
    var obj = this;
    if (typeof message === "function") {
        finalMessage = function() {
            return message.call(newException)
        }
    } else {
        finalMessage = function() {
            return name + ": DOM Exception " + code;
        }
    }
    props.value = function() {
        return finalMessage.call(newException)
    }

    Object.defineProperty(newException, "toString", props);
    return newException;

}

And some tests:

// Throws SYNTAX_ERR    
console.log(new CustomDOMException(12)); 

// Custom message 
console.log(new CustomDOMException(1, "ERROR!")); 

// Custom message 
console.log(new CustomDOMException(1, function() {
    return "Custom Err:" + this.name + " : " + Date.now()
}));

// Throws TypeError     
try {
    new CustomDOMException(2)
} catch (e) {
    console.log(e);    
}

// Throws TypeError
try {
    new CustomDOMException()
} catch (e) {
    console.log(e);    
}    

// Throws TypeError    
try {
    new CustomDOMException("Wee!")
} catch (e) {
    console.log(e);    
}

//Check the inheritance chain     
var ext = new CustomDOMException(17);
var isInstance = ext instanceof DOMException;
console.log("instanceof DOMException: " + isInstance)​
like image 29
Marcosc Avatar answered Oct 16 '22 07:10

Marcosc