private void HandleKeyInput() { List <Event> keyEvents = browser.UIHandler.KeyEvents; if (keyEvents.Count == 0) { return; } foreach (Event item in keyEvents) { int windowsKeyCode = KeyMappings.GetWindowsKeyCode(item); if (item.character == '\n') { item.character = '\r'; } if (item.character != 0 && item.type == EventType.KeyDown) { BrowserNative.zfb_characterEvent(browser.browserId, item.character, windowsKeyCode); } else { BrowserNative.zfb_keyEvent(browser.browserId, item.type == EventType.KeyDown, windowsKeyCode); } } }
/** * Returns a list of all cookies in the browser across all domains. * * Note that cookies are shared between browser instances. * * If the browser is not ready yet (browser.IsReady or WhenReady()) this will return an empty list. */ public IPromise <List <Cookie> > GetCookies() { Cookie.Init(); var ret = new List <Cookie>(); if (!browser.IsReady || !browser.enabled) { return(Promise <List <Cookie> > .Resolved(ret)); } var promise = new Promise <List <Cookie> >(); BrowserNative.GetCookieFunc cookieFunc = cookie => { try { if (cookie == null) { browser.RunOnMainThread(() => promise.Resolve(ret)); cookieFuncs.Remove(promise); return; } ret.Add(new Cookie(this, cookie)); } catch (Exception ex) { Debug.LogException(ex); } }; BrowserNative.zfb_getCookies(browser.browserId, cookieFunc); cookieFuncs[promise] = cookieFunc; return(promise); }
private static void CB_ShowContextMenuFunc( int browserId, string json, int x, int y, BrowserNative.ContextMenuOrigin origin ) { var browser = GetBrowser(browserId); if (!browser) { return; } if (json != null && (browser.allowContextMenuOn & origin) == 0) { //ignore this BrowserNative.zfb_sendContextMenuResults(browserId, -1); return; } lock (browser.thingsToDo) browser.thingsToDo.Add(() => { if (json != null) { browser.CreateDialogHandler(); } if (browser.dialogHandler != null) { browser.dialogHandler.HandleContextMenu(json, x, y); } }); }
/** * Returns a user agent that, hopefully, tricks legacy, stupid, non-feature-detection websites * into giving us their actual content. * * If you change this, the Editor will usually need to be restarted for changes to take effect. */ public static string GetUserAgent() { if (agentOverride != null) { return(agentOverride); } var chromeVersion = Marshal.PtrToStringAnsi(BrowserNative.zfb_getVersion()); //(Note: I don't care what version of the OS you have, we're telling the website you have this //version so you get a good site.) string osStr = "Windows NT 6.1"; #if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX osStr = "Macintosh; Intel Mac OS X 10_7_0"; #elif UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX osStr = "X11; Linux x86_64"; #endif var userAgent = "Mozilla/5.0 " + "(" + osStr + "; Unity 3D; ZFBrowser 2.0.0; " + Application.productName + " " + Application.version + ") " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + chromeVersion + " Safari/537.36" ; //Chromium has issues with non-ASCII user agents. userAgent = Regex.Replace(userAgent, @"[^\u0020-\u007E]", "?"); return(userAgent); }
private static void CheckForEditorExit() { //Read off the last bit of log to see if we are going to reload or exit. //NB: Doing Debug.Log in this function before the read will result in a read of that log instead. const string exitString = "Cleanup mono"; const int readBack = 500; var buffer = new byte[readBack]; try { using (var file = File.Open(logLocation, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) { file.Seek(-readBack, SeekOrigin.End); var len = file.Read(buffer, 0, readBack); var readStr = System.Text.Encoding.UTF8.GetString(buffer, 0, len); // Debug.Log("len " + len + " readstr " + readStr); if (readStr.Contains(exitString)) { Debug.Log("Editor shutting down, also stopping ZFBrowser"); BrowserNative.UnloadNative(); } } } catch (Exception ex) { Debug.LogError("Failed to check for shutdown: " + ex); } }
public void HandleInput() { browser.UIHandler.InputUpdate(); if (browser.UIHandler.MouseHasFocus) { HandleMouseInput(); } if (browser.UIHandler.KeyboardHasFocus) { if (!wasFocused) { BrowserNative.zfb_setFocused(browser.browserId, wasFocused = true); } HandleKeyInput(); } else { if (wasFocused) { BrowserNative.zfb_setFocused(browser.browserId, wasFocused = false); } } }
/// <summary> /// Feeds scroll events to the browser. /// In particular, it will clump together scrolling "floods" into fewer larger scrolls /// to prevent the backend from getting choked up and taking forever to execute the requests. /// </summary> /// <param name="mouseScroll"></param> private void FeedScrolling(Vector2 mouseScroll, float scrollSpeed) { accumulatedScroll += mouseScroll * scrollSpeed; if (accumulatedScroll.sqrMagnitude != 0 && Time.realtimeSinceStartup > lastScrollEvent + maxScrollEventRate) { //Debug.Log("Do scroll: " + accumulatedScroll); //The backend seems to have trouble coping with horizontal AND vertical scroll. So only do one at a time. //(And if we do both at once, vertical appears to get priority and horizontal gets ignored.) if (Mathf.Abs(accumulatedScroll.x) > Mathf.Abs(accumulatedScroll.y)) { BrowserNative.zfb_mouseScroll(browser.browserId, (int)accumulatedScroll.x, 0); accumulatedScroll.x = 0; accumulatedScroll.y = Mathf.Round(accumulatedScroll.y * .5f); //reduce the thing we weren't doing so it's less likely to accumulate strange } else { BrowserNative.zfb_mouseScroll(browser.browserId, 0, (int)accumulatedScroll.y); accumulatedScroll.x = Mathf.Round(accumulatedScroll.x * .5f); accumulatedScroll.y = 0; } lastScrollEvent = Time.realtimeSinceStartup; } }
protected void LateUpdate() { //Note: we use LateUpdate here in hopes that commands issued during (anybody's) Update() //will have a better chance of being completed before we push the render if (lastUpdateFrame != Time.frameCount) { UnityEngine.Profiling.Profiler.BeginSample("Browser.NativeTick"); BrowserNative.zfb_tick(); UnityEngine.Profiling.Profiler.EndSample(); lastUpdateFrame = Time.frameCount; } ProcessCallbacks(); if (pageReplacer != null) { pageReplacer(); pageReplacer = null; } if (browserId == 0) { return; //not ready yet or not loaded } if (EnableRendering) { Render(); } }
/// <summary> /// Sends headers, status code, content-type, etc. for a request. /// </summary> /// <param name="id"></param> /// <param name="pre"></param> protected void SendPreamble(int id, ResponsePreamble pre) { var headers = new JSONNode(JSONNode.NodeType.Object); if (pre.headers != null) { foreach (var kvp in pre.headers) { headers[kvp.Key] = kvp.Value; } } if (pre.statusText == null) { if (!statusTexts.TryGetValue(pre.statusCode, out pre.statusText)) { pre.statusText = statusTexts[-1]; } } headers[":status:"] = pre.statusCode.ToString(); headers[":statusText:"] = pre.statusText; headers["Content-Type"] = pre.mimeType; //Debug.Log("response headers " + headers.AsJSON); lock (BrowserNative.symbolsLock) { BrowserNative.zfb_sendRequestHeaders(id, pre.length, headers.AsJSON); } }
/** * Adds the given browser as an overlay of this browser. * * The overlaid browser will appear transparently over the top of us on our texture. * {overlayBrowser} must not have an overlay and must be sized exactly the same as {this}. * Additionally, overlayBrowser.EnableRendering must be false. You still need to * do something to handle getting input to the right places. Overlays take a notable performance * hit on rendering (CPU alpha compositing). * * Overlays are used internally to implement context menus and pop-up dialogs (alert, onbeforeunload). * If the page causes any type of dialog, the overlay will be replaced. * * Overlays will be resized onto our texture when we are resized. The sizes must always match exactly. * * Remove the overlay (SetOverlay(null)) before closing either browser. * * (Note: though you can't set B as an overlay to A when B has an overlay, you can set * an overlay on B /while/ it is the overlay for A. For an example of this, try * right-clicking on the text area inside a prompt() popup. The context menu that * appears is an overlay to the overlay to the actual browser.) */ public void SetOverlay(Browser overlayBrowser) { if (DeferUnready(() => SetOverlay(overlayBrowser))) { return; } if (overlayBrowser && overlayBrowser.DeferUnready(() => SetOverlay(overlayBrowser))) { return; } BrowserNative.zfb_setOverlay(browserId, overlayBrowser ? overlayBrowser.browserId : 0); overlay = overlayBrowser; if (!overlay) { return; } if ( !overlay.Texture || (overlay.Texture.width != Texture.width || overlay.Texture.height != Texture.height) ) { overlay.Resize(Texture); } }
/** * Returns a list of all cookies in the browser across all domains. * * Note that cookies are shared between browser instances. * * If the browser is not ready yet (browser.IsReady or WhenReady()) this will return an empty list. * * This method is not reentrant! You must wait for the returned promise to resolve before calling it again, * even on a differnet object. */ public IPromise <List <Cookie> > GetCookies() { if (currentFetch != null) { //This method Wait for the previous promise to resolve, then make your call. //If this limitation actually affects you, let me know. throw new InvalidOperationException("GetCookies is not reentrant"); } Cookie.Init(); var result = new List <Cookie>(); if (!browser.IsReady || !browser.enabled) { return(Promise <List <Cookie> > .Resolved(result)); } var promise = new Promise <List <Cookie> >(); BrowserNative.GetCookieFunc cookieFunc = CB_GetCookieFunc; BrowserNative.zfb_getCookies(browser.browserId, cookieFunc); currentFetch = new CookieFetch { promise = promise, nativeCB = cookieFunc, manager = this, result = result, }; return(promise); }
protected void OnApplicationQuit() { //According to http://docs.unity3d.com/Manual/ExecutionOrder.html, //OnDisable will be called before this. Experience shows this to be not so much the case. //Therefore, we will forcefully call OnDestroy() OnDestroy(); if (BrowserNative.zfb_numBrowsers() == 0) { //last one out, turn off the lights //beforeunload windows won't fully disappear without ticking the message loop //Ideally, we'd just keep ticking it, but we are stopping. for (int i = 0; i < 10; ++i) { BrowserNative.zfb_tick(); System.Threading.Thread.Sleep(10); } #if UNITY_EDITOR //You can't re-init CEF, so if we are the editor, never shut it down. #else BrowserNative.UnloadNative(); #endif } }
public void ClearAll() { if (!browser.DeferUnready(ClearAll)) { BrowserNative.zfb_clearCookies(browser.browserId); } }
/** * Loads the given HTML string as if it were the given URL. * Use http://-like porotocols or else things may not work right. * * Note that, instead of using this, you can also load "data:" URIs into this.Url. * This allows pretty much any type of content to be loaded as the whole page. */ public void LoadHTML(string html, string url = null) { if (DeferUnready(() => LoadHTML(html, url))) { return; } //Debug.Log("Load HTML " + html); loadPending = true; if (string.IsNullOrEmpty(url)) { url = LocalUrlPrefix + "custom"; } if (string.IsNullOrEmpty(this.Url)) { //Nothing will happen if we don't have an initial page, so cause one. this.Url = "about:blank"; skipNextLoad = true; } BrowserNative.zfb_goToHTML(browserId, html, url); }
public void Delete() { if (original != null) { BrowserNative.zfb_editCookie(cookies.browser.browserId, original, BrowserNative.CookieAction.Delete); original = null; } }
public void OnDestroy() { if (!BrowserNative.SymbolsLoaded) { return; } BrowserNative.zfb_windowClose(windowId); }
public void Update() { if (!BrowserNative.SymbolsLoaded) { return; } BrowserNative.zfb_windowRender(windowId, browserId); }
private void HandleMouseInput() { var handler = browser.UIHandler; var mousePos = handler.MousePosition; // ReSharper disable CompareOfFloatsByEqualityOperator var currentButtons = handler.MouseButtons; var mouseScroll = handler.MouseScroll; if (mousePos != prevPos) { BrowserNative.zfb_mouseMove(browser.browserId, mousePos.x, 1 - mousePos.y); } if (mouseScroll.sqrMagnitude != 0) { BrowserNative.zfb_mouseScroll( browser.browserId, (int)mouseScroll.x * handler.InputSettings.scrollSpeed, (int)mouseScroll.y * handler.InputSettings.scrollSpeed ); } // ReSharper restore CompareOfFloatsByEqualityOperator var leftChange = (prevButtons & MouseButton.Left) != (currentButtons & MouseButton.Left); var leftDown = (currentButtons & MouseButton.Left) == MouseButton.Left; var middleChange = (prevButtons & MouseButton.Middle) != (currentButtons & MouseButton.Middle); var middleDown = (currentButtons & MouseButton.Middle) == MouseButton.Middle; var rightChange = (prevButtons & MouseButton.Right) != (currentButtons & MouseButton.Right); var rightDown = (currentButtons & MouseButton.Right) == MouseButton.Right; if (leftChange) { if (leftDown) { leftClickHistory.ButtonPress(mousePos, handler, browser.Size); } BrowserNative.zfb_mouseButton( browser.browserId, BrowserNative.MouseButton.MBT_LEFT, leftDown, leftDown ? leftClickHistory.repeatCount : 0 ); } if (middleChange) { //no double-clicks, to be consistent with other browsers BrowserNative.zfb_mouseButton( browser.browserId, BrowserNative.MouseButton.MBT_MIDDLE, middleDown, 1 ); } if (rightChange) { //no double-clicks, to be consistent with other browsers BrowserNative.zfb_mouseButton( browser.browserId, BrowserNative.MouseButton.MBT_RIGHT, rightDown, 1 ); } prevPos = mousePos; prevButtons = currentButtons; }
/** * Show the development tools for the current page. * * If {show} is false the dev tools will be hidden, if possible. */ public void ShowDevTools(bool show = true) { if (DeferUnready(() => ShowDevTools(show))) { return; } BrowserNative.zfb_showDevTools(browserId, show); }
public void Stop() { if (!IsReady) { return; } CheckSanity(); BrowserNative.zfb_changeLoading(browserId, BrowserNative.LoadChange.LC_STOP); }
public void GoBack() { if (!IsReady) { return; } CheckSanity(); BrowserNative.zfb_doNav(browserId, -1); }
public void GoForward() { if (!IsReady) { return; } CheckSanity(); BrowserNative.zfb_doNav(browserId, 1); }
/** * Sends a command such as "select all", "undo", or "copy" * to the currently focused frame in th browser. */ public void SendFrameCommand(BrowserNative.FrameCommand command) { if (DeferUnready(() => SendFrameCommand(command))) { return; } BrowserNative.zfb_sendCommandToFocusedFrame(browserId, command); }
public void Update() { if (original != null) { Delete(); } original = new BrowserNative.NativeCookie(); Copy(this, original); BrowserNative.zfb_editCookie(cookies.browser.browserId, original, BrowserNative.CookieAction.Create); }
protected void Render() { CheckSanity(); BrowserNative.RenderData renderData; UnityEngine.Profiling.Profiler.BeginSample("Browser.UpdateTexture.zfb_getImage", this); { renderData = BrowserNative.zfb_getImage(browserId, forceNextRender); forceNextRender = false; if (renderData.pixels == IntPtr.Zero) { return; //no changes } if (renderData.w != texture.width || renderData.h != texture.height) { //Mismatch, can happen, for example, when we resize and ask for a new image before the IPC layer gets back to us. return; } if (pixelData == null || pixelData.Length != renderData.w * renderData.h) { pixelData = new Color32[renderData.w * renderData.h]; } } UnityEngine.Profiling.Profiler.EndSample(); /* * Getting the frame data from CEF to the GPU is the biggest framerate bottleneck. * * This memcpy is unfortunate, and with more work we could just upload directly to the GPU texture from C++. * That said, the profiler tells us: * - With mipmaps on, GPUUpload takes an order of magnitude more time than DataCopy * - With mipmaps off, GPUUpload takes 1-2x the time DataCopy does. * - On my machine with a quite large 2048x2048 texture constantly updating, both weigh in at about ~10ms/frame * without mipmap generation. */ UnityEngine.Profiling.Profiler.BeginSample("Browser.UpdateTexture.DataCopy", this); { GCHandle handle = GCHandle.Alloc(pixelData, GCHandleType.Pinned); BrowserNative.zfb_memcpy(handle.AddrOfPinnedObject(), renderData.pixels, pixelData.Length * 4); handle.Free(); } UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Browser.UpdateTexture.GPUUpload", this); { texture.SetPixels32(pixelData); texture.Apply(true); } UnityEngine.Profiling.Profiler.EndSample(); }
public static string GetUserAgent() { if (agentOverride != null) { return(agentOverride); } string text = Marshal.PtrToStringAnsi(BrowserNative.zfb_getVersion()); string text2 = "Windows NT 6.1"; string input = "Mozilla/5.0 (" + text2 + "; Unity 3D; ZFBrowser 1.1.0; " + Application.productName + " " + Application.version + ") AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + text + " Safari/537.36"; return(Regex.Replace(input, "[^\\u0020-\\u007E]", "?")); }
private void HandleMouseInput() { var handler = browser.UIHandler; var mousePos = handler.MousePosition; var currentButtons = handler.MouseButtons; var mouseScroll = handler.MouseScroll; if (mousePos != prevPos) { BrowserNative.zfb_mouseMove(browser.browserId, mousePos.x, 1 - mousePos.y); } FeedScrolling(mouseScroll, handler.InputSettings.scrollSpeed); var leftChange = (prevButtons & MouseButton.Left) != (currentButtons & MouseButton.Left); var leftDown = (currentButtons & MouseButton.Left) == MouseButton.Left; var middleChange = (prevButtons & MouseButton.Middle) != (currentButtons & MouseButton.Middle); var middleDown = (currentButtons & MouseButton.Middle) == MouseButton.Middle; var rightChange = (prevButtons & MouseButton.Right) != (currentButtons & MouseButton.Right); var rightDown = (currentButtons & MouseButton.Right) == MouseButton.Right; if (leftChange) { if (leftDown) { leftClickHistory.ButtonPress(mousePos, handler, browser.Size); } BrowserNative.zfb_mouseButton( browser.browserId, BrowserNative.MouseButton.MBT_LEFT, leftDown, leftDown ? leftClickHistory.repeatCount : 0 ); } if (middleChange) { //no double-clicks, to be consistent with other browsers BrowserNative.zfb_mouseButton( browser.browserId, BrowserNative.MouseButton.MBT_MIDDLE, middleDown, 1 ); } if (rightChange) { //no double-clicks, to be consistent with other browsers BrowserNative.zfb_mouseButton( browser.browserId, BrowserNative.MouseButton.MBT_RIGHT, rightDown, 1 ); } prevPos = mousePos; prevButtons = currentButtons; }
private IEnumerator FixFocus() { //OS X: Magically, new browser windows that are focused don't think they are focused, even though we told them so. //Hack workaround. yield return(null); BrowserNative.zfb_setFocused(browserId, false); yield return(null); BrowserNative.zfb_setFocused(browserId, true); yield return(null); BrowserNative.zfb_setFocused(browserId, KeyboardHasFocus); }
private void HandleKeyInput(List <Event> keyEvents) { #if ZF_OSX ReconstructInputs(keyEvents); #endif foreach (var ev in keyEvents) { var keyCode = KeyMappings.GetWindowsKeyCode(ev); if (ev.character == '\n') { ev.character = '\r'; //'cuz that's what Chromium expects } if (ev.character == 0) { if (ev.type == EventType.KeyDown) { keysToReleaseOnFocusLoss.Add(ev.keyCode); } else { keysToReleaseOnFocusLoss.Remove(ev.keyCode); } } // if (false) { // if (ev.character != 0) Debug.Log("k >>> " + ev.character); // else if (ev.type == EventType.KeyUp) Debug.Log("k ^^^ " + ev.keyCode); // else if (ev.type == EventType.KeyDown) Debug.Log("k vvv " + ev.keyCode); // } FireCommands(ev); if (ev.character != 0 && ev.type == EventType.KeyDown) { #if ZF_LINUX //It seems, on Linux, we don't get keydown, keypress, keyup, we just get a keypress, keyup. //So, fire the keydown just before the keypress. BrowserNative.zfb_keyEvent(browser.browserId, true, keyCode); //Thanks for being consistent, Unity. #endif BrowserNative.zfb_characterEvent(browser.browserId, ev.character, keyCode); } else { BrowserNative.zfb_keyEvent(browser.browserId, ev.type == EventType.KeyDown, keyCode); } } }
public void HandleFocusLoss() { foreach (var keyCode in keysToReleaseOnFocusLoss) { //Debug.Log("Key " + keyCode + " is held, release"); var wCode = KeyMappings.GetWindowsKeyCode(new Event() { keyCode = keyCode }); BrowserNative.zfb_keyEvent(browser.browserId, false, wCode); } keysToReleaseOnFocusLoss.Clear(); }