Application is using IE WebBrwoser Control. However sometimes javascript error dialogs comes up,to solve this put_silent property was used on WebBrowser element,but that disables all the dialogs.So is there a way to disable Javascript error debugging in WebBrowser control?
On your control do right click and click on Inspect Element. If you did not disable the IE menu, it should open the Developer window on right or bottom side. Select there the tab "Debug", click on the hexagon and check "Don't stop on exception" or "Stop on unhandled exceptions". I believe this is a global setting for the browser, so you maybe can do it just from IE.
Update 1
First implement IDocHostUIHandler and wrap the external handler calls. It is declared in Mshtmhst.h so you probably have to include it. Don't forget about IUnknown members, also have to be wrapped. ATL wizards can be used to implement interfaces, but anyway you will have to understand exactly that you do:
class MyDocHostUIHandler: public IDocHostUIHandler
{
public:
IDocHostUIHandler* externalHandler;
HRESULT EnableModeless( BOOL fEnable)
{
return externalHandler->EnableModeless(fEnable);
}
HRESULT FilterDataObject(IDataObject* pDO, IDataObject** ppDORet)
{
return externalHandler->FilterDataObject(pDO, ppDORet)ș
}
.... Wrap all the functions from External Handler like above
};
Create an instance of your class:
MyDocHostUIHandler* myHandler = new MyDocHostUIHandler();
Then in your code call like it is specified in MSDN.
First you get the MSHTML object
CComPtr<IHTMLDocument2> m_spDocument;
hr = m_WebBrowser->get_Document(&m_spDocument);// Get the MSHTML object
Then you get the existing default handler
ComPtr<IOleObject> spOleObject;
hr = m_spDocument.As(&spOleObject);
ComPtr<IOleClientSite> spClientSite;//<--this will be the default handler
hr = spOleObject->GetClientSite(&spClientSite);
Save the existing handler to your class, so you will be able to wrap its functions
//see myHandler is the instance of interface you implemented in first step
myHandler->externalHandler = spClientSite;
Get the custom doc:
ComPtr<ICustomDoc> spCustomDoc;
hr = m_spDocument.As(&spCustomDoc);//m_spDocument it is the pointer to your MSHTML
Now replace the handler from HSMTML:
//myHandler is the instance of class you implemented above
spCustomDoc->SetUIHandler(myHandler);
After this step, the MSHTML should not notice anything, but you will be able to add breakpoints in your MyDocHostUIHandler class and see which function is called by your MSHTML and when.
Related
I have a single page, single class Vaadin project. In this project, I might change the layout to make it seem as if a new page has been entered. But since no new link/page is approached, I cannot figure out how to allow users to "return" to the previous element.
For a bit, I thought I could change the url, and then catch whether people used the forward or backward buttons. I have found hashChange events, but I cannot make sense of it or make it work. Vaadin has a HistoryChangeEventHandler, but it does not listen for hashChanges, and as such my solution would not work.
I am coming up short with regards to solving this. I found this thread from 2019, which seems to be a solution to my issue, but I cannot make the code work. I suppose I am trying to pass a wrong element to the javaScript execution.
So, I have a simple mainview that extends a div. In this div, I have the following code:
History history = UI.getCurrent().getPage().getHistory();
TextField textField = new TextField();
add(textField);
Button button = new Button();
button.setText("New link");
button.addClickListener(e-> history.pushState(null, "#"+textField.getValue()));
add(button);
Button button1 = new Button();
button1.setText("Go back");
button1.addClickListener(e-> history.back());
add(button1);
getElement().executeJs(
"const serverCallback = element.$server.onHashChange; this.window.addEventListener('hashchange', function () {serverCallback(location.hash);}, false);"
);
#ClientCallable
public void onHashChange(String hash) {
System.out.print("test "+hash);
}
I expected that the onHashChange method would be called. But I am getting a javascript error saying: (ReferenceError) : element is not defined.
Working solution:
getElement().executeJs("const serverCallback = $0.$server.onHashChange;" +
"window.addEventListener('hashchange', function () {serverCallback(location.hash);}, false);", getElement());
My getElement() is the extended div, I guess. And here is my onHashChange method.
#ClientCallable
public void onHashChange(String location) {
System.out.println("Current location is: " + location);
}
Also, if you ever wanted to just call a java method from vaadin. You can do the following:
getElement().executeJs("$0.$server.onHashChange(\"It works\");", getElement());
Good luck! And thank you Erik for taking the time to help out.
Looking at the JavaDoc for executeJs, it says:
* Asynchronously runs the given JavaScript expression in the browser in the
* context of this element.
* ...
* This element will be available to the expression as <code>this</code>.
So try replacing element.$server with this.$server.
I have created the following class (condensed version), heres a reference to the full file
https://github.com/cotyembry/CastRemoteNative/blob/7e74dbc56f037cc61241f6ece24a94d8c52abb32/root/ios/CastRemoteNative/NativeMethods.swift
#objc(NativeMethods)
class NativeMethods: RCTEventEmitter {
#objc(sendEventToJSFromJS)
func sendEventToJSFromJS {
self.emitEvent(eventName: "test", body: "bodyTestString")
}
func emitEvent(eventName: String: body: Any) {
self.sendEvent(withName: eventName, body: body)
}
}
This works perfectly and fires my callback listener that is in my javascript code when I call the emitEvent method like the following, its an altered snippet from
https://github.com/cotyembry/CastRemoteNative/blob/7e74dbc56f037cc61241f6ece24a94d8c52abb32/root/js/Components/ChromecastDevicesModal.js
From the javascript side
import {
NativeModules,
NativeEventEmitter
} from 'react-native'
//here I bring in the swift class to use inside javascript
var NativeMethods = NativeModules.NativeMethods;
//create an event emitter to use to listen for the native events when they occur
this.eventEmitter = new NativeEventEmitter(NativeMethods);
//listen for the event once it sends
this.subscription = this.eventEmitter.addListener('test', (body) => { console.log('in test event listener callback', body)});
NativeMethods.sendEventToJSFromJS() //call the native method written in swift
I simply have the sendEventToJSFromJS method invoked on a button press in javascript
Again, this works and the console.log('in test event listener callback', body) code works and runs on the javascript side
My Issue where this does NOT work:
If I was to do the following inside the swift file after defining the class, this would not work:
var nativeMethodsInstance = nativeMethods()
nativeMethodsInstance.sendEventToJSFromSwift()
Why? Because the following error is thrown:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'bridge is not set. This is probably because you've explicitly synthesized the bridge in NativeMethods, even though it's inherited from RCTEventEmitter.'
So, when creating an instance of NativeMethods, versus not... what is the difference?
For additional information:
Objective-C gets the same bridge not set issue when I write these same snippets of code in .h and .m files instead of in .swift files
I found where the error message is getting printed in the native code, but it just has the variable
_bridge
and is checking to see if it is nil
The files are this error comes from is:
RCTEventEmitter.h
RCTEventEmitter.c
here is the full snippet of RCTEventEmitter.c
- (void)sendEventWithName:(NSString *)eventName body:(id)body
{
RCTAssert(_bridge != nil, #"bridge is not set. This is probably because you've "
"explicitly synthesized the bridge in %#, even though it's inherited "
"from RCTEventEmitter.", [self class]);
if (RCT_DEBUG && ![[self supportedEvents] containsObject:eventName]) {
RCTLogError(#"`%#` is not a supported event type for %#. Supported events are: `%#`",
eventName, [self class], [[self supportedEvents] componentsJoinedByString:#"`, `"]);
}
if (_listenerCount > 0) {
[_bridge enqueueJSCall:#"RCTDeviceEventEmitter"
method:#"emit"
args:body ? #[eventName, body] : #[eventName]
completion:NULL];
} else {
RCTLogWarn(#"Sending `%#` with no listeners registered.", eventName);
}
}
Where does this _bridge value get set and how does it get set so I can know, in the cases where it is failing how to set it
I found the following also in RCTEventEmitter.h
#property (nonatomic, weak) RCTBridge *bridge;
In the error that is given it mentions the bridge is inherited in the RCTEventEmitter, so is this maybe an issue with the weak part to the bridge property?
Or do I need to change my strategy in how I'm doing this all together?
I know it probably has to be something to do with me not fully understanding the
#synthesize bridge = _bridge;
part of the code and all the languages being mixed in doesnt help much lol...
This is really hard, so any help would be much appreciated!
Thanks so much for your time
here is a link to the full project when the project history code represented the code from my question above (since I have since made changes to the project):
https://github.com/cotyembry/CastRemoteNative/tree/7e74dbc56f037cc61241f6ece24a94d8c52abb32
I figured it out
Warning: this solution uses a deprecated method react native method - I could not figure out how to "properly" inherit from the RCTEventEmitter and send an event... every time I tried to the _bridge would end up being nil
Make sure Swift is bridged to Objective C (if you're using swift to send the event to javascript)
Do Not create instances of the exported Native modules (whether they be written in Swift or Objective C)
Let React Native's underlying implementation do this and for each and every class that needs to send an event, export that particular Native Class Objective C Implementation code or Swift code (the Native Module) to React-Native. This allows the javascript to be able to listen to the event
var publicBridgeHelperInstance = PublicBridgeHelper() //instantiate the the objective c class from inside the .swift file to use later when needing to get a reference to the bridge to send an event to javascript written in react native
#objc(DeviceManager) //export swift module to objective c
class DeviceManager: NSObject {
#objc(deviceDidComeOnline:) //expose the function to objective c
public func deviceDidComeOnline(_ device: GCKDevice) {
//imagine this deviceDidComeOnline function gets called from something from the Native code (totally independent of javascript) - honestly this could be called from a native button click as well just to test it works...
//emit an event to a javascript function that is a in react native Component listening for the event like so:
//1. get a reference to the bridge to send an event through from Native to Javascript in React Native (here is where my custom code comes in to get this to actually work)
let rnBridge = publicBridgeHelperInstance.getBridge() //this gets the bridge that is stored in the AppDelegate.m file that was set from the `rootView.bridge` variable (more on this later)
//(if you want to print the bridge here to make sure it is not `nil` go ahead:
print("rnBridge = \(rnBridge)")
//2. actually send the event through the eventDispatcher
rnBridge?.eventDispatcher().sendAppEvent(withName: "test", body: "testBody data!!!")
}
}
in AppDelegate.h put (additionally to the code that was already in the file)
#import "YourProjectsBridgingHeaderToMakeThisCodeAvailableInSwift.h" //replace this with your actual header you created when creating a swift file (google it if you dont know how to bridge swift to objective c)
#interface PublicBridgeHelper: NSObject
-(RCTBridge*)getBridge;
#end
in AppDelegate.m put (in addition to the code that was already in the file)
#import <React/RCTRootView.h>
RCTBridge *rnBridgeFromRootView;
#implementation PublicBridgeHelper //this is created to SIMPLY return rnBridgeFromRootView defined above over to my Swift class when actually sending the event to javascript that defines a react native Component
-(RCTBridge*)getBridge {
NSLog(#"rnBridgeFromRootView = #%#", rnBridgeFromRootView);
return rnBridgeFromRootView;
}
important - also make sure to add the following line of code to the Objective C .h's bridging header to make this PublicBridgeHelper definition available to be used in the .swift code
#import "AppDelegate.h"
finally,
now to show you how to set the rnBridgeFromRootView variable used in AppDelegate.m (that gets returned and used in the .swift code right before sending the event to javascript)
open AppDelegate.m and in the method body of
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... }
include the following after the line of code that instantiates the rootView variable
i.e. after the line that probably looks like
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:#"YourProjecNameProbably" initialProperties:nil launchOptions:launchOptions];
add:
rnBridgeFromRootView = rootView.bridge //set the bridge to be exposed and returned later and used by the swift class
Now to explain the publicBridgeHelperInstance.getBridge() part that is in the .swift file
publicBridgeHelper is an instance of an objective c class which allows the swift class ability to get a reference to the react native bridge
If you are still having problems understanding my answer after reading this I made a video over it and you can watch it here:
https://www.youtube.com/watch?v=GZj-Vm9cQIg&t=9s
I have a window which contains a ChromiumWebBrowser.Now I want to get a message from that Browser by a JavaScriptObject and when I get it, I want to look inside my list, what image, video or hyperlink I shall show next and then do exactly that.There is only the problem of the static and non-static methods. the complete class and all methods inside where the browser is made filled are non-static. The JavascriptObject is static.There are many examples out there that show how to create this object but not how to use it.Now my question: "Is there any way of calling the Javascript inside the HTML to access non-static objects?"
edit:
Ok, I try to be more precise and detailed.
I have an app, which creates numerous windows, which are inheriting a ChromiumWebBrowser-object, an Image-object and a MediaElement-object.
When the window is created (and every few minutes afterwards) I read a XML-file (which can be edited by another user) that tells me what I shall show in my window. Some things are easy to do, like at the end of a video I look for the next item to show. But on websites its a little bit harder. So I searched and came up with the JavaScriptObject of the ChromiumWebBrowser. I implement it in the window right after the InitializeComponent() by this code:
FirstBrowser.RegisterJsObject("callbackObject", new JavaScriptCommunicationObject());
SecondBrowser.RegisterJsObject("callbackObject", new JavaScriptCommunicationObject());
Now I can use the follwing JavaScript to communicate with my app:
<script type="text/javascript">
callbackObject.callNextMedia('NextMedia');
</script >
Inside the window I declared a new class and a method. But this method is static, so I cannot call any methods from the mainclass:
public class JavaScriptCommunicationObject
{
public void CallNextMedia(string message)
{
if (message.Equals("NextMedia"))
{
MixedMediaWindow.FileCounter++;
MixedMediaWindow.ShowNextMedia();
}
}
}
I could make the FileCounter static, so I could change it from the second one. But I cannot call the ShowNextMedia(). So I wanted to ask if there is a way to call it by any direct means. I could set a static flag (boolean) and check it every few milliseconds for a change and if it was changed, so I could call the ShowNextMedia()-method from inside the DispatcherTimer. But I wanted to avoid too much Timers, a direct method-call would be much nicer.
I am making a feed reader in Visual Studio using JavaScript, and when I run it, I get an error:
0x800a138f - JavaScript runtime error: Unable to get property 'addEventListener' of undefined or null reference
and the code that is affected is:
articlelistElement.addEventListener("iteminvoked", itemInvoked);
articleListElement refers to a variable that references a WinJS.UI.Listview.
Thanks for your help!
If your document.getElementById("articlelist") is returning null, then you must be attempting to add the listener before the DOM is actually loaded.
If you started with the Blank project template, then in default.js you'll see this code:
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize
// your application here.
} else {
// TODO: This application has been reactivated from suspension.
// Restore application state here.
}
args.setPromise(WinJS.UI.processAll());
}
};
Until this activated event is called, the DOM won't be ready and getElementById can return null. (For a full run-down of the activation sequence, see Chapter 3 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript).
If you're using the Navigation or Grid app templates, then you need to place that initialization code inside the page control's ready method (or the processed method). With the Navigation template, you'll see that code in pages/home/home.js. (The same chapter of my free ebook talks about page controls.)
That's the first step--if you're calling getElementById at the right time, then it should give you back the div where you declare the ListView.
There's more to the story, however, because to attach an event handler properly to a WinJS control, you have to make sure that control has been instantiated. This is the purpose of WinJS.UI.processAll in the Blank template code above (the same call is also made automatically as part of loading a page control).
WinJS.UI.processAll (or WinJS.UI.process, which works in an individual element and its children rather than the whole document), goes through the DOM looking for data-win-control attributes. When it finds one, it parses the data-win-options attribute (if you provide one), and then calls the constructor identified in data-win-control with that parsed options object.
Until that happens, the where you declare the control will just be a div and not contain any other control structure.
With page controls, again, within the processed or ready methods the controls should be instantiated. In the Blank template, you need to attach a completed handler to WinJS.UI.processAll:
args.setPromise(WinJS.UI.processAll().then(function () {
//Controls have been instantiated, do initialization here
//Note that calling .then and not .done is necessary because we need to return a promise
//to args.setPromise.
});
At that point, then, document.getElementById("articlelist") should get you to the ListView's host div.
The last step is that to attach a handler to a WinJS control event, you have to add the listener to the control object not the host div, which you get through the .winControl property of that host element. So your code needs to look like this:
articlelistElement.winControl.addEventListener("iteminvoked", itemInvoked);
Following on from this question here
I have a custom ActiveX control which raises an event "BeforePageValidated" which has a parameter "args" which is an object which contains a boolean. The purpose of this is to allow the client to override the validation and force it fail by setting the boolean value to true.
I can catch the event in IE using the following
<script for="objInterviewNOW" event="BeforePageValidated(args)">
However I can't access the property of the args object.
How should I be doing this?
Worked it out for myself.
The problem was that the event was being passed down from another dll used by the activeX control, and this is where the args class was declared.
Fixed it by creating a copy of the args class in the activeX object and using this when raising the event.
Unfortunately does mean the args class is duplicated, so maybe someone knows a better way?
args also needs to register as com object.
[ComVisible(true)]
[GuidAttribute("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
public class args
{
}
Hope this will help.